前言
因为将原项目的单进程架构改为多进程架构,引发了不少跨进程调用和跨进程使用共享数据的问题。本篇文章分享几个我在跨进程共享数据时使用的开源框架。
对跨进程调用感兴趣的同学,可以参阅另一篇文章《跨进程的EventBus,开源框架HermesEventBus使用小结》介绍了一种基于AIDL的跨进程开源框架的使用。
跨进程的SharedPreference——Tray
项目地址:https://github.com/grandcentrix/tray
首先官方已经在API23后废除了SharedPreference的多进程模式:
This constant was deprecated in API level 23.MODE_MULTI_PROCESS does not work reliably in some versions of Android, and furthermore does not provide any mechanism for reconciling concurrent modifications across processes. Applications should not attempt to use it. Instead, they should use an explicit cross-process data management approach such as ContentProvider.
至于之前能否保证跨进程安全,好像也是不太可靠的。所以建议使用ContentProvider来实现跨进程共享的数据,而在Github上的开源项目** tray ** 底层通过ContentProvider实现了跨进程共享数据的安全性,并且将上层API的使用封装的更像SharedProference。对跨进程SharedPreference有需求的同学不妨尝试使用它。
** gradle 引入项目: **
implementation 'net.grandcentrix.tray:tray:0.12.0' //低版本gradle用compile代替implementation
** 初始化 **
private AppPreferences appPreferences = new AppPreferences(context);
** 简单使用 **
boolean put(@NonNull final String key, @Nullable final String value);
String getString(@NonNull final String key, @Nullable final String defaultValue);
相比SharedPreference去掉了editor(),写入数据更像在用Map,不过只支持String和基本类型。
快速开发ContentProvider——ProviGen
项目地址:https://github.com/TimotheeJeannin/ProviGen
ProviGen提供了注解支持可以帮助我们在运行时自动创建数据库表,并且封装了ContentProvider模块,开发者只要让自己的Provider继承自ProviGenProvider不再需要自己实现insert()、update()、delete()、query()等接口。
** gradle 引入项目: **
implementation 'com.github.provigen:ProviGen-lib:2.0.+' //低版本gradle用compile代替implementation
** 实现自定义Contract:**
public interface ILocalMessage extends ProviGenBaseContract {
@Column(Column.Type.INTEGER)
public static final String Id = "id";
@Column(Column.Type.TEXT)
public static final String Title = "title";
@Column(Column.Type.TEXT)
public static final String Summary = "summary";
@Column(Column.Type.TEXT)
public static final String Content ="content";
@Column(Column.Type.INTEGER)
public static final String Type = "type";
@Column(Column.Type.INTEGER)
public static final String Status = "status";
@Column(Column.Type.INTEGER)
public static final String Level = "level";
@Column(Column.Type.TEXT)
public static final String Recievetime = "receive_time";
@Column(Column.Type.INTEGER)
public static final String Isfocus = "focus";
@Column(Column.Type.TEXT)
public static final String Username = "user_name";
@ContentUri
public static final Uri CONTENT_URI = Uri.parse("content://xxx.xxx.xxx/message_table");
}
这个类中需要定义一个数据库表的数据结构,通过注解@Column标记列名变量以及它的类型,通过@ContentUri标记这个数据库表的URI。
** 下一步完善自定义的Provider类:**
public class MessageProvider extends ProviGenProvider {
private static Class[] messages = new Class[]{ILocalMessage.class};
@Override
public SQLiteOpenHelper openHelper(Context context) {
return new CipherProviGenOpenHelper(getContext(), "message-db", null, 1, messages);
}
@Override
public Class[] contractClasses() {
return messages;
}
@Override
public boolean onCreate() {
return super.onCreate();
}
** 接着只需要在AndroidManifest注册Provider组件 :**
<provider
android:name=".provider.MessageProvider"
android:authorities="xxx.xxx.xxx"
android:enabled="true"
android:exported="true"></provider>
下面就可以通过Context.getContentResolver()的增删改成接口跨进程访问数据库了。
加密的SQLite数据库——android-database-sqlcipher
项目地址:https://github.com/sqlcipher/android-database-sqlcipher
如果在ROOT的手机上运行我们的程序,是可以被轻易拿到SQLite数据库文件的,那么不对数据库加密就显得很不安全的了。android-database-sqlcipher让加密SQLite数据库变得简单。
** 引入项目: **
先编译出so文件,放入到libs文件夹下,然后配置gradle:
android{
sourceSets{
main{
jniLibs.srcDir(['libs'])
}
}
}
dependencies {
implementation files('libs/sqlcipher.jar')
}
** 加载so **
SQLiteDatabase.loadLibs(getContext());
注意这段代码需要在SQlite创建前执行,否则无法获取so提供的api导致程序崩溃。
** 使用方面 **
android-database-sqlcipher提供了net.sqlcipher.database.SQLiteDatabase和net.sqlcipher.database.SQLiteOpenHelper,开发者需要替换掉原生的SQLiteDatabase和SQLiteOpenHelper。
在得到数据库时,需要指定密钥:
public synchronized SQLiteDatabase getWritableDatabase(String password) {
return this.getWritableDatabase(password == null?null:password.toCharArray());
}