Android ContentProvider 是一个SQLite数据库,分为数据提供方和数据使用方。 二者通过匿名共享内存来传输数据,数据不需要从一个地址复制到另一个地址,当数据量很大的时候,速度是非常快的。
并不是所有的数据传递都需要ContentProvider, 比如 Activity 跳转需要传递 字符串,整数之类的数据,这些数据的传递是基于Binder来实现的, Binder跨进程通信速度也很快。当要传输数据量不超过1M的时候,使用Binder; 数据量超过1M时,就需要使用ContentProvider.
ContentProvider的插件化:
Android Apk 安装时,所有的ContentProvider的安装都是通过ActivityThread的installContentProvider方法实现的。可以使用该方式将插件的ContentProvider 加载进来。
具体步骤:
1)将插件apk 和 宿主apk 的dex 合并起来
2)读取插件中的ContentProvider信息,借助PackageParser的parsePackage方法,然后提供generateProviderInfo 方法,把得到的Package 对象转换为ProviderInfo对象。
3)将插件ContentProvider放入宿主中,通过修改ContentProvider的packageName
4) 通过反射执行ActivityThread的installContentProviders, 将插件contentprovider 作为参数。
具体代码如下:
public static List<ProviderInfo> parseProviders(File apkFile) {
try{
//获取PackageParser对象实例
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");
Object packageParser = packageParserClass.newInstance();
// 首先调用parsePackage获取到apk对象对应的Package对象
Class[] p1 = {File.class, int.class};
Object[] v1 = {apkFile, PackageManager.GET_PROVIDERS};
Object packageObj = ReflectUtil.invokeInstanceMethod(packageParser, "parsePackage",p1, v1);
// 读取Package对象里面的services字段
// 接下来要做的就是根据这个List<Provider> 获取到Provider对应的ProviderInfo
List providers = (List) ReflectUtil.getFieldObject(packageObj, "providers");
// 调用generateProviderInfo 方法, 把PackageParser.Provider转换成ProviderInfo
//准备generateProviderInfo方法所需要的参数
Class<?> packageParser$ProviderClass = Class.forName("android.content.pm.PackageParser$Provider");
Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState");
Object defaultUserState = packageUserStateClass.newInstance();
int userId = (Integer) ReflectUtil.invokeStaticMethod("android.os.UserHandle", "getCallingUserId",null,null);
Class[] p2 = {packageParser$ProviderClass, int.class, packageUserStateClass, int.class};
List<ProviderInfo> ret = new ArrayList<>();
// 解析出intent对应的Provider组件
for (Object provider : providers) {
Object[] v2 = {provider, 0, defaultUserState, userId};
ProviderInfo info = (ProviderInfo) ReflectUtil.invokeInstanceMethod(packageParser, "generateProviderInfo",p2, v2);
ret.add(info);
}
return ret;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
public static void installProviders(Context context, File apkFile){
try{
List<ProviderInfo> providerInfos = parseProviders(apkFile);
for (ProviderInfo providerInfo : providerInfos) {
providerInfo.applicationInfo.packageName = context.getPackageName();
}
Object currentActivityThread = ReflectUtil.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");
Class[] p1 = {Context.class, List.class};
Object[] v1 = {context, providerInfos};
ReflectUtil.invokeInstanceMethod(currentActivityThread, "installContentProviders", p1, v1);
}catch (Exception e){
e.printStackTrace();
}
}