美文网首页安卓
Android数据存储的5种方式

Android数据存储的5种方式

作者: 小的橘子 | 来源:发表于2019-03-08 18:00 被阅读0次

    本文基于Android8.0验证

    1. SharedPrefenece

    存储路径/data/data/{包名}/shared_prefs/<文件名>
    利用XML存储键值对,适合存储一些简单的配置信息.

    2. 文件存储

    从 Android 4.4 开始,读取或写入应用私有目录中的文件不再需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 权限。应用私有目录指的是/data/data/{packageName}//storage/emulated/0/Android/data/{packageName}/

    内部存储

    /data/data/目录 ,对用户不可见

    String FILENAME = "hello_file.txt";
    String string = "hello world!";
    
    FileOutputStream fos = null;
    try {
        //文件路径  /data/data/com.example.myapplication/files/
        fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
        fos.write(string.getBytes());
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    openFileOutput还有其他Flag

    • MODE_PRIVATE 私有数据,其他应用无法访问
    • MODE_APPEND 文件已追加方式打开
    • MODE_WORLD_WRITEABLE 所有应用可读,注意:已被废弃,在Android8.0已不能使用该属性

    常用API

    getFilesDir(); // 返回 /data/data/com.example.myapplication/files/  文件夹
    fileList(); // 返回files/下的所有文件名字的数组
    deleteFile(fileName); // 删除files/下 文件名为fileName的文件
    

    总结

    • openFileOutput()的方法写入文件路径在/data/data/com.example.myapplication/files/
    • data/{pakcage}/在应用卸载后会清除

    外部存储

    外部存储指的是可移除的存储介质(例如 SD 卡)或内部(不可移除)存储.保存到外部存储的文件是全局可读取文件.外部存储用户是否在文件管理器中看到的. 存储分为两种:

    1. 应用卸载后,存储数据也会被删除
    // 返回/storage/emulated/0/Android/data/com.example.myapplication/files
    File externalFile = getExternalFilesDir(null);
    Log.i(TAG, "onCreate:" + externalFile.getAbsolutePath());
    
    • 除了getExternalFilesDir()之外,还有getExternalCacheDir(),文件将保存到/storage/emulated/0/Android/data/{packageName}/cache目录下,当文件不再需要时,记得把缓存文件删除。
    • 该部分也属于程序私有目录,故无需读写权限
    1. 永久存储,即使应用被卸载,存储的数据依然存在
    // 返回如下路径 /storage/emulated/0 
    File externalStoreage = Environment.getExternalStorageDirectory(); 
    String externalStoragePath = externalStoreage.getAbsolutePath();
    String directory = externalStoragePath + "/hello.txt";
    // directory输出为/storage/emulated/0/hello.txt
    Log.i(TAG, "onCreate: directory=" + directory);
    try {
        FileOutputStream fos = new FileOutputStream(directory);
        // 需要动态存储权限
        fos.write("helloworld".getBytes());
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    上面代码生成的文件在手机的文件管理器下点击Internal shared storeage进去,就可看到hello.txt

    3. SQLite存储

    SQLite与大型数据库的区别

    两者都是支持关系的关系型数据库,SQLite是一个嵌入型的轻量级数据库,适合小数据量,大型数据库独立运行在数据库服务器上,适合大数据量级别,大型数据库通常以网络的方式对外提供服务。

    SQLite的使用

    1. 通过SQLiteOpenHelper帮助创建数据库
      • 创建该对象后并不会回调onCreate(),onOpen()方法。
      • 调用该对象的getWritableDatabase或getReadableDatabase()才会回调onCreate(),onOpen()方法。
        此时会在/data/data/com.nan.exer/databases路径下生成创建的db
    2. getWritableDatabase()和getReadableDatabase()区别
      两者均返回SQLiteDatabase对象,用来实现对数据库的操作
      • getWritableDatabase()
        以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase() 方法就会出错。
      • getReadableDatabase()
        则是先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象。
        ``

    增删改查CRDU

    方式一 执行sql语句

    1. 增删改
    //将增删改的sql语句字符串直接通过db的execSQL()方法进行执行
    db.execSQL(sql);
    
    1. 查询
      查询不能通过execSQL(sql)来执行
    //通过rawQuery方法实现原始sql的执行,第二个参数为查询的条件,执行完返回Cursor对象
    db.rawQuery(sql,null);
    

    方式二 使用Android封装好的api实现

    我们只需要传入指定的参数即可,底层会将参数拼接成sql语句执行

    // 增删改查对应方法
    db.insert();
    db.delete();
    db.update();
    db.query();
    

    SQLite数据库升级

    一、软件v1.0

    安装v1.0,假设v1.0版本只有一个account表,这时走继承SQLiteOpenHelper的onCreate,不走onUpgrade。

    1、v1.0(直接安装v1.0)

    二、软件v2.0

    有2种安装软件情况:

    1、v1.0 --> v2.0 不走onCreate,走onUpgrade

    2、v2.0(直接安装v2.0) 走onCreate,不走onUpgrade

    v1.0版本只有一个account表,软件版本升级到v2.0了,但是v2.0数据库需要新增一个member表,那怎么办呢?这里有2种情况了:一种是安装了v1.0升级到v2.0,这时不会走继承SQLiteOpenHelper的onCreate,而是直接走onUpgrade,这时就要在onUpgrade添加member表的代码了,在onCreate加了也没用,因为这种情况都不走onCreate。。另一种情况就是用户从来没有安装过这个软件,直接安装v2.0,这时走继承SQLiteOpenHelper的onCreate,不走onUpgrade,所以要在onCreate添加member表的代码。这怎么办呢?这就要合理升级数据库版本了。

    三、软件v3.0

    假设v3.0又新增一个news表,这里有三种情况:

    1、v1.0 --> v3.0 不走onCreate,走onUpgrade

    2、v2.0 --> v3.0 不走onCreate,走onUpgrade

    3、v3.0(直接安装v3.0) 走onCreate,不走onUpgrade

    SQLite3 sql语句

    1. 创建表
    CREATE TABLE [IF NOT EXISTS] 表名(字段名称 数据类型 约束,...);
    eg:CREATE TABLE IF NOT EXISTS contact(_id Integer primary key autoincrement,name text,phone_number text);
    
    1. 删除表
    DROP TABLE [IF EXISTS] 表名;
    DROP TABLE [IF EXISTS] contact;
    
    1. 插入数据
    INSERT INTO 表名[字段1,字段2] values(values1,values2,...);
    eg:INSERT INTO contact VALUES(2,"wyn","110")//常用,必须从表的第一列到最后一列进行赋值
    eg:INSERT INTO contact(name,phone_number) VALUES("nan","111")//指明列名
    
    1. 修改数据
    UPDATE 表名 set 字段1=新值,字段2=新值 WHERE 条件
    eg:UPDATE contact SET name="hello",phone_number="10086" WHERE _id=1;
    eg:UPDATE contact SET name="hello",phone_number="10086";表示对所有数据进行修改
    
    1. 删除数据
    DELETE FROM 表名 WHERE 条件
    eg:DELETE FROM contact where _id=1;//删除_id为1的行数据
    eg:DELETE FROM contact;//删除所有行数据
    
    1. 查询数据
    SELECT 字段名 FROM 表名 WHERE 查询条件 GROUP BY 分组的字段 HAVING 筛选条件 ORDER BY 排序字段//GROUP BY,HAVING不常用
    eg:SELECT * FROM contact;
    SELECT * FROM contact WHERE _id=1;
    SELECT * FROM contact WHERE _id<>1;//_id不等于1
    SELECT * FROM contact WHERE _id=1 AND age>18;//与的意思
    SELECT * FROM contact WHERE name LIKE "%好%";//含有好的所有字符串
    SELECT * FROM contact WHERE name LIKE "_好%";//好前面只有一个字符的所有字符串
    SELECT * FROM contact WHERE name IS NULL;
    SELECT * FROM contact WHERE age BETWEEN 10 AND 20;//age在10到20之间的
    SELECT * FROM contact WHERE age>20 ORDER BY _id;//_id进行排序
    

    面试相关

    1. SQLite中事务处理是如何做的

    SQLite在做CRDU是默认开启了事务,然后把SQL语句最终交由SQLiteStatement并调用相应的CRDU方法,此时整个操作还在db-journal这个临时文件上进行,只有顺利完成才会更新.db数据库,否则就会被回滚。

    2. 删除SQLite中表一个字段如何做?

    SQLite数据库只允许添加表字段而不允许删除和修改表字段,只能采取复制思想,即创建一个表包含想要的字段,再将数据复制到该表中,最后删除旧表。

    3. 使用SQLite会有哪些优化操作?

    1. 使用事务做批量操作,如上
    2. 及时关闭Cursor,避免内存泄漏
    3. 数据库操作较多建议子线程执行
    4. ContentValues容量调整:其内部采用HashMap存储键值数据,ContentValues初始容量为8,扩容时翻倍。因此建议对ContentValues填入的内容进行估量,设定合适的容量,减少不必要的扩容。

    4. SQLite做批量操作有什么好方法吗?

    使用SQLite的事务处理,由于默认SQLite开启事务处理,故每一条都包含事务,如果操作较多,大大影响性能。提升性能有如下两种方式:

    1. 手动开启事务,批量操作,提交事务 (性能提升幅度大)
    2. 使用db.compileStatement返回的SQLiteStatement进行sql语句操作(性能提升小)

    实例如下

    String sql = "INSERT INTO table (number, nick) VALUES (?, ?)";  
    // 1. 开启事务
    db.beginTransaction();  
    // 2. 通过SQLiteStatement操作
    SQLiteStatement stmt = db.compileStatement(sql);  
    for (int i = 0; i < values.size(); i++) {  
        stmt.bindString(1, values.get(i).number);  
        stmt.bindString(2, values.get(i).nick);  
        stmt.execute();  
        stmt.clearBindings();  
    }  
       
    db.setTransactionSuccessful();  
    // 3. 结束事务
    db.endTransaction(); 
    

    SettingsProvider中DatabaseHelper就是采用该种方式

    SQLite3使用

    Sqlite3 对手机中数据库操作

    1. 进入程序的数据库位置(对于真机必须进行root才可以操作)
    cd databases/
    
    1. 打开数据库
    sqlite3 xxx.db
    
    1. 查看表格
    .tables
    
    1. 查看帮助则看其他使用
    .help
    
    1. 显示表头
    // 该命在sqlite3 打开数据库后执行
    1. ".header on" 启用表头
    2. ".mode column" 使用列模式
    

    sqlite3 : not found 问题

    原因:

    system/xbin/目录下不存在sqlite3的执行文件
    

    解决

    1. adb pull system/xbin/sqlite3 //对存在该文件的手机中操作
    2. adb remount //目标机器
    3. adb push sqlite3 system/xbin/ //文件权限需要和源文件保持一致
    

    db-journal作用

    • 该文件是临时文件,用于事务的回滚。事务开始时产生,事务结束时删除。当事务开始时掉电,db-journal文件就会保存在磁盘上,再次开机则可用于事务回滚。
    • 在Android上,该文件不会删除,当没有事务时该文件大小为0,存在事务时则该文件用于事务回滚。

    4. ContentProvider

    5. 网络存储

    相关文章

      网友评论

        本文标题:Android数据存储的5种方式

        本文链接:https://www.haomeiwen.com/subject/pctdpqtx.html