-
前言
做久了App就知道一锅熟大杂烩不好受,因为之前看到系统有无图标的apk运行,所以自己做了组实验又恰逢当时在做公安局的一个项目,这种项目都是多家独立开发互不开放源码,又需要接入平台式的一个apk,没有源码接入就只能使用这种独立的无图标应用的方式,将内容剥离主程序——“插件化”,即插即用,若有源码则推荐直接使用aar包的形式进行组件化开发。
-
问题
- 疑问:现在的插件化框架那么多为什么还要存在利用无图标App这种低级方式?
这个都明白插件化框架干的什么,插件化技术分几个主要方向:占坑代理,钩子,字节码插装,但是这些犹如入侵技术不是一个万全之策,其稳定性、各家的使用规则性、安全性都不可控,经常受谷歌政策的影响,如果要做一款稳定多的、安全性可控的、开发方式遵循谷歌爸爸的,恰逢不开放源码的这种就非常实用,譬如公家的项目,最近也发现鲁大师的VR检测模块貌似也是这个路子。 - 疑问:平台App死亡,“插件”App在RecentList不会仍存在,万一点击任务快照会不会直接唤起“插件”App?
解:平台App死亡后,隐式打开的“插件”App并不会存在于RecentList中,很哈皮,估计是共享一个uid默认在一个任务栈中,原计划“插件”App监测平台App是否运行,不运行则弹窗拒绝开启并自杀,清理RecentList,后来发现清理行为不可为;。 - 疑问:平台App卸载后,“插件”App如何卸载?
解:除在【设置】【应用列表】中手动外无法卸载!原计划每个“插件”App实现一个静态Service,每个Service监听平台App是否卸载,若收到卸载广播则排队卸载,卸载完后Service直接自杀.若Service被不小心清理掉那就GG,不卸载了;后来发现无法这样操作,平台App卸载后无法卸载“插件”App,近日找了些APP做了对比发现鲁大师的VR检测模块也无法卸载,证明技术层面无法处理,建议主要在应用中进行业务提醒与引导,但是如果是使用代码进行卸载则可以进行广播与代码操作(卸载代码后面的代码将会继续顺序执行不受影响,现象好似多线程并行运行)。
- 疑问:如何控制模块的版本更新?
获取各模块应用的版本version进行比对,需要通过后台进行上传包的管理
//获取版本号代码
try {
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo("com.biabia.sonlauncher1", 0);
String versionName = info.versionName;
int versionCode = info.versionCode;
} catch (Exception e) {
e.printStackTrace();
}
在平台App上增加子模块管理并添加检测,如果子模块版本增加则下载子模块,此时会需要用户安装,需要在下载前进行业务引导。
- 疑问:如何进行模块间传值?
原计划模块间传值,将模块使用与平台App一样的签名并sharedUserId,使其在共享同一用户空间下,这样可以共享数据data目录下的数据或是据说通过获取目标context访问目标app的一些资源,所有的bean类传值皆以json形式,一App不可以直接访问另一个App内存值,但是这些传值方式其实都是够了的,只需要写一个调度管理工具类,sharedUserId本质上与process属性开多进程其实是效果相同,当然也可以通过process令其在同一进程中运行,所以后期废除了,现在有很多开源路由可以用,譬如ARouter不过据说它无法跨进程通信。
这个博客写的sharedUserId总结不错:https://www.cnblogs.com/mythou/p/3258715.html
猜想总结:
- 猜想“插件”App监测平台App是否运行不运行则弹窗拒绝开启并自杀(实际运行来看暂时不需要,可以忽略)
- 每个子模块需要记录各自版本用于更新
所遇弊端:
1.跳转时貌似遇到“冷启动慢的问题”,在第一次跳转时会遇到白屏,状态栏都是白的,看起来挺奇怪的,但是第二次打开后属于“热启动”没有这些问题,正常如初,这个可以设置一个“模块加载中...”的windowBackground图片背景给予提示,也可以开启一个同进程名的Service进行预加载,免去使用时的进程初始化等系列时间达到白屏消失的目的。
2.由于是几乎完全隔离的两个App,那么第三方包之类的不会进行共用,各个组件包重复率高。
3.通信不便,老生常谈,不作解释。(可以通过开源路由框架解决譬如ARouter,它不跨进程通信)
-
具体做法:
ComponentName 在实战中需从服务器拉取,平台方的CMS动态配置,这样才能跳转平台外的APP
1.平台App所做:
//跳转到“插件”APP
Intent intent = new Intent();
//EaseConstant.PAGENAME:子模块包名;
//"biabia.com.pigapp.MainActivity":子模块具体Activity(完整路径) EaseConstant.PAGENAME为子模块包名
ComponentName cn = new ComponentName(EaseConstant.PAGENAME,"biabia.com.pigapp.MainActivity");
intent.setComponent(cn);
intent.setAction("android.intent.action.MAIN");
intent.putExtra("exe","启动了");
try {
startActivityForResult(intent, RESULT_OK);
} catch (Exception e) {
ToastUtil.showToast(context,"暂无该子模块,请下载安装!");
}
2.“插件”App所做
a.若在平台Activty里面点击按钮跳转“插件”App 则模块设置为:
<!-- android:host为宿主Activity名 android:scheme为宿主Activity的包名-->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<data android:host="MainActivity" android:scheme="com.danze.im.ui"/>
</intent-filter>
</activity>
b.若在平台fragment里面点击按钮跳转到“插件”App, 则“插件”App设置为
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- android:host:平台App Fragment类名 android:scheme:平台App Fragment包名 -->
<data android:host="GridWorkFragment"
android:scheme="com.danze.im.ui.fragment"/>
</intent-filter>
</activity>