接下来来聊聊数据层,而做处理数据时,有一条原则请大家铭记于心:耗时的IO操作要异步进行,不能阻塞UI渲染线程!
安卓的数据存储主要分成下面3种类型,分别有适用的场景和条件。
安卓框架提供的数据库是SQLite,本身是一个典型的关系数据库,是设计给嵌入式设备或是轻量级应用来用的。当然我记得官方宣称单表能到4G等指标,但是一般还是轻量级应用和客户端用的比较多。
SQLite最大的优点就是用起来和mysql一样,裸写SQL或者用安卓的ORM都比较方便,默认的数据库文件的路径是在应用目录里,一般是在内部存储里。
下面是一个典型的数据库类模板:
public class HelloDB { private static int DATABASE_VERSION = 1; private static final String DATABASE_NAME = "hello.db"; private DatabaseHelper helper; private Context context; public HelloDB(Context context) { helper = new DatabaseHelper(context); this.context = context; } public Context getContext() { return context; } private class DatabaseHelper extends SQLiteOpenHelper { public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE notification (id INTEGER PRIMARY KEY, json TEXT, INTEGER new)"); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } }
SQLite的使用需要注意的方面有:
id | 需要排序的字段 | json字符串
直接存json字符串,这样做的好处是服务端数据结构有变化时,并不需要改动表的列,比较灵活。但是注意,需要排序(也就是where后面可能会出现的字段)还是单独成列比较好,用数据库就要充分利用SQL的特性,如果只是存储,那还不如用文件
try { SQLiteDatabase db = helper.getWritableDatabase(); db.beginTransaction(); db.execSQL("update notification set new = 0"); db.setTransactionSuccessful(); db.endTransaction(); } catch (SQLiteException e) { e.printStackTrace(); }
SharePreferences是key/value的存储,实质上是应用目录里的一个XML文件。一般用来存储设置信息,应用现场信息等。
SharedPreferences sp = context.getSharedPreferences("my_pref", MODE_PRIVATE);
String s = pref.getString("key", "default_value");
//edit这个名字略脑残,其实安卓的底层代码还是有很多丑陋的存在的 _(:з」∠)_
sp.edit().putString("key", "new_value").commit();//注意!一定要commit,否则不会生效。
getSharedPreferences的第二个参数mode,具体含义如下
SP注意事项:
文件一般用于存储cache或者图片等大量数据,典型的如某个列表的前20条数据,存成一个cache文件;或图片的本地缓存。
一些数据量比较小,但是需要快速存取的可以使用内部存储(其实内部存储和外部存储的性能差别并不太大)
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close(); //注意!因为IO操作,一定要注意异步处理。
如果是图片等比较占空间的数据,或者增长比较快数据,是要放到外部存储的。因为外部存储比较大,但是因为外部存储一般是SD卡,是可以被卸载的,需要监控具体的状态
boolean mExternalStorageAvailable = false; boolean mExternalStorageWriteable = false; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { // We can read and write the media mExternalStorageAvailable = mExternalStorageWriteable = true; } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // We can only read the media mExternalStorageAvailable = true; mExternalStorageWriteable = false; } else { // Something else is wrong. It may be one of many other states, but all we need // to know is we can neither read nor write mExternalStorageAvailable = mExternalStorageWriteable = false; }
打开外部存储的文件的api是context的getExternalFilesDir(),是从API 8引入的,可以设定一个type,其实就是一个子目录。API 7以下使用getExternalStorageDirectory()得到外部存储的目录。因为现在的版本分布,我们可以认为API 8是兼容的最低线了。