前言
因为将原项目的单进程架构改为多进程架构,引发了不少跨进程调用和跨进程使用共享数据的问题。本篇文章分享几个我在跨进程共享数据时使用的开源框架。
跨进程的SharedPreference——Tray
首先官方已经在API23后废除了SharedPreference的多进程模式:
至于之前能否保证跨进程安全,好像也是不太可靠的。所以建议使用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
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
如果在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());
}