Android插件化框架,支持APK免安装运行
此插件框架主要目的用于加载非第三方插件,即插件会认为其在插件框架中运行,且大部分情况下对宿主或其它插件存在依赖。
如果你需要类似应用双开加载任意第三方Apk的能力,请参考VirtualApp
- 支持 API 14+, 适配Android O
- 低入侵,对插件开发透明,无需任何修改即可直接加载插件apk
- 支持Android四大组件,Activity, Service, ContentProvider, Broadcast等,支持so加载。
- 支持多进程,提供4种进程模式可供选择。
- 调试方便,编译生成的插件apk即可以独立运行,也可以在插件框架中运行。
- 框架原理较为简洁,Hook单一,易于集成。
- 插件间,插件与宿主间除了可以通过标准的系统api通信,PluginApi中还提供一套IInvoker框架,提供支持跨进程的函数调用,Binder服务获取。
- 不支持Activity的TaskAffinity属性, 不支持overridePendingTransition中携带插件自定义资源(可以通过PluginApi接口动态的获取宿主的资源)
- 不支持通知中携带插件自定义资源。
- 不支持系统或其它App直接调起插件组件,需要宿主预埋相应的组件做桥接*处理(即调用关系为: 系统或其它App->宿主->插件)。
- 暂不支持插件中宿主资源与插件资源合并,即插件的资源是隔离的,无法在插件中直接使用宿主的资源(可以通过PluginApi接口动态的获取宿主的资源)
- 插件中需要的所有权限需要预埋到宿主中。
-
PluginApi: 插件通信Api模块。此模块专门为非独立插件提供插件间,插件与宿主间的通信。如果插件不需要与宿主或其它插件进行通信,可以不用依赖此模块。
-
PluginManager: 插件核心框架。
-
testhost: 测试宿主Demo,可以免安装启动/sdcard/目录中的apk
-
testplugin: 测试插件Demo, 主要包含Android四大组件,so加载的测试。
-
testplugin2: 另一个测试插件Demo, 主要包含插件间,插件与宿主间调用测试。
-
pluginsharelib: 插件共享代码库,可以作为公共功能模块在插件中直接使用。
执行这个脚本直接为为你编译好testplugin,testplugin2的apk,并启动testhost, 进行插件测试:
chmod +x install-test
./install-test
如果你还想测试其它apk作为插件,可以将apk放入/sdcard/目录中即可。
- 将PluginM作为library引入到您的宿主项目中
dependencies {
implementation project(':PluginManager')
...
}
- 预埋插件中需要使用到的权限
- 在您的Application中添加如下代码:
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 插件配置
PluginConfigs pluginConfigs = new PluginConfigs();
// 选择插件进程模式,总共为4种,默认为PROCESS_TYPE_INDEPENDENT
// 1. PROCESS_TYPE_INDEPENDENT:独立进程模式, 即为每个插件独享一个进程。
// 2. PROCESS_TYPE_SINGLE:单一进程模式, 即所有插件都分配在一个固定的进程。
// 3. PROCESS_TYPE_DUAL:双进程模式, 即所有插件都分配在两个固定的进程(一个前台进程,一个后台进程)。
// 4. PROCESS_TYPE_COMPLETE:完整进程模式, 即所有插件都完全拥有自身全部的进程,进程名与插件声明的进程名称一致。
// 进程名与插件包名相同的在一个特定进程,否则在另一个特定进程。
pluginConfigs.setProcessType(pluginConfigs.PROCESS_TYPE_INDEPENDENT)
// 如果插件中使用插件ClassLoader加载未成功时,是否允许宿主尝试加载。默认为true
.setUseHostLoader(Prefs.Config.getUseHostClassloader(this, true))
// 是否开启签名检测,默认为false
//.setSignatureCheckEnabled(true)
// 如果开启签名检测,请填写合法的签名信息. 默认为空
//.addSignatures(MaybeYourHostSignatures)
PluginM.onAttachBaseContext(this, pluginConfigs);
}
- 如果插件不依赖于宿主或其它插件,则不用做任何处理,直接编译出APK即可被加载
- 如果插件依赖与宿主或其它插件,则需要依赖于PluginApi,请在插件工程的build.gradle中加入PluginApi依赖.
dependencies {
implementation project(':PluginApi')
...
}
// 安装插件
PluginM.install(pluginApkPath);
// 卸载插件(插件已运行时无法卸载,需要重启进程后卸载)
PluginM.uninstall(pluginPackageName);
// 获取已安装插件列表
PluginM.getAllInstalledPlugins();
// 宿主中启动插件Activity
PluginM.startActivity(context, intent);
// 宿主中启动插件Service
PluginM.startService(context, intent);
// 宿主中访问插件ContentProvider
PluginM.getContentResolver(context).query(....);
// 使用IInvoker框架进行函数式调用 (调用宿主或其它插件)
IInvokeResult result = PluginM.invoke(packageName, serviceName, methodName, params, callback);
// 使用IInvoker框架进行Binder服务获取 (调用宿主或其它插件)
IBinder binder = PluginM.fetchService(packageName, serviceName);
插件中启动四大组件不需要任何特殊处理
// 插件中启动四大组件不需要任何特殊处理。
Intent intent = new Intent(this, PluginActivity.class);
startActivity(intent);
// 获取宿主context
PluginHelper.getHostContext(context);
// 获取插件package信息
PluginHelper.getPluginPackageInfo(context, packageName, flags)
// 使用IInvoker框架进行函数式调用(调用宿主或其它插件)
IInvokeResult result = PluginHelper.invoke(packageName, serviceName, methodName, params, callback);
// 使用IInvoker框架进行Binder服务获取 (调用宿主或其它插件)
IBinder binder = PluginHelper.fetchService(packageName, serviceName);
除了四大组件的标准系统api提供的通信机制,PluginApi还为插件间,插件与宿主间的通讯提供了另一种标准接口框架。详细api请参考IInvoker
宿主或插件都可以在Manifest中注册一个或多个IInvoker,用于提供对外部(插件或宿主)统一的函数接口调用和Binder服务获取。
<application .... >
<!-- 此插件中在插件的com.example.testplugin进程中注册了一个服务名称为main的IInvoker,
其具体实现在com.example.testplugin.MyPluginInvoker -->
<meta-data android:name="pluginm_invoker"
android:value="[
{'service':'main', 'class':'com.example.testplugin.MyPluginInvoker', 'process':'com.example.testplugin'}
]" />
......
......
</application>
service: 每个IInvoker都必须配置一个服务名称,同包名下不可以相同。
class: 每个IInvoker都必须配置一个实现IInvoker接口的实现类。
process: 每个IInvoker都必须存在于一个进程,如果未填写,则默认为与包名相同的进程。