根据官方Demo,尝试接入RePlugin。
宿主程序接入
- 在根目录的build.gradle文件中添加RePlugin宿主的插件依赖
classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.3'
注意:
replugin-host-gradle
- 在宿主程序app的build.gradle中添加RePlugin的核心依赖库和应用
replugin-host-gradle
插件
需要注意的是 a. 宿主引入的核心库是apply plugin: 'com.android.application' android { ... } dependencies { ... implementation 'com.qihoo360.replugin:replugin-host-lib:2.3.3' } apply plugin: 'replugin-host-gradle' /** * 配置项均为可选配置,默认无需添加 * 更多可选配置项参见replugin-host-gradle的RepluginConfig类 * 可更改配置项参见 自动生成RePluginHostConfig.java */ repluginHostConfig { //是否使用 AppCompat 库,如果使用除了在宿主app中添加AppComat-v7依赖还需要 //配置该属性 useAppCompat = true }
replugin-host-lib
b. RePlugin插件需要放在android配置代码块后面,否则会出现applicationId找不到而导致报错 - 配置Application,这里使用的是继承自RePluginApplication方法,也可以选择继承自定义的Application然后,在各个生命周期中调用RePlugin.App.XXX。详情可参考官方文档
在application中可以自定义配置一些参数,如下public class SampleApplication extends RePluginApplication { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); RePlugin.enableDebugger(base, BuildConfig.DEBUG); } /** * RePlugin允许提供各种“自定义”的行为,让您“无需修改源代码”,即可实现相应的功能 */ @Override protected RePluginConfig createConfig() { RePluginConfig c = new RePluginConfig(); return c; } }
更多配置可以查看//是否启用调试器,Debug阶段建议开启,Release阶段建议关闭,默认为关闭状态 RePlugin.enableDebugger(base, BuildConfig.DEBUG); //用来自定义RePlugin行为的类,必须在RePlugin.App.attachBaseContext中被传递 //如果是继承自RePluginApplication可以重写createConfig() RePluginConfig c = new RePluginConfig () //设置插件回调方法,可自定义插件框架的回调行为 c.setCallbacks(RePluginCallbacks callbacks) //设置插件化框架的事件回调方法,调用者可自定义插件框架的事件回调行为 c.setEventCallbacks(RePluginEventCallbacks eventCallbacks) //当插件没有指定类时,是否允许使用宿主的类?若为true,则当插件内没有指定类时,将默认使用宿主的。 c.setUseHostClassIfNotFound(boolean useHostClassIfNotFound) //设置插件是否开启签名校验。默认为false。但强烈建议Release时候开启此开关, //此开关将必须和 RePlugin.addCertSignature 配合使用 c.setVerifySign(boolean verifySign) //在插件安装时,是否将文件“移动”到app_p_a目录下?默认为True c.setMoveFileWhenInstalling(boolean moveFileWhenInstalling) //是否在Art上对首次加载插件速度做优化,默认为false c.setOptimizeArtLoadDex(boolean optimizeArtLoadDex)
RePluginConfig
类。在配置的时候看到有两个回调的类RePluginCallbacks
和RePluginEventCallbacks
RePluginCallbacks:
可以自定义宿主和插件的ClassLoader或者当找不到插件时触发回调等RePluginEventCallbacks:
当安装插件成功失败或者插件中的activity启动销毁等则回调该类
插件程序接入
- 在根目录的build.gradle文件中添加RePlugin插件的插件依赖
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.3'
注意:
replugin-plugin-gradle
- 在插件程序的build.gradle中添加RePlugin的核心依赖库和应用
replugin-plugin-gradle
插件
需要注意的是 a. 宿主引入的核心库是apply plugin: 'com.android.application' android { ... } dependencies { ... implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.3.3' } apply plugin: 'replugin-plugin-gradle' /** * 配置项均为可选配置,默认无需添加 * 更多可选配置项参见replugin-plugin-gradle的RepluginConfig类 */ repluginPluginConfig{ //插件名称 pluginName = "demo1" //宿主的ApplicationId hostApplicationId = "com.zh.replugindemo" //宿主的启动页面全名 hostAppLauncherActivity = "com.zh.replugindemo.MainActivity" }
replugin-plugin-lib
b. RePlugin插件需要放在android配置代码块后面,否则会出现applicationId找不到而导致报错
插件安装
- 外置插件
所谓外置插件是指可通过“下载”、“放入SD卡”等方式来安装并运行的插件,这样的插件可以通过RePlugin.install("apk路径")
方法直接安装。升级插件也一样
- 内置插插件
添加内置插件方式:
- 将生成的apk文件重新命名为:"插件名".jar
- 放入主程序的assets/plugins目录下
这样,当编译主程序时,RePlugin中的“动态编译方案”会自动在assets目录下生成一个名叫“plugins-builtin.json”文件,记录了其内置插件的主要信息,方便运行时直接获取。
预加载
无论是外置插件安装还是内置插件,都不会提前释放优化的资源,这样可以节约内存空间,因为不是所有插件都会去使用,只有当使用的时候才会加载资源,加载时候会有明显的卡顿因为过程是同步的并且比较耗时。
我们可以选择一个时机调用RePlugin.preLoad("插件名")
,去预加载插件。它会将插件的dex“提前做释放”,并将Dex缓存到内存中,这样在下次启动插件时,可无需走dex2oat过程,速度会快很多。预加载需要放在子线程中执行避免ANR。
常用API
- 插件管理
//安装或者升级指定路径下的apk插件 RePlugin.install(String pluginPath); //卸载指定名称的插件 RePlugin.uninstall(String pluginName); //预加载指定名称的插件 RePlugin.preload(String pluginName); //判断插件是否已被安装(但不一定被使用过,如可能不会释放Dex、Native库等) RePlugin.isPluginInstalled(String pluginName); //判断当前插件是否已释放了Dex、Native库等 RePlugin.isPluginDexExtracted(String pluginName); /* * 判断插件是否曾被使用过。只要释放过Dex、Native的,就认为是“使用过”的 * 和isPluginDexExtracted的区别:插件会在升级完成后,会删除旧Dex。 * 其isPluginDexExtracted为false,而isPluginUsed仍为true */ RePlugin.isPluginUsed(String pluginName);
- 插件中组件调用
1.开启Activity
宿主开启插件的Activity
//第一种方式
Intent intent = new Intent();
intent.setComponent(new Component("插件名称", "Activity全名"));
context.startActivity(intent);
//第二种方式
Intent intent = RePlugin.createIntent("插件名称", "Activity全名");
context.startActivity(intent);
//第三种方式
RePlugin.startActivity(context, intent)
RePlugin.startActivity(context, intent, "插件名称", "Activity全名")
RePlugin.startActivityForResult(context, intent, requestCode)
插件开启宿主的Activity,基本上和宿主开始插件一样只是注意传递的是宿主的包名
Intent intent = new Intent()
intent.setComponent(new Component("宿主的包名", "Activity全名"));
context.startActivity(intent);
我们可以通过RePlugin.getHostContext()
获取宿主的Context,通过Context可以获取到宿 主的包名等信息
2.宿主启动插件中的Service,和开发单品传递的参数类似,只是通过PluginServiceClient
类调用
//开启服务
PluginServiceClient.startService(context, intent)
//停止服务
PluginServiceClient.stopService(context, intent)
//绑定服务
PluginServiceClient.bindService(context, intent, serviceConnection, flag)
//解除绑定
PluginServiceClient.unbindService(context, intent, serviceConnection, flag)
3.插件中Binder通信
首先需要在Binder具体实现的插件模块中在Application中提前调用RePlugin.registerPluginBinder()
需要传入注册的IBinder名称(随便取)和Binder接口的实现类。
//plugindemo2中Application
public class MainApp extends Application {
@Override
public void onCreate() {
super.onCreate();
RePlugin.registerPluginBinder("demo2test", new Demo2Impl()); }
}
}
然后在需要跨插件调用的地方通过RePlugin.fetchBinder("插件名", "注册时IBinder名称")
,获取到IBinder对象。通过AIDL协议生成的IInterface
的实现类其内部的Binder类,调用它的asInterface()方法获取到Binder代理,实现插件之间的Binder通信
//plugindemo1和plugindemo2通过binder通信
IBinder b = RePlugin.fetchBinder("demo2", "demo2test");
if (b == null) {
return;
}
IDemo2 demo2 = IDemo2.Stub.asInterface(b);
try {
demo2.hello("helloooooooooooo");
} catch (RemoteException e) {
e.printStackTrace();
}
4.RePlugin中fetchXXX方法
//在当前进程加载插件,并通过插件里的Plugin类,获取插件定义的IBinder
RePlugin.fetchBinder("插件名","要加载的模块")
//获取到指定插件的ClassLoader,通过ClassLoader可以加载class文件
RePlugin.fetchClassLoader("插件名")
//获取指定插件的Context
RePlugin.fetchContext("插件名")
//获取指定插件的资源Id
RePlugin.fetchResourceIdByName("插件名", "要获取的“资源类型+资源名”")//例如"layout/activity_main"
//获取指定指定插件的Resource对象
RePlugin.fetchResource("插件名")
//获取指定插件的View对象
RePlugin.fetchViewByLayoutName("","布局名称",rootView)