這篇文章介紹Activity的插件化方案汤纸,Activity的插件化方案不止今天介紹的這一種。建議在看本文之前疙剑,先看我的前兩篇博客,如果前兩篇有認(rèn)真看過椭岩,那么閱讀本文至多十分鐘完事兒!
- Android插件化系列第(一)篇---Hook技術(shù)之Activity的啟動(dòng)過程攔截
- Android插件化系列第(二)篇---動(dòng)態(tài)加載技術(shù)之a(chǎn)pk換膚
- Android插件化系列第(四)篇---插件加載機(jī)制兩種方案
看過上面文章巧涧,我們知道了怎么加載一個(gè)插件中的Activity類了薯蝎,想做Activity的插件化,還需要解決幾個(gè)問題谤绳。
- 1占锯、插件中所有的Activity都沒有在宿主中注冊(cè),怎么欺騙AMS去啟動(dòng)一個(gè)清單文件不存在的Activity闷供;
- 2烟央、把一個(gè)Activity類加載之后统诺,怎么使插件里的Activity具有生命周期歪脏;
- 3、插件apk中用過的各種資源粮呢,如何動(dòng)態(tài)的加載資源婿失。
關(guān)于問題1,在本系列的第一篇已經(jīng)說過啄寡,首先在宿主中聲明一個(gè)ProxyActivity豪硅,然后啟動(dòng)ProxyActivity,即:ProxyActivity + 插件中沒注冊(cè)的Activity = 標(biāo)準(zhǔn)的Activity
關(guān)于問題2挺物,其實(shí)在本系列第一篇也有體現(xiàn)懒浮,準(zhǔn)備在下一篇寫的更清楚一點(diǎn)。
關(guān)于問題3识藤,在本系列的第二篇也寫過砚著,以一個(gè)換膚的例子說明怎么樣去加載插件中的資源。
本篇博客主要講述的是第二個(gè)問題痴昧,把一個(gè)Activity類加載之后稽穆,怎么使插件里的Activity具有生命周期。
這里使用Activity代理模式赶撰。老套路舌镶,在宿主APK注冊(cè)一個(gè)ProxyActivity(代理Activity),就是作為占坑使用豪娜。每次打開插件APK里的某一個(gè)Activity的時(shí)候餐胀,都是在宿主里使用啟動(dòng)ProxyActivity,然后在ProxyActivity的生命周期里方法中瘤载,調(diào)用插件中的Activity實(shí)例的生命周期方法否灾,從而執(zhí)行插件APK的業(yè)務(wù)邏輯。所以思路就來了惕虑。
第一坟冲、ProxyActivity中需要保存一個(gè)Activity實(shí)例磨镶,該實(shí)例記錄著當(dāng)前需要調(diào)用插件中哪個(gè)Activity的生命周期方法。
第二健提、ProxyActivity如何調(diào)用插件apk中Activity的所有生命周期的方法,使用反射呢琳猫?還是其他方式。
這種Activity的插件化思想最初來自dynamic-load-apk私痹,dynamic-load-apk調(diào)用插件apk中Activity的所有生命周期的方法最初使用的是反射的方式脐嫂,因?yàn)榉瓷鋾?huì)影響效率,最后換成接口的方式紊遵。先看反射這種方式账千,在代理activity中,第一步要獲取將要反射所有生命周期方法暗膜。
protected void instantiateLifecircleMethods(Class<?> localClass) {
String[] methodNames = new String[] {
"onRestart",
"onStart",
"onResume",
"onPause",
"onStop",
"onDestory"
};
for (String methodName : methodNames) {
Method method = null;
try {
method = localClass.getDeclaredMethod(methodName, new Class[] { });
method.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//mActivityLifecircleMethods是一個(gè)map集合
mActivityLifecircleMethods.put(methodName, method);
}
Method onCreate = null;
try {
onCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });
onCreate.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
mActivityLifecircleMethods.put("onCreate", onCreate);
Method onActivityResult = null;
try {
onActivityResult = localClass.getDeclaredMethod("onActivityResult",
new Class[] { int.class, int.class, Intent.class });
onActivityResult.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
mActivityLifecircleMethods.put("onActivityResult", onActivityResult);
}
第二步匀奏,反射所有Activity的生命周期方法
@Override
protected void onResume() {
super.onResume();
Method onResume = mActivityLifecircleMethods.get("onResume");
if (onResume != null) {
try {
onResume.invoke(mRemoteActivity, new Object[] { });
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
protected void onPause() {
Method onPause = mActivityLifecircleMethods.get("onPause");
if (onPause != null) {
try {
onPause.invoke(mRemoteActivity, new Object[] { });
} catch (Exception e) {
e.printStackTrace();
}
}
super.onPause();
}
protected void setRemoteActivity(Object activity) {
try {
//插件中Activity對(duì)象
mRemoteActivity = (Activity) activity;
} catch (ClassCastException e) {
e.printStackTrace();
}
}
這是反射的方式,后面dynamic-load-apk經(jīng)過優(yōu)化学搜,改成接口的方式娃善,將activity的生命周期方法封裝一個(gè)接口,代理activity中實(shí)現(xiàn)一下這個(gè)接口瑞佩,然后還是通過代理activity去調(diào)用插件activity實(shí)現(xiàn)的生命周期方法聚磺。
public interface DLPlugin {
public void onStart();
public void onRestart();
public void onActivityResult(int requestCode, int resultCode, Intent data);
public void onResume();
public void onPause();
public void onStop();
public void onDestroy();
public void onCreate(Bundle savedInstanceState);
public void setProxy(Activity proxyActivity, String dexPath);
public void onSaveInstanceState(Bundle outState);
public void onNewIntent(Intent intent);
public void onRestoreInstanceState(Bundle savedInstanceState);
public boolean onTouchEvent(MotionEvent event);
public boolean onKeyUp(int keyCode, KeyEvent event);
public void onWindowAttributesChanged(LayoutParams params);
public void onWindowFocusChanged(boolean hasFocus);
}
@Override
protected void onStart() {
mRemoteActivity.onStart();
super.onStart();
}
@Override
protected void onRestart() {
mRemoteActivity.onRestart();
super.onRestart();
}
@Override
protected void onResume() {
mRemoteActivity.onResume();
super.onResume();
}
@Override
protected void onPause() {
mRemoteActivity.onPause();
super.onPause();
}
這就是dynamic-load-apk之中Activity插件化方案,如果要使用這種方案,我們開發(fā)插件需要遵循一定的規(guī)范炬丸,dynamic-load-apk要求遵循的規(guī)范如下:
1瘫寝、慎用this(接口除外):因?yàn)閠his指向的是當(dāng)前對(duì)象,即apk中的activity稠炬,但是由于activity已經(jīng)不是常規(guī)意義上的activity焕阿,所以this是沒有意義的,但是如果this表示的是一個(gè)接口而不是context酸纲,比如activity實(shí)現(xiàn)了而一個(gè)接口捣鲸,那么this繼續(xù)有效。
2闽坡、使用that:既然this不能用栽惶,那就用that,that是apk中activity的基類BaseActivity中的一個(gè)成員疾嗅,它在apk安裝運(yùn)行的時(shí)候指向this外厂,而在未安裝的時(shí)候指向宿主程序中的代理activity,anyway代承,that is better than this汁蝶。
-3、activity的成員方法調(diào)用問題:原則來說,需要通過that來調(diào)用成員方法掖棉,但是由于大部分常用的api已經(jīng)被重寫墓律,所以僅僅是針對(duì)部分api才需要通過that去調(diào)用用。同時(shí)幔亥,apk安裝以后仍然可以正常運(yùn)行耻讽。
4、啟動(dòng)新activity的約束:?jiǎn)?dòng)外部activity不受限制帕棉,啟動(dòng)apk內(nèi)部的activity有限制针肥,首先由于apk中的activity沒注冊(cè),所以不支持隱式調(diào)用香伴,其次必須通過BaseActivity中定義的新方法startActivityByProxy和startActivityForResultByProxy慰枕,還有就是不支持LaunchMode。
5即纲、目前暫不支持Service具帮、BroadcastReceiver等需要注冊(cè)才能使用的組件,但廣播可以采用代碼動(dòng)態(tài)注冊(cè)崇裁。
可以看到限制非常大匕坯,現(xiàn)在估計(jì)不會(huì)有誰采用這種方式做插件化開發(fā)了,但是2014年那個(gè)時(shí)候拔稳,dynamic-load-apk這種思想還是非常先進(jìn)的,相信這為后來的插件化方案提供了很寶貴的經(jīng)驗(yàn)锹雏,下一篇博客講解用Hook方式巴比,實(shí)現(xiàn)業(yè)界比較倡導(dǎo)的一種Activity插件化方案。
Please accept mybest wishes for your happiness and success !