数据驾驭术:探索存储之道,释放数据的魔力

第五章 数据存储

在这里插入图片描述

  1. 简述Android数据存储的方式
    Android平台提供的五种数据存储方式,分别为文件存储、SharedPreferences、SQLite数据库、ContentProvider和网络存储,这些存储方式的特点如下。
  • 文件存储:Android提供了openFileInput()和openFileOutput()方法来读取设备上的文件,其读取方式与Java中I/O程序是完全一样的。
  • SharedPreferences:这是Android提供的用来存储一些简单的配置信息的一种机制,他采用了XML格式将数据存储到设备中。通常情况下,我们使用SharedPreferences存储一些应用程序的各种配置信息,如用户名、密码等。
  • SQLite数据库:SQLite是Android自带的一个轻量级的数据库,他运算速度快,占用资源少,还支持基本SQL语法,一般使用他作为复杂数据的存储引擎,可以存储用户信息等。
  • ContentProvider:Android四大组件之一,主要用于应用程序之间的数据交换,他可以将自己的数据共享给其他应用程序使用。
  • 网络存储:需要与Android网络数据包打交道,将数据存储到服务器上,通过网络提供的存储空间来存储/获取数据信息。

文件存储(非重点)

Android设备的两个文件存储区域

  • 内部存储区
  • 外部存储区

内部存储

  • app的内部存储区的目录在Android文件系统的指定目录下,由你的app的包名命名,通常为data/data/package_name/。这些目录既包括用于存储持久性文件的专属位置,也包括用于存储缓存数据的其他位置。系统会阻止其他应用访问这些位置,并且在 Android 10(API 级别 29)及更高版本中,系统会对这些位置进行加密。这些特征使得这些位置非常适合存储只有应用本身才能访问的敏感数据。
  • 对于每个应用,系统都会在内部存储空间中提供目录,一个目录专为应用的持久性文件而设计,而另一个目录包含应用的缓存文件。

获取或者打开目录

  • getFilesDir() 返回一个代表internal目录的File对象
  • getCacheDir() 返回一个用于存放临时缓存文件的internal目录。确保这些文件一旦不再需要就马上删除掉,并给出一个合理大小的使用限制,比如1MB。如果系统的运行存储低了,cache文件会被自行删除。
  • fileList() 获取私有目录中所有文件或文件夹列表,返回一个字符串数组。

操作文件

  • 获取文件输出流:调用openFileOutput()方法来获取FileOutputStream,使用FileOutputStream对象的write()方法写入文件,使用close()方法关闭流。

  • 获取文件输入流:调用openFileInput()方法来获取FileInputStream

  • 总结,从内部存储中读取文件:

    1. 通过调用openFileInput()方法,并传递需读取文件的文件名,该方法会返回一个FileInputStream对象;
    2. 再调用该FileInputStream 对象的的read()方法,读取数据;
    3. 最后通过close()方法关闭流。

外部存储区

  • 系统会在外部存储空间中提供目录,应用可以在该存储空间中整理仅在应用内对用户有价值的文件。该外部专有目录中,一个子目录为应用的持久性文件而设计,而另一个子目录包含应用的缓存文件。
  • 在使用之前一定要检查是否可用。通过调用 Environment.getExternalStorageState() 查询该卷的状态。
    • 如果返回的状态为 MEDIA_MOUNTED,那么可以在外部存储空间中读取和写入应用专属文件。
    • 如果返回的状态为MEDIA_MOUNTED_READ_ONLY,只能读取这些文件。

SharedPreferences存储

SharedPreferences 存储:它是An droid 提供的用来存储一些简单配置信 息的一种机制,采用了 XML格式将数据存储到设备中。只能在同一个包内使用,不能在不同的包之间使用。


  • 创建或访问一个shared preference:
    • getSharedPreferences() 如果需要通过名字区分多个shared preference文件,该方法的第一个参数就是该文件名字。
    getString(R.string.preference_file_key), Context.MODE_PRIVATE);
    
    • getPreferences() 当你的activity仅仅需要一个shared preference文件的时候。因为这个方法会检索activity的默认shared preference文件,所以你不需要提供名字。

写入Shared Preferences

  • 调用 edit() 以获取 SharedPreferences.Editor。然后使用Editor的 putBoolean() 和 putString() 等方法添加值。最后,使用Editor的 commit() 提交新值
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putInt(getString(R.string.saved_high_score_key), newHighScore);
    editor.apply();
    
    • apply() 会立即更改内存中的 SharedPreferences 对象,但会将更新异步写入磁盘。也可以使用 commit() 将数据同步写入磁盘。但是,由于 commit() 是同步的,应避免从主线程调用它,因为它可能会暂停界面的呈现。

读取数据

  • 调用 SharedPreferences 的 getInt()和getString()方法,给这些方法传递你需要获取值的key,如果没有对应key值,会返回一个默认值。
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    int defaultValue = getResources().getInteger(R.string.saved_high_score_default);
    int highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);
    

总结

  • 使用两种方法来获取SharedPreferences对象:
    • getSharedPreferences() – 该方法的第一个参数为preferences文件名,该方法用于区分不同的若干preferences文件;
    • getPreferences() – 如果你的activity只使用一个preferences文件,该方法不需要文件名。
  • 写入值时,是通过调用edit()来获取一个SharedPreferences.Editor对象,使用该editor对象的putXXX()方法来写入值,最后通过commit()方法,整体提交数据的修改。而读取值时,则直接使用SharedPreferences对象的getXXX()方法来获取。

SQLite数据库

  • SQLite数据库:SQLite是Android自带的一个轻量级的数据库,他运算速度快,占用资源少,还支持基本SQL语法,一般使用他作为复杂数据的存储引擎,可以存储用户信息等。

SQLite数据库的创建

  • 使用SQLiteOpenHelper创建数据库,需要创建一个子类并重写onCreate()、onUpgrade()以及onOpen()等这些回调方法。
     public class MyHelper extends SQLiteOpenHelper {
    
    
           public MyHelper(Context context) {
    
    
           		//    上下文    数据库名     游标工厂 数据库版本
                 super(context, "itcast.db", null, 2);
            }
            //第一次创建时调用,用于初始化表结构
            public void onCreate(SQLiteDatabase db) {
    
    
                  db.execSQL("CREATE TABLE information(_id INTEGER PRIMARY 
                  KEY AUTOINCREMENT, name VARCHAR(20), price INTEGER)");
            }
            //当数据库的版本号增加时调用
           public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
    
            }
    }

操作数据库数据

  • 操作数据库中的数据,需要使用SQLiteDatabase类,首先使用helper获取一个该类的对象:
    SQLiteDatabase db = helper. getWritableDatabase();
    
  • 然后通过db对象对数据进行增删改查。
  • 该类中包含一系列的针对数据库内容操作的方法。常用方法有:
    public void execSQL (String sql, Object[] bindArgs)
    
    /*
    table 要插入的表
    nullColumnHack 可以是null,SQL中不允许插入完全空的行,所以当第三个参数为空或者它里面没有内容,insert会失败,为防止这种情况,在这里指定一个列名,一旦发现将要插入的行为空行时,会将这个参数指定的列设置为null,再向数据库中插入。
    values 这里是ContentValues对象,底层为map键值对,是每行的列值,key为列名,value为列值。*/
    public long insert (String table, String nullColumnHack, ContentValues values)
    
public void insert(String name,String price) {
    
    
     MyHelper helper = new MyHelper(MainActivity.this);
     //获取可读写SQLiteDatabse对象
     SQLiteDatabase db = helper.getWritableDatabase();
     //创建ContentValues对象并将数据添加到ContentValues对象中
     ContentValues values = new ContentValues(); 
     values.put("name", name); 
     values.put("price", price);
     //调用insert()方法将数据添加到数据库中
     long id = db.insert("information",null,values); 
     //关闭数据库
     db.close();                                            
}  

  • 使用query()方法从数据库中读取数据,传递给它你的查询条件和所需要的列。查询的结果为一个Cursor对象,游标在第一条之前。该方法被重载了几次,其中的一个为:
    public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
    

参数如下:

  • table 被查询的表名
  • columns 查询返回的列,如果该参数为null,则返回所有的列
  • selection 相当于SQL中的where子句,为null的话,返回该表的所有行
  • selectionArgs 在上面参数中可能有?,这里这个参数既是实际参数,来取代相应顺序的?
  • groupBy 相当于是SQL中的GROUP BY子句,如果是null,则不会分组
  • having 相当于SQL中的having子句,如果是null表示所有的分组.
  • orderBy 行的排序结果,相当于SQL中的ORDER BY子句,如果是null则表示默认顺序,此时可能是无序的返回值为Cursor对象,其中为查询结果集

Curosr

  • Cursor是一个接口,用于作为存放查询的结果集,它提供了随机读写结果集的方法
  • rawQuery方法中的查询类似于execSQL中的使用SQL的select语句,只是execSQL不会返回值,而rawQuery方法会返回一个Cursor对象
public void find(int id){
    
    
    MyHelper helper = new MyHelper(MainActivity.this);
    SQLiteDatabase db = helper.getReadableDatabase();
    //调用query ()方法查询数据库中的数据,返回一个行数集合Cursor
    Cursor cursor = db.query("information", null, "_id=?", new String[]{
    
    id+""},null, null, null);
    //数据总条数
    if (cursor.getCount() != 0){
    
       
   		   //移动游标到下一行
           while (cursor.moveToNext()){
    
    
           //获取数据
            String _id = cursor.getString(cursor.getColumnIndex("_id"));
            String name = cursor.getString(cursor.getColumnIndex("name"));
            String price = cursor.getString(cursor.getColumnIndex("price"));
        }
    }
    cursor.close(); 
    db.close();
}


  • 删除信息
    该方法如下:
public int delete (String table, String whereClause, String[] whereArgs)

参数:

  • table 表名
  • whereClause 相当于where子句,如为null,则删除所有行
  • whereArgs 对应where中的?的实际参数
    返回:
  • 受影响的行数,没有行被删除则返回0.
public int delete(long id){
    
    	
	//调用delete ()方法删除数据库中指定数据
	SQLiteDatabase db = helper.getWritableDatabase();
	int number = db.delete("information", "_id=?", new String[]{
    
    id+""});
	db.close();
	return number;
}

数据库的事务

  • 事务是针对数据库的一组操作,它可以由一条或者多条SQL语句组成。
  • 事务具有原子性,就是说,事务中的语句要么都执行,要么都不执行,若其中有语句没有成功执行,则已经成功执行的语句会发生回滚。
  • SQLite是遵守ACID的关系型数据库管理系统,ACID是指数据库事务正确执行的四个基本要素:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
  • 下面是标准的事务处理模式:
   db.beginTransaction();
   try {
    
    
     ///...
     db.setTransactionSuccessful();
   } finally {
    
    
     db.endTransaction();
   }
  • 事务是可以嵌套的,也就是说在try中可以有子事务,只有外层的事务完成,则全部事务才算完成。try中的最后一条语句应0是setTransactionSuccessful,设置事务成功执行。在finally子句中关闭事务:endTransaction。
    在这里插入图片描述

习题

  • 错误纠正:
    • 选择32题:在下列选项中,存放sqlite3命令行工具的目录是sdk/tools
    • 填空22题:在Activity中,可以使用 openFileOutput() 方法可以打开/data/data/包名/files/a.txt文件的输出流对象
  • Android—数据存储练习题

猜你喜欢

转载自blog.csdn.net/yang2330648064/article/details/131276937