相關(guān)閱讀
插件化知識(shí)梳理(1) - Small 框架之如何引入應(yīng)用插件
插件化知識(shí)梳理(2) - Small 框架之如何引入公共庫(kù)插件
插件化知識(shí)梳理(3) - Small 框架之宿主分身
插件化知識(shí)梳理(4) - Small 框架之如何實(shí)現(xiàn)插件更新
插件化知識(shí)梳理(5) - Small 框架之如何不將插件打包到宿主中
插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理
插件化知識(shí)梳理(7) - 類的動(dòng)態(tài)加載入門
插件化知識(shí)梳理(8) - 類的動(dòng)態(tài)加載源碼分析
插件化知識(shí)梳理(9) - 資源的動(dòng)態(tài)加載示例及源碼分析
插件化知識(shí)梳理(10) - Service 插件化實(shí)現(xiàn)及原理
一、Service 插件化思路
很可惜,Small不支持Service的插件化,但是在項(xiàng)目中我們確實(shí)有這樣的需求,那么就需要研究一下如何自己來(lái)實(shí)現(xiàn)Service的插件化。在討論如何實(shí)現(xiàn)Service的插件化之前,必須有三點(diǎn)準(zhǔn)備:
- 掌握
Service的基本知識(shí),包括Service的生命周期、如何啟動(dòng)和結(jié)束Service,bindService和startService之間的區(qū)別和聯(lián)系,這一部分就不過(guò)多介紹了,大家可以看一下 Carson_Ho 大神這篇文章,介紹的很全面 Android四大組件:Service服務(wù)史上最全面解析。 - 了解
Service啟動(dòng)的內(nèi)部原理,對(duì)源碼進(jìn)行一次簡(jiǎn)單的走讀,主要是對(duì)Service調(diào)用者、所有者以及AMS之間的三方通信有一個(gè)清晰的認(rèn)識(shí),大家可以看一下我之前寫的這篇文章 Framework 源碼解析知識(shí)梳理(5) - startService 源碼分析。 - 掌握動(dòng)態(tài)代理的知識(shí),這里在前面分析
Retrofit的時(shí)候也有介紹過(guò),Retrofit 知識(shí)梳理(2) - Retrofit 動(dòng)態(tài)代理內(nèi)部實(shí)現(xiàn)。
在 插件化知識(shí)梳理(6) - Small 源碼分析之 Hook 原理 這篇文章中,我們一起學(xué)習(xí)了如何實(shí)現(xiàn)Activity的插件化,簡(jiǎn)單地來(lái)說(shuō),實(shí)現(xiàn)原理就是:
- 在調(diào)用
startActivity啟動(dòng)插件Activity后,通過(guò)替換mInstrumentation成員變量,攔截這一啟動(dòng)過(guò)程,在后臺(tái)偷偷地把startActivity時(shí)傳入的intent中的component替換成為在AndroidManifest.xml中預(yù)先注冊(cè)的占坑Activity,再通知ActivityManagerService。 - 當(dāng)
ActivityManagerService完成調(diào)度后,有替換客戶端中的ActivityThread中的mH中的mCallback,將占坑的Activity重新恢復(fù)成插件的Activity。
而Service的插件化也可以采用類似的方式,大體的思路如下:
- 在調(diào)用
startService啟動(dòng)插件Service時(shí),通過(guò)攔截ActivityManagerProxy的對(duì)應(yīng)方法,將Intent中的插件Service類型替換成預(yù)先在AndroidManifest.xml中預(yù)先注冊(cè)好的占坑Service。 - 當(dāng)
AMS通過(guò)ApplicationThreadProxy回調(diào)占坑Service對(duì)應(yīng)的生命周期時(shí),我們?cè)僭谡伎?code>Service中的onStartCommand中,去創(chuàng)建插件Service的實(shí)例,如果是第一次創(chuàng)建,那么先調(diào)用它的onCreate方法,再調(diào)用它的onStartCommand方法,否則,就只調(diào)用onStartCommand方法就可以了。 - 在調(diào)用
stopService停止插件Service時(shí),同樣通過(guò)攔截ActivityManagerProxy的對(duì)應(yīng)方法,去調(diào)用插件Service的onDestroy,如果此時(shí)發(fā)現(xiàn)沒(méi)有任何一個(gè)與占坑Service關(guān)聯(lián)的插件Service運(yùn)行時(shí),那么就可以停止插件Service了。
二、具體實(shí)現(xiàn)
傳了一個(gè)簡(jiǎn)單的例子到倉(cāng)庫(kù),大家可以簡(jiǎn)單地對(duì)照著看一下,下面,我們開(kāi)始分析具體的實(shí)現(xiàn)。
2.1 準(zhǔn)備工作
初始化的過(guò)程如下所示:
public void setup(Context context) {
try {
//1.通過(guò)反射獲取到ActivityManagerNative類。
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(activityManagerNativeClass);
//2.獲取mInstance變量。
Class<?> singleton = Class.forName("android.util.Singleton");
Field instanceField = singleton.getDeclaredField("mInstance");
instanceField.setAccessible(true);
//3.獲取原始的對(duì)象。
Object original = instanceField.get(gDefault);
//4.動(dòng)態(tài)代理,用于攔截Intent。
Class<?> iActivityManager = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(context.getClassLoader(), new Class[]{ iActivityManager }, new IActivityManagerInvocationHandler(original));
instanceField.set(gDefault, proxy);
//5.讀取插件當(dāng)中的Service。
loadService();
//6.占坑的Component。
mStubComponentName = new ComponentName(ServiceManagerApp.getAppContext().getPackageName(), StubService.class.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
這里最主要的就是做了兩件事:
2.1.1 對(duì) ActivityManagerNative.getDefault() 調(diào)用的攔截
這里應(yīng)用到了動(dòng)態(tài)代理的知識(shí),我們用Proxy.newProxyInstance所創(chuàng)建的proxy對(duì)象,替代了ActivityManagerNative中的gDefault靜態(tài)變量。在 Framework 源碼解析知識(shí)梳理(1) - 應(yīng)用程序與 AMS 的通信實(shí)現(xiàn) 中,我們分析過(guò),它其實(shí)是一個(gè)AMS在應(yīng)用程序進(jìn)程中的代理類。通過(guò)這一替換過(guò)程,那么當(dāng)調(diào)用ActivityManagerNative.getDefault()方法時(shí),就會(huì)先經(jīng)過(guò)IActivityManagerInvocationHandler類,我們就可以根據(jù)invoke所傳入的方法名,在方法真正被調(diào)用之前插入一些自己的邏輯(也就是前文所說(shuō)的,將啟動(dòng)插件Service的Intent替換成啟動(dòng)占坑Service的Intent),最后才會(huì)通過(guò)Binder調(diào)用到達(dá)AMS端,以此達(dá)到了“欺騙系統(tǒng)”的目的。
下面是InvocationHandler的具體實(shí)現(xiàn),構(gòu)造函數(shù)中傳入的是原始的ActivityManagerProxy對(duì)象。
private class IActivityManagerInvocationHandler implements InvocationHandler {
private Object mOriginal;
public IActivityManagerInvocationHandler(Object original) {
mOriginal = original;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
switch (methodName) {
case "startService":
Intent matchIntent = null;
int matchIndex = 0;
for (Object object : args) {
if (object instanceof Intent) {
matchIntent = (Intent) object;
break;
}
matchIndex++;
}
if (matchIntent != null && ServiceManager.getInstance().isPlugService(matchIntent.getComponent())) {
Intent stubIntent = new Intent(matchIntent);
stubIntent.setComponent(getStubComponentName());
stubIntent.putExtra(KEY_ORIGINAL_INTENT, matchIntent);
//將插件的Service替換成占坑的Service。
args[matchIndex] = stubIntent;
}
break;
case "stopService":
Intent stubIntent = null;
int stubIndex = 0;
for (Object object : args) {
if (object instanceof Intent) {
stubIntent = (Intent) object;
break;
}
stubIndex++;
}
if (stubIntent != null) {
boolean destroy = onStopService(stubIntent);
if (destroy) {
//如果需要銷毀占坑的Service,那么就替換掉Intent進(jìn)行處理。
Intent destroyIntent = new Intent(stubIntent);
destroyIntent.setComponent(getStubComponentName());
args[stubIndex] = destroyIntent;
} else {
//由于在onStopService中已經(jīng)手動(dòng)調(diào)用了onDestroy,因此這里什么也不需要做,直接返回就可以。
return null;
}
}
break;
default:
break;
}
Log.d("ServiceManager", "call invoke, methodName=" + method.getName());
return method.invoke(mOriginal, args);
}
}
先看startService,args參數(shù)中保存了startService所傳入的實(shí)參,這里面就包含了啟動(dòng)插件Service的Intent,我們將目標(biāo)Intent的Component替換成為占坑的Component,然后將原始的Intent保存在KEY_ORIGINAL_INTENT字段當(dāng)中,最后,通過(guò)原始的對(duì)象調(diào)用到ActivityManagerService端。
2.1.2 加載插件 Service 類
這里其實(shí)就是用到了前面介紹的DexClassLoader的知識(shí),詳細(xì)的可以看一下前面的這兩篇文章 插件化知識(shí)梳理(7) - 類的動(dòng)態(tài)加載入門,插件化知識(shí)梳理(8) - 類的動(dòng)態(tài)加載源碼分析。
最終,我們會(huì)將插件Service的Class對(duì)象保存在一個(gè)mLoadServices的Map當(dāng)中,它的Key就是插件Service的包名和類名。
private void loadService() {
try {
//從插件中加載Service類。
File dexOutputDir = ServiceManagerApp.getAppContext().getDir("dex2", 0);
String dexPath = Environment.getExternalStorageDirectory().toString() + PLUG_SERVICE_PATH;
DexClassLoader loader = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, ServiceManagerApp.getAppContext().getClassLoader());
try {
Class clz = loader.loadClass(PLUG_SERVICE_NAME);
mLoadedServices.put(new ComponentName(PLUG_SERVICE_PKG, PLUG_SERVICE_NAME), clz);
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
2.2 啟動(dòng)插件 Service
接下來(lái),在宿主中通過(guò)下面的方式啟動(dòng)插件Service類:
public void startService(View view) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(ServiceManager.PLUG_SERVICE_PKG, ServiceManager.PLUG_SERVICE_NAME));
startService(intent);
}
按照前面的分析,首先會(huì)走到我們預(yù)設(shè)的“陷阱”當(dāng)中,可以看到,這里面的Intent還是插件Service的Component名字:

然而,經(jīng)過(guò)替換,最終調(diào)用時(shí)的
Intent就變成了占坑的Service。
如果一切正常,接下來(lái)占坑
Service就會(huì)啟動(dòng),依次調(diào)用它的onCreate和onStartCommand方法,我們?cè)?code>onStartCommand中,再去回調(diào)插件Service對(duì)應(yīng)的生命周期:
public class StubService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.d("StubService", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("StubService", "onStartCommand");
ServiceManager.getInstance().onStartCommand(intent, flags, startId);
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
ServiceManager.getInstance().onDestroy();
Log.d("StubService", "onDestroy");
}
}
這里,我們?nèi)〕鲋氨4嬖?code>KEY_ORIGINAL_INTENT中原始的Intent,通過(guò)它找到對(duì)應(yīng)插件Service的包名和類名,以此為key,在mLoadedServices中找到前面從插件apk中加載的Service類,并通過(guò)反射實(shí)例化該對(duì)象,如果是第一次創(chuàng)建,那么先執(zhí)行它的onCreate方法,并將它保存在mAliveServices中,之后再執(zhí)行它的onStartCommand方法。

這一過(guò)程的打印如下圖所示:

2.3 停止插件 Service
當(dāng)我們通過(guò)stopService方法,停止插件Service時(shí),也會(huì)和前面類似,先走到攔截的邏輯當(dāng)中:

而在
onStop方法中,我們判斷它是否是需要停止插件Service,如果是那么就調(diào)用插件Service的onDestory()方法,并且判斷與占坑Service相關(guān)聯(lián)的插件Service是否都已經(jīng)結(jié)束了,如果是,那么就返回true,讓占坑Service也銷毀。
銷毀的時(shí)候,就是將插件
Service的Intent替換成占坑Service:
這時(shí)的打印為:

三、總結(jié)
以上,就是實(shí)現(xiàn)插件化Service的核心思路,實(shí)現(xiàn)起來(lái)并不簡(jiǎn)單,需要涉及到很多的知識(shí),這已經(jīng)是插件化學(xué)習(xí)的第十篇文章了。如果大家能一路看下來(lái),可以發(fā)現(xiàn),其實(shí)插件化并沒(méi)有什么神秘的地方,如果我們希望實(shí)現(xiàn)任意一個(gè)組件的插件化,無(wú)非就是以下幾點(diǎn):
- 組件的生命周期。
- 組件的啟動(dòng)過(guò)程,最主要就是和
ActivityManagerService的交互過(guò)程,這也是最難的地方,要花很多的時(shí)間去看源碼,而且各個(gè)版本的API也可能有所差異。 - 插件化常用技巧,也就是
Hook,動(dòng)態(tài)代理之類的知識(shí)。 - 類動(dòng)態(tài)加載的知識(shí)。
掌握了以上幾點(diǎn),對(duì)于市面上大廠的插件框架基本能夠看懂個(gè)六七成,但是對(duì)于大多數(shù)人而言,并沒(méi)有這么多的時(shí)間和條件,去分析一些細(xì)節(jié)問(wèn)題。我寫的這些文章,也只能算是入門水平,和大家一起學(xué)習(xí)基本的思想。真正核心的東西,還是需要有機(jī)會(huì)能應(yīng)用到生產(chǎn)環(huán)境中才能真正掌握。
很可惜,我也沒(méi)有這樣的機(jī)會(huì),感覺(jué)每天工作的時(shí)間都是在調(diào)UI、解Bug、浪費(fèi)時(shí)間,只能靠著晚上的時(shí)間,一點(diǎn)點(diǎn)摸索,寫Demo,哎,說(shuō)出來(lái)都是淚,還有半年,繼續(xù)加油吧!
更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.itdecent.cn/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/