全面插件化时代RePlugin来临
一、RePlugin简介
RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案。我们“逐>词”拆开来解释这个定义:
完整的:让插件运行起来“像单品那样”,支持大部分特性
稳定的:如此灵活完整的情况下,其框架崩溃率仅为业内很低的“万分之一”
适合全面使用的:其目的是让应用内的“所有功能皆为插件”
占坑类:以稳定为前提的Manifest占坑思路
插件化方案:基于Android原生API和语言来开发,充分利用原生特性
RePlugin:<a href="https://github.com/Qihoo360/RePlugin">Github地址</a>
RePlugin使用简易示例:<a href="https://github.com/aBenVip/RePluginHostDemo.git">Github地址</a>仅作为学习交流使用,欢迎star
二、RePlugin配置说明
RePlugin分宿主工程和插件工程,需要在工程下配置以下内容
1、宿主工程配置
A)在Project级别gradle的dependencies中添加
classpath 'com.qihoo360.replugin:replugin-host-gradle:2.1.3'
B)在Moudle级别gradle中添加
apply plugin: 'replugin-host-gradle'
compile 'com.qihoo360.replugin:replugin-host-lib:2.1.3'
2、插件工程配置
A)在Project级别gradle的dependencies中添加
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.1.4'
B)在Moudle级别gradle中添加
apply plugin: 'replugin-plugin-gradle'
compile 'com.qihoo360.replugin:replugin-plugin-lib:2.1.3'
三、RePlugin内置插件
1、内置插件安装
内置插件以jar文件的形式放置在assets->plugins目录下(注:内置插件只有放在此目录下才能被RePlugin识别并自动安装,如plugins文件夹未自动创建,需要手动创建)
2、内置插件识别机制
插件的相关配置信息采用json的格式进行存储,存储在assets->plugins-builtin.json文件下,程序启动时,由RePlugin识别并自动进行插件安装
[{"high":null,
"frm":null,
"ver":100,
"low":null,
"pkg":"com.qihoo360.replugin.sample.demo1",
"path":"plugins/demo1.jar","name":"demo1"
}]
3、内置插件升级
A)主程序随包升级
安装包更新时,若插件版本更新时自动升级
B)通过install方法升级
同外置插件升级,RePlugin.install()方法进行插件升级
4、内置插件删除
直接移除jar文件即可,RePlugin会完成剩余操作
四、RePlugin外置插件
1、外置插件安装
i. 外置插件安装方式
采用加载apk的方式,使用RePlugin.install()方法进行安装
ii. 外置插件安装注意事项
如果处于debug模式,需要在application中关闭签名校验,通过对RePluginConfig的处理,关闭签名校验,(预计下个版本会默认关闭签名校验,不过官方建议在release中打开该校验)
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//--------------开发的时候不验证签名
RePluginConfig config = new RePluginConfig();
config.setVerifySign(!BuildConfig.DEBUG);
config.setPrintDetailLog(BuildConfig.DEBUG);
config.setUseHostClassIfNotFound(true);
RePlugin.App.attachBaseContext(this, config); }
iii. 插件的安装
通过RePlugin.install()方法进行安装,可以通过RePlugin.preload()方法对插件进行预加载,提升用户体验
PluginInfo pluginInfo = RePlugin.install(absolutePath + "/aa.apk");
if (pluginInfo != null) {
Toast.makeText(MainActivity.this, "插件安装成功",Toast.LENGTH_SHORT).show();
boolean preload = RePlugin.preload(pluginInfo);
if (preload){
Toast.makeText(MainActivity.this, "预加载完成", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "插件安装失败",Toast.LENGTH_SHORT).show();
}
iiii. 插件的版本
需要Plugin的AndroidManifest中对插件的别名和版本进行设置,不设置的话以包名和version为默认值(注:暂不支持同版本覆盖,升级插件必须修改版本号,卸载重新安装也需要进行版本修改)
<meta-data
android:name="com.qihoo360.plugin.name"
android:value="plugin01" />
<meta-data
android:name="com.qihoo360.plugin.version.ver"
android:value="114" />
2、插件的升级
通过RePlugin.install(pi)方法进行升级
3、插件的卸载
通过RePlugin.uninstall("demo01")卸载
五、RePlugin宿主与插件交互
1、宿主打开插件中的四大组件
A)Activity
RePlugin.startActivity(MainActivity.this, RePlugin.createIntent("plugin01", "com.dg.replugindemo01.MainActivity"));
B)Service
PluginServiceClient.bindService(RePlugin.createIntent( "exam", "AbcService"), mServiceConn);
B)Service
PluginServiceClient.bindService(RePlugin.createIntent( "exam", "AbcService"), mServiceConn);
B)ContentProvider
PluginProviderClient.query(xxx);
。。。。
2、宿主调用插件方法
A)fetchComponentList(String pluginName);//获取插件的组件列表
B)fetchPackageInfo(String pluginName);//获取插件的包信息
C)fetchResources(String pluginName);//获取插件的资源信息
D)fetchClassLoader(String pluginName);//获取插件的ClassLoader对象
E)fetchContext(String pluginName);//获取插件的Context对象
F)fetchBinder(String pluginName, String module, String process);
//通过插件里的Plugin类,获取插件定义的IBinder
示例:
//加载插件中的工具类
ClassLoader classLoader = RePlugin.fetchClassLoader("plugin01");
try {
Class<?> aClass = classLoader.loadClass("com.dgcredit.replugindemo01.DateHelper");
Method formatDate = aClass.getMethod("formatDate", Date.class);
String invoke = (String) formatDate.invoke(null, new Date());
tvShow.setText(invoke);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
///加载插件,并获取插件的资源信息
Resources resources = RePlugin.fetchResources("plugin01");
int identifier = resources.getIdentifier("icon_app", "drawable", "com.dgcredit.replugindemo01");
ivShow.setImageResource(identifier);
3、插件调用宿主方法
//加载宿主中的工具类
ClassLoader classLoader = RePlugin.getHostClassLoader();
try {
Class<?> aClass = classLoader.loadClass("com.dgcredit.repluginhostdemo.DateHelper");
Method formatDate = aClass.getMethod("formatDate", Date.class);
String invoke = (String) formatDate.invoke(null, new Date());
viewById.setText(invoke);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//加载宿主中图片资源
ClassLoader classLoader = RePlugin.getHostClassLoader();
try{
Class clazz = classLoader.loadClass("com.dgcredit.repluginhostdemo.R$drawable");
Field field = clazz.getField("icon_app");
int identifier = (int)field.get(null);
ivShow.setImageResource(identifier);
}catch(Exception e){
Log.i("Loader", "error:"+Log.getStackTraceString(e));
}
4、插件调用宿主组件
Intent intent = new Intent();
intent.setComponentName(new ComponentName("com.qihoo360.replugin.sample.host", "com.qihoo360.replugin.sample.host.MainActivity"));
context.startActivity(intent);
六、RePlugin接入错误集锦
1、Theme错误
Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
解决方式
A)去除插件中对application的theme的引入,单独对插件中的activity设置相关theme
android:theme="@style/AppTheme"
B)在宿主的build.gradle中加入
repluginHostConfig {
useAppCompat = true
}
2、ActivityNotFoundException错误
Unable to find explicit activity class{com.qihoo360.replugin.sample.host/con.qihoo360.replugin.sample.host.loader.a.ActivityN1NRNTS3};have you declared this activity in your AndroidManifest.xml?
解决方式:
关闭Setting中instand run功能。目前此Bug在官方2.1.4版本已修复,尚未验证
3、Fail to apply plugin [id 'replugin-plugin-gradle']错误
解决方式:
这个plugin需要放在android配置之后,因为需要读取android中的配置项
4、外置插件RePlugin.install()方法无反应
解决方式:
@override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
//-------------- 开发的时候不验证签名 -----------//
RePluginConfig config = new RePluginConfig();
config.setVerifySign(!BuildConfig.DEBUG);
RePlugin.App.attachBaseContext(this, config);
// =======================//
}