关于存储的操作,一切的核心优化都在以下三个方面:
1.避免在主线程I/O
2.减少I/O读写量
3.减少I/O的操作次数
从这三个方面,下面介绍如何针对不同的存储方式进行相应的优化:
-
针对SharedPreferences,需要做到如下的优化:
-
SP文件没有被加载到内存时,调用getSharedPreferences方法会初始化文件并读入内存,建议在Application中初始化,重写attachBaseContext方法,SharedPreference的context传入Application对象即可,最好使用单例,不必每次都获取Sp对象,减少开销。(可以归结到预加载,后续使用时,效率更高)
-
不要使用SharedPreference存储大文件及存储大量的key和value,这样的话会造成界面卡顿或者ANR,比较占内存。记住它是简单存储,如果有类似的需求请考虑数据库、磁盘文件存储等等。(这样做的目的就是减少I/O的读写量)
-
推荐使用apply进行存储,这也是官方推荐,当读入内存后,因为它是异步写入磁盘的,所以效率上会比commit好,如果你需要存储状态或者即存即用的话还是尽量使用commit。 (这样做的本质其实就是尽量避免在主线程操作I/O)
-
尽量不要存放Json及html,数据少可以,无需担心,大量的话请放弃。(这个其实也是说的是减少I/O的读写量)
-
不要所有的数据都存在一个文件,不同类型的文件或者数据可以分开多个文件存储, 避免使用一个大文件,这样可提高读取速度。(这个其实也是说的是减少I/O的读写量)
-
跨进程操作不要使用MULTI_PROCESS标志,而是使用contentprovide等进程间通信的方式。(跨进程使用sp,官方文档提示不支持多进程,多进程使用数据可能会丢失。不建议使用)
-
如果你的项目对于存储性能要求非常高的情况,可以考虑放弃系统的SharedPreference存储,推荐你使用腾讯的高性能组件MMKV。
-
-
针对Sqlite数据库,需要做到如下的优化:
-
减少使用select * ,这样就能减少从数据库读取的数据量,减少耗时。(本质就是减少I/O读写量)
-
利用缓存减少重复的读取。(本质就是减少I/O读写量)
-
数据库减少AUTOINCREMENT 因为这样要多操作一个表,InSert耗时2~4倍。(本质就是减少I/O读写量)
-
使用合适的数据库分页,sqlite读/写磁盘是以page为单位的,在3.12.0版本之前,Sql默认page size是1KB,从3.12.0开始,page size调整为4KB。(本质就是减少I/O读写量)
-
频繁查询的表使用索引,索引可以极大地减少读磁盘的数据量,极大的提升效率,但是会增加插入和更新的时间,所以,对于读取频率大于写入频率的情景,建议使用。(本质就是减少I/O读写量)
-
避免无效索引,无效索引的问题通常是严重的。除了触发全表扫描,产生大量冗余的读/写之外,还降低了写入性能。(本质就是减少I/O读写量)
-
批量更新数据库使用事务,启用事务,根据业务规模,会大量减少I/O读/写量和操作次数,从而提升效率.(本质是减少I/O次数)
-
ZIP压缩大量小文件时建议使用ZipInputStream。
-
增加查询条件当你只要一条数据时增加limit 1,这样搜索到了后面的就不会再查询了,大大的加快了速度。(本质就是减少I/O读写量)
-
提前将字段的index映射好减少getColumnIndex的时间,可以缩短一半的时间。(本质就是减少I/O读写量)
-
通过冗余换取查询速度,这个要看具体使用场景了,如果太多冗余,也会造成增加I/O的读写量和次数。
-
-
针对ContentProvider优化,需要做到如下的优化:
@Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException{ SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try{ ContentProviderResult[]results = super.applyBatch(operations); db.setTransactionSuccessful(); return results; }finally { db.endTransaction(); } }
在处理数据时使用ContentProviderOperation:
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
然后循环加入数据库操作:
ContentValues value = new ContentValues(); ops.add(ContentProviderOperation.newUpdate(MyProvider.CONTENT_URI) .withSelection("_id='" + entry.getId() + "'", null) .withValues(value) .withYieldAllowed(true) .build());
然后提交操作:
try { mContext.getContentResolver().applyBatch(MyProvider.AUTHORITY, ops); } catch (RemoteException e) { e.printStackTrace(); } catch (OperationApplicationException e) { e.printStackTrace(); } }
这样,用了事务之后,数据库效率会大大提升。
总结:
参考:
《Android移动性能实战》