-
前言
做久了App就知道一鍋熟大雜燴不好受,因?yàn)橹翱吹较到y(tǒng)有無(wú)圖標(biāo)的apk運(yùn)行,所以自己做了組實(shí)驗(yàn)又恰逢當(dāng)時(shí)在做公安局的一個(gè)項(xiàng)目怯伊,這種項(xiàng)目都是多家獨(dú)立開發(fā)互不開放源碼,又需要接入平臺(tái)式的一個(gè)apk陋守,沒有源碼接入就只能使用這種獨(dú)立的無(wú)圖標(biāo)應(yīng)用的方式震贵,將內(nèi)容剝離主程序——“插件化”,即插即用水评,若有源碼則推薦直接使用aar包的形式進(jìn)行組件化開發(fā)猩系。
-
問題
- 疑問:現(xiàn)在的插件化框架那么多為什么還要存在利用無(wú)圖標(biāo)App這種低級(jí)方式?
這個(gè)都明白插件化框架干的什么中燥,插件化技術(shù)分幾個(gè)主要方向:占坑代理寇甸,鉤子,字節(jié)碼插裝疗涉,但是這些猶如入侵技術(shù)不是一個(gè)萬(wàn)全之策拿霉,其穩(wěn)定性、各家的使用規(guī)則性咱扣、安全性都不可控绽淘,經(jīng)常受谷歌政策的影響,如果要做一款穩(wěn)定多的闹伪、安全性可控的沪铭、開發(fā)方式遵循谷歌爸爸的,恰逢不開放源碼的這種就非常實(shí)用偏瓤,譬如公家的項(xiàng)目,最近也發(fā)現(xiàn)魯大師的VR檢測(cè)模塊貌似也是這個(gè)路子杀怠。 - 疑問:平臺(tái)App死亡,“插件”App在RecentList不會(huì)仍存在厅克,萬(wàn)一點(diǎn)擊任務(wù)快照會(huì)不會(huì)直接喚起“插件”App赔退?
解:平臺(tái)App死亡后,隱式打開的“插件”App并不會(huì)存在于RecentList中,很哈皮硕旗,估計(jì)是共享一個(gè)uid默認(rèn)在一個(gè)任務(wù)棧中窗骑,原計(jì)劃“插件”App監(jiān)測(cè)平臺(tái)App是否運(yùn)行,不運(yùn)行則彈窗拒絕開啟并自殺,清理RecentList卵渴,后來(lái)發(fā)現(xiàn)清理行為不可為慧域;鲤竹。 - 疑問:平臺(tái)App卸載后浪读,“插件”App如何卸載?
解:除在【設(shè)置】【應(yīng)用列表】中手動(dòng)外無(wú)法卸載辛藻!原計(jì)劃每個(gè)“插件”App實(shí)現(xiàn)一個(gè)靜態(tài)Service碘橘,每個(gè)Service監(jiān)聽平臺(tái)App是否卸載,若收到卸載廣播則排隊(duì)卸載,卸載完后Service直接自殺.若Service被不小心清理掉那就GG吱肌,不卸載了痘拆;后來(lái)發(fā)現(xiàn)無(wú)法這樣操作,平臺(tái)App卸載后無(wú)法卸載“插件”App氮墨,近日找了些APP做了對(duì)比發(fā)現(xiàn)魯大師的VR檢測(cè)模塊也無(wú)法卸載纺蛆,證明技術(shù)層面無(wú)法處理,建議主要在應(yīng)用中進(jìn)行業(yè)務(wù)提醒與引導(dǎo)规揪,但是如果是使用代碼進(jìn)行卸載則可以進(jìn)行廣播與代碼操作(卸載代碼后面的代碼將會(huì)繼續(xù)順序執(zhí)行不受影響桥氏,現(xiàn)象好似多線程并行運(yùn)行)。
- 疑問:如何控制模塊的版本更新猛铅?
獲取各模塊應(yīng)用的版本version進(jìn)行比對(duì)字支,需要通過后臺(tái)進(jìn)行上傳包的管理
//獲取版本號(hào)代碼
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();
}
在平臺(tái)App上增加子模塊管理并添加檢測(cè),如果子模塊版本增加則下載子模塊奸忽,此時(shí)會(huì)需要用戶安裝堕伪,需要在下載前進(jìn)行業(yè)務(wù)引導(dǎo)。
- 疑問:如何進(jìn)行模塊間傳值栗菜?
原計(jì)劃模塊間傳值欠雌,將模塊使用與平臺(tái)App一樣的簽名并sharedUserId,使其在共享同一用戶空間下疙筹,這樣可以共享數(shù)據(jù)data目錄下的數(shù)據(jù)或是據(jù)說通過獲取目標(biāo)context訪問目標(biāo)app的一些資源,所有的bean類傳值皆以json形式,一App不可以直接訪問另一個(gè)App內(nèi)存值富俄,但是這些傳值方式其實(shí)都是夠了的,只需要寫一個(gè)調(diào)度管理工具類,sharedUserId本質(zhì)上與process屬性開多進(jìn)程其實(shí)是效果相同腌歉,當(dāng)然也可以通過process令其在同一進(jìn)程中運(yùn)行蛙酪,所以后期廢除了,現(xiàn)在有很多開源路由可以用翘盖,譬如ARouter不過據(jù)說它無(wú)法跨進(jìn)程通信桂塞。
這個(gè)博客寫的sharedUserId總結(jié)不錯(cuò):https://www.cnblogs.com/mythou/p/3258715.html
猜想總結(jié):
- 猜想“插件”App監(jiān)測(cè)平臺(tái)App是否運(yùn)行不運(yùn)行則彈窗拒絕開啟并自殺(實(shí)際運(yùn)行來(lái)看暫時(shí)不需要,可以忽略)
- 每個(gè)子模塊需要記錄各自版本用于更新
所遇弊端:
1.跳轉(zhuǎn)時(shí)貌似遇到“冷啟動(dòng)慢的問題”馍驯,在第一次跳轉(zhuǎn)時(shí)會(huì)遇到白屏阁危,狀態(tài)欄都是白的玛痊,看起來(lái)挺奇怪的,但是第二次打開后屬于“熱啟動(dòng)”沒有這些問題狂打,正常如初擂煞,這個(gè)可以設(shè)置一個(gè)“模塊加載中...”的windowBackground圖片背景給予提示,也可以開啟一個(gè)同進(jìn)程名的Service進(jìn)行預(yù)加載趴乡,免去使用時(shí)的進(jìn)程初始化等系列時(shí)間達(dá)到白屏消失的目的对省。
2.由于是幾乎完全隔離的兩個(gè)App,那么第三方包之類的不會(huì)進(jìn)行共用晾捏,各個(gè)組件包重復(fù)率高蒿涎。
3.通信不便,老生常談惦辛,不作解釋劳秋。(可以通過開源路由框架解決譬如ARouter,它不跨進(jìn)程通信)
-
具體做法:
ComponentName 在實(shí)戰(zhàn)中需從服務(wù)器拉取胖齐,平臺(tái)方的CMS動(dòng)態(tài)配置玻淑,這樣才能跳轉(zhuǎn)平臺(tái)外的APP
1.平臺(tái)App所做:
//跳轉(zhuǎn)到“插件”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","啟動(dòng)了");
try {
startActivityForResult(intent, RESULT_OK);
} catch (Exception e) {
ToastUtil.showToast(context,"暫無(wú)該子模塊,請(qǐng)下載安裝!");
}
2.“插件”App所做
a.若在平臺(tái)Activty里面點(diǎn)擊按鈕跳轉(zhuǎn)“插件”App 則模塊設(shè)置為:
<!-- 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.若在平臺(tái)fragment里面點(diǎn)擊按鈕跳轉(zhuǎn)到“插件”App呀伙, 則“插件”App設(shè)置為
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- android:host:平臺(tái)App Fragment類名 android:scheme:平臺(tái)App Fragment包名 -->
<data android:host="GridWorkFragment"
android:scheme="com.danze.im.ui.fragment"/>
</intent-filter>
</activity>