Android数据存储的5种方式

本文基于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. 网络存储

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355