插件化技術(shù)和熱修復(fù)技術(shù)都屬于動(dòng)態(tài)加載技術(shù),從普及率的角度來看,插件化沒有熱修復(fù)的普及率高,主要原因是占大多數(shù)的中小型應(yīng)用很少也沒有必要去采用插件化技術(shù)。雖然插件化的普及率現(xiàn)在還不算高,但是理解插件化的原理對于應(yīng)用開發(fā)的技術(shù)提升有很大幫助,可以使你更好的地理解系統(tǒng)源碼,并將系統(tǒng)源碼和應(yīng)用開發(fā)相結(jié)合。
動(dòng)態(tài)加載技術(shù)
在講到插件化原理之前,需要先了解它的前身:動(dòng)態(tài)加載技術(shù)。在傳統(tǒng)Android開發(fā)中,一旦應(yīng)用程序的代碼被打包成APK并上傳到各個(gè)應(yīng)用市場,我們就不能修改App的源碼了,只能通過服務(wù)器來控制程序中預(yù)留的分支代碼邏輯。但是大多數(shù)情況下我們都無法提前預(yù)知新的需求和突發(fā)情況,也就無法提前在代碼中預(yù)留分支,這個(gè)時(shí)候就需要采用動(dòng)態(tài)加載技術(shù):在App運(yùn)行時(shí),動(dòng)態(tài)加載一些程序中原本不存在的可執(zhí)行文件并執(zhí)行這些文件中 的代碼邏輯。在Android中,可執(zhí)行文件分為兩種,一種是動(dòng)態(tài)鏈接庫so,另一種是dex相關(guān)文件(dex以及包含dex的jar/apk文件)。在之前的文章Android熱修復(fù)及原理總結(jié)中也提到了上述可執(zhí)行文件的加載,這是因?yàn)闊嵝迯?fù)技術(shù)本身也是由動(dòng)態(tài)加載技術(shù)派生出來的。
插件化的定義
插件化的App有宿主和插件兩部分組成,宿主是指先被安裝在手機(jī)上的APK,就是平常我們加載的普通APK。插件一般指的是經(jīng)過處理的APK、so和dex等文件,插件可以被宿主動(dòng)態(tài)加載并執(zhí)行,有的插件也可以作為App獨(dú)立運(yùn)行。所以,插件化的定義可以理解為:將一個(gè)應(yīng)用按照插件的方式進(jìn)行改造的過程。
Activity的插件化
四大組件的插件化是插件化技術(shù)的核心知識(shí)點(diǎn),而Activity的插件化又是四大組件中最重要的。Activity的插件化實(shí)質(zhì)上指的就是啟動(dòng)插件中的Activity,它和普通的Activity區(qū)別在于插件中的Activity,沒有在宿主App的AndroidManifest.xml文件中注冊。
Activity的插件化主要有三種實(shí)現(xiàn)方式,分別是反射實(shí)現(xiàn),接口實(shí)現(xiàn)和Hook技術(shù)實(shí)現(xiàn)。反射實(shí)現(xiàn)會(huì)對性能有所影響,主流的插件化框架沒有采用這種方式,關(guān)于接口實(shí)現(xiàn),可以閱讀Dynamic-load-apk的源碼,這里不多做介紹,目前Hook技術(shù)實(shí)現(xiàn)是主流,本文將主要介紹Hook技術(shù)實(shí)現(xiàn)的Activity插件化。
Hook技術(shù)實(shí)現(xiàn)主要有兩種解決方案,一種是通過Hook IActivityManager來實(shí)現(xiàn),另一種是Hook Intrumentation實(shí)現(xiàn)。主要也就是Hook點(diǎn)的選擇不同,要了解Hook點(diǎn)的選擇,我們需要從整體上再來回顧下Activity的啟動(dòng)過程。
普通的Actiivty(A->B)啟動(dòng)整體過程回顧
在應(yīng)用程序進(jìn)程中的ActivityA向AMS請求創(chuàng)建ActivityB,AMS會(huì)對ActivityB的生命周期和棧進(jìn)行管理,校驗(yàn)ActivityB等,如果ActivityB滿足AMS的校驗(yàn),AMS就會(huì)請求應(yīng)用程序進(jìn)程中的ActivityThread去創(chuàng)建并啟動(dòng)ActivityB。
image.png
Hook IActivityManager方案的實(shí)現(xiàn)
如上圖所示,Activity的啟動(dòng)必須經(jīng)過AMS的校驗(yàn),而這個(gè)校驗(yàn)過程的關(guān)鍵,就是要檢查Activity是否已經(jīng)在AndroidManifest.xml文件中注冊。AMS存在于SystemServer進(jìn)程中,我們無法直接修改,只能在應(yīng)用程序進(jìn)程中做文章。可以采用預(yù)先占坑的方式來解決沒有在AndroidManifest.xml文件中注冊的問題,具體做法就是再上圖的【步驟1】之前使用一個(gè)在AndroidManifest.xml中注冊的Activity來進(jìn)行占坑,用來通過AMS的校驗(yàn)。接著在【步驟2】之后用插件的Activity替換占坑的Activity。
由于篇幅有限,這里省略了插件Activity的加載邏輯,想了解的同學(xué)可以看看這篇關(guān)于ClassLoader的文章。首先直接創(chuàng)建一個(gè)TargetActivity代表已經(jīng)加載進(jìn)來的插件Activity。
public class TargetActivity extends Activity {
private final static String TAG = "TargetActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_target);
Log.e(TAG,"onCreate");
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.e(TAG,"onResume");
}
}
代碼很簡單,沒有多余的邏輯,我們這里主要是通過在生命周期函數(shù)中打印log,來驗(yàn)證TargetActivity是否啟動(dòng)。接著我們在創(chuàng)建一個(gè)用來占坑的SubActivity。代碼如下:
public class SubActivity extends AppCompatActivity {
private final static String TAG = "SubActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub);
Log.e(TAG,"onCreate");
}
@Override
protected void onResume() {
super.onResume();
Log.e(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.e(TAG,"onResume");
}
}
然后在AndroidManifest.xml中注冊SubActivity。
<application
android:name=".TestApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SubActivity"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
TargetActivity用來代表插件中的Activity,因此不需要在AndroidManifest.xml中進(jìn)行注冊。如果我們直接在MainActivity中啟動(dòng)TargetActivity,通常情況下肯定是要報(bào)錯(cuò)的。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button start_one_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start_one_btn = findViewById(R.id.start_one_btn);
start_one_btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.start_one_btn:
Intent intentOne = new Intent(this,TargetActivity.class);
startActivity(intentOne);
break;
}
}
}
接下來就是本方案的重點(diǎn)了。
使用占坑的SubActivity通過AMS的校驗(yàn)
為了防止上面提到的報(bào)錯(cuò),需要將啟動(dòng)的TargetActivity替換成SubActivity。用SubActivity來通過AMS的校驗(yàn)。這里需要指出Android 8.0與Android 7.0的AMS的相關(guān)類的代碼和結(jié)構(gòu)有一些差別,主要是Android8.0去掉了AMS的代理ActivityManagerProxy,代替它的是IActivityManager,直接采用AIDL進(jìn)行進(jìn)程間通信。Android7.0的Activity的啟動(dòng)會(huì)調(diào)用到ActivityManagerNative的getDefault方法,代碼如下:
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
...
static public IActivityManager getDefault() {
return gDefault.get();
}
...
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
...
}
getDefault方法返回了IActivityManager類型的對象,IActivityManager借助Singleton類來實(shí)現(xiàn)單例,而且Singelton類型的對象gDefault又是靜態(tài)對象,因此IActivityManager是一個(gè)比較好的Hook點(diǎn)。Android8.0的Activity的啟動(dòng)會(huì)調(diào)用到ActivityManager的getService方法,代碼如下:
public class ActivityManager {
...
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
...
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
...
}
...
}
同樣的getService方法也返回了IActivityManager類型的對象,這個(gè)IActivityManager對象也是借助Singleton類來實(shí)現(xiàn)單例,這樣就可以確定了無論是Android 8.0或Android 7.0,IActivityManager都是一個(gè)比較好的Hook點(diǎn)。同時(shí)我們還需要了解下Singleton類的源碼,便于后面的Hook實(shí)現(xiàn):
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
接下來開始定義替換IActivityManager的代理類IActivityManagerProxy,這里用到的是動(dòng)態(tài)代理機(jī)制,代碼如下:
public class IActivityManagerProxy implements InvocationHandler {
private Object iActivityManager;
public final static String TARGET_NAME = "target_activity";
public IActivityManagerProxy(Object iActivityManager){
this.iActivityManager = iActivityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("startActivity")){
Intent intent = null;
int index = 0;
for(int i = 0;i < args.length;i++){
if(args[i] instanceof Intent){
index = i;
intent = (Intent)args[i];
break;
}
}
if(intent.getComponent().getClassName().contains("TargetActivity")){
Intent proxyIntent = new Intent();
proxyIntent.putExtra(TARGET_NAME,intent);
proxyIntent.setClassName("com.zacky.activityplugintest",
"com.zacky.activityplugintest.SubActivity");
args[index] = proxyIntent;
}
}
return method.invoke(iActivityManager, args);
}
}
簡述其邏輯:攔截startActivity方法的調(diào)用,獲取參數(shù)args中的第一個(gè)Intent對象,判斷它要啟動(dòng)的組件是不是TargetActivity,如果是就創(chuàng)建一個(gè)新的Intent對象proxyIntent,用來啟動(dòng)SubActivity,并把這個(gè)啟動(dòng)TargetActivity的Intent保存到proxyIntent中,用于之后還原TargetActivity。然后將參數(shù)args中的Intent參數(shù)替換成proxyIntent。這樣啟動(dòng)的目標(biāo)就變成了SubActivity,用來通過AMS的校驗(yàn)。最后用IActivityManagerProxy來替換IActivityManager。代碼如下:
private void initHookActivityManagerAndH() throws Exception {
Object defaultSingleton;
if(Build.VERSION.SDK_INT >= 26) {
Class activityManagerClass = Class.forName("android.app.ActivityManager");
Field songletonField =
activityManagerClass.getDeclaredField("IActivityManagerSingleton");
songletonField.setAccessible(true);
defaultSingleton = songletonField.get(activityManagerClass);
}else{
Class activityManagerClass = Class.forName("android.app.ActivityManagerNative");
Field songletonField = activityManagerClass.getDeclaredField("gDefault");
songletonField.setAccessible(true);
defaultSingleton = songletonField.get(activityManagerClass);
}
Class singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object iActivityManager = mInstanceField.get(defaultSingleton);
Object iActivityManagerProxy = Proxy.newProxyInstance(getClassLoader(),
new Class[]{Class.forName("android.app.IActivityManager")},
new IActivityManagerProxy(iActivityManager));
mInstanceField.set(defaultSingleton,iActivityManagerProxy);
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField =
activityThreadClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadField.get(activityThreadClass);
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Object mH = mHField.get(sCurrentActivityThread);
Class handlerCLass = Class.forName("android.os.Handler");
Field mCallbackField = handlerCLass.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mH,new PluginHandlerCallback((Handler) mH));
}
主要也就是用到反射,這個(gè)Hook的方法其實(shí)可以在啟動(dòng)TargetActivity之前的任何時(shí)機(jī)調(diào)用都行。我這里是在自定義Application的onCreate方法中調(diào)用的,代碼如下:
public class TestApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
try {
initHookActivityManagerAndH();
} catch (Exception e) {
e.printStackTrace();
}
}
}
還原插件Activity
細(xì)心的同學(xué)肯定發(fā)現(xiàn)了initHookActivityManagerAndH方法中,還通過反射getActivityThread類中的mH對象設(shè)置了mCallback。這個(gè)就和還原TargetActivity有關(guān)。后面會(huì)講到。
上面已經(jīng)演示了用占坑的SubActivity替換TargetActivity,通過了AMS的校驗(yàn),但我們的真實(shí)目的是啟動(dòng)代表插件的TargetActivity,所以還需要用TargetActivity來替換占坑的SubActivity。替換的時(shí)機(jī)就是上面的Activity啟動(dòng)過程演示圖中的【步驟2】之后。簡單來說就是,通過AMS的校驗(yàn)后,AMS就會(huì)請求應(yīng)用程序進(jìn)程中的ActivityThread去創(chuàng)建并啟動(dòng)目標(biāo)Activity。ActivityThread會(huì)通過H類將代碼邏輯的處理切換到主線程中,H類是ActivityThread的內(nèi)部類并繼承自Handler。了解Activity啟動(dòng)過程的朋友對他肯定不陌生。但是由于Android 9.0的源碼,將ActivityThread類中的相關(guān)代碼邏輯做了一些改動(dòng),這里有必要簡單的介紹下:
Android 9.0引入了ClientTransaction、ClientLifecycleManager和ClientLifecycleHandler來輔助管理Activity的生命周期。以Activity的啟動(dòng)過程為例,在ActivityStackSupervisor.realStartActivityLocked方法中為ClientTransaction對象添加LaunchActivityItem的callback,然后設(shè)置當(dāng)前的生命周期狀態(tài),再調(diào)用ClientLifecycleManager.scheduleTransaction方法執(zhí)行。這個(gè)方法的內(nèi)部會(huì)調(diào)用ClientTransaction的transaction方法,transaction方法又會(huì)去調(diào)用IApplicationThread的scheduleTransaction方法,轉(zhuǎn)而調(diào)用ActivityThread的scheduleTransaction方法,這個(gè)方法繼承自ClientLifecycleHandler。在ClientLifecycleHandler的scheduleTransaction方法中,會(huì)將transaction通過Message傳遞給mH來處理,最終由TransactionExecutor來執(zhí)行LaunchActivityItem的execute方法,execute方法里面的代碼,就是Android9.0之前在mH的handleMessage方法中處理LAUNCH_ACTIVITY的相關(guān)代碼,創(chuàng)建ActivityClientRecord對象,調(diào)用handleLaunchActivity方法,創(chuàng)建和啟動(dòng)Activity。對相關(guān)細(xì)節(jié)感興趣的同學(xué),可以參考(Android 9.0)Activity啟動(dòng)流程源碼分析
ActivityThread通過mH將代碼邏輯的處理切換到主線程中執(zhí)行,一個(gè)應(yīng)用程序進(jìn)程中,只有一個(gè)ActivityThread對象sCurrentActivityThread,代表主線程。每個(gè)ActivityThread對象包含一個(gè)final修飾的mH對象,也就是說每個(gè)應(yīng)用程序進(jìn)程中也就只有唯一的mH對象,而Activity的生命周期管理肯定會(huì)經(jīng)過mH的handleMessage方法來轉(zhuǎn)發(fā)處理。所以mH可以還原TargetActivity的Hook點(diǎn)。H繼承自Handler。了解Handler機(jī)制的同學(xué),肯定都知道,Handler的dispatchMessage方法分發(fā)Message時(shí),會(huì)先判斷mCallback是否為null,如果不為null,就會(huì)執(zhí)行mCallback的handleMessage方法,否則執(zhí)行handler自身的handleMessage方法。代碼如下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
那么Hook mH的思路就出來了,我們可以用自定義的Callback來替換mH的mCallback,Callback定義如下
public class PluginHandlerCallback implements Handler.Callback {
public static int EXECUTE_TRANSACTION = 159;
Handler handler;
public PluginHandlerCallback(Handler handler){
this.handler = handler;
}
@Override
public boolean handleMessage(@NonNull Message msg) {
try {
if(EXECUTE_TRANSACTION == msg.what){
Intent intent;
Class activityClientRecordClass =
Class.forName("android.app.servertransaction.ClientTransaction");
Method getCallbacksMethod = activityClientRecordClass.getDeclaredMethod("getCallbacks");
getCallbacksMethod.setAccessible(true);
List mActivityCallbacks = (List) getCallbacksMethod.invoke(msg.obj,null);
if(mActivityCallbacks != null){
for (Object item : mActivityCallbacks){
Class launchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
if(item.getClass() == launchActivityItemClass){
Field mIntentField = launchActivityItemClass.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
intent = (Intent)mIntentField.get(item);
if(intent.getComponent().getClassName().contains("SubActivity")){
if(intent!=null && intent.hasExtra(TARGET_NAME)
&& intent.getParcelableExtra(TARGET_NAME) instanceof Intent){
Intent targetIntent = intent.getParcelableExtra(TARGET_NAME);
mIntentField.set(item,targetIntent);
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
handler.handleMessage(msg);
return true;
}
}
在PluginHandlerCallback的構(gòu)造方法中傳入一個(gè)Handler對象,實(shí)際使用時(shí)就是mH。在定義handleMessage方法的邏輯之前我們需要了解下ClientTransaction類的結(jié)構(gòu),因?yàn)閺纳厦嫣岬降腁ndroid9.0的Activity啟動(dòng)過程中,有mH處理的Message對象的obj屬性是一個(gè)ClientTransaction對象。ClientTransaction代碼如下:
public class ClientTransaction implements Parcelable, ObjectPoolItem {
/** A list of individual callbacks to a client. */
private List<ClientTransactionItem> mActivityCallbacks;
...
/** Get the list of callbacks. */
@Nullable
List<ClientTransactionItem> getCallbacks() {
return mActivityCallbacks;
}
}
而最終調(diào)用handleLaunchActivity方法的LaunchActivityItem對象,就存放在mActivityCallbacks列表中。
public class LaunchActivityItem extends ClientTransactionItem {
...
private Intent mIntent;
...
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
由此我們就可以理清handleMessage方法中的代碼邏輯了。我們首先要根據(jù)msg.what來找到和Activity的生命周期管理相關(guān)的Message,通過反射找到LaunchActivityItem對象和它的mIntent變量,這個(gè)mIntent對象就是最終創(chuàng)建和啟動(dòng)Activity的Intent,我們需要做的就是用最初啟動(dòng)TargetActivity的Intent來替換這個(gè)mIntent。在handleMessage方法的最后,調(diào)用handler.handleMessage(msg)來處理mH的原有邏輯。
至此,PluginHandlerCallback的定義就對應(yīng)了initHookActivityManagerAndH方法中,通過反射來Hook mH和mCallback的那一段代碼。
代碼講解完畢,運(yùn)行程序,如點(diǎn)擊按鈕啟動(dòng)TargetActivity,查看Logcat可以看到程序不但沒有報(bào)錯(cuò),TargetActivity也成功創(chuàng)建并啟動(dòng)。
Hook Intrumentation方案的實(shí)現(xiàn)
上文中提到了Activity的插件化,除了Hook IActivityManager方案之外,還可以通過Hook Intrumentation來實(shí)現(xiàn)。首先還是回顧下Activity啟動(dòng)過程,Intrumentation的作用:
- 在Activity類的startActivityForResult方法中會(huì)調(diào)用Instrumentation的execStartActivity方法,在這個(gè)方法里面就會(huì)AMS的startActivity方法,可以說,mInstrumentation的execStartActivity方法是Activity啟動(dòng)流程從應(yīng)用程序進(jìn)程轉(zhuǎn)到AMS所在的SystemServer進(jìn)程的過渡。
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
...
}
- 在ActivityThread的performLaunchActivity方法中會(huì)調(diào)用Instrumentation的newActivity方法來創(chuàng)建Activity的實(shí)例。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
}
可以看到,剛好一個(gè)是在AMS的校驗(yàn)之前,一個(gè)是在通過AMS校驗(yàn)之后。可以作為合適的Hook點(diǎn)。Hook Intrumentation方案比Hook IActivityManager方案實(shí)現(xiàn)要簡單一些,由于Android不同版本的源碼對IActivityManager的實(shí)現(xiàn)會(huì)有變化,相比之下,Intrumentation在Activity的啟動(dòng)過程中的作用和方法的調(diào)用時(shí)機(jī)并沒有什么變化。并且現(xiàn)在人氣很高的插件化開源框架VirtualAPK,也是用Hook Intrumentation方案來實(shí)現(xiàn)Activity的插件化。
Hook Intrumentation方案的實(shí)現(xiàn)同樣需要用到占坑的Activity,所以這里和Hook IActivityManager方案的演示代碼有重復(fù)的地方,不多贅述。
首先,我們自定義一個(gè)Instrumentation,代碼如下:
public class ProxyInstrumentation extends Instrumentation {
public final static String TARGET_NAME = "target_activity";
private Instrumentation instrumentation;
private PackageManager mPackageManager;
public ProxyInstrumentation(Instrumentation instrumentation,PackageManager mPackageManager){
this.instrumentation = instrumentation;
this.mPackageManager = mPackageManager;
}
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options){
List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent,PackageManager.MATCH_ALL);
if(infos==null || infos.size()==0){
Intent proxyIntent = new Intent();
proxyIntent.putExtra(TARGET_NAME,intent);
proxyIntent.setClassName("com.zacky.activityplugintest",
"com.zacky.activityplugintest.SubActivity");
try {
Method execStartActivityMethod =
Instrumentation.class.getDeclaredMethod("execStartActivity",
Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class,Bundle.class);
execStartActivityMethod.setAccessible(true);
ActivityResult activityResult = (ActivityResult)execStartActivityMethod.invoke(instrumentation,
who,contextThread,token,target,proxyIntent,requestCode,options);
return activityResult;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
if(intent.hasExtra(TARGET_NAME) && intent.getParcelableExtra(TARGET_NAME) instanceof Intent){
Intent targetIntent = intent.getParcelableExtra(TARGET_NAME);
return super.newActivity(cl, targetIntent.getComponent().getClassName(), targetIntent);
}else{
return super.newActivity(cl, className, intent);
}
}
}
因?yàn)镮nstrumentation類不是接口,不能用動(dòng)態(tài)代理實(shí)現(xiàn),我們用自定義的ProxyInstrumentation繼承自Instrumentation。
重寫execStartActivity方法,判斷待啟動(dòng)的Activity是否已經(jīng)在AndroidManifest.xml中注冊。如果沒有,就創(chuàng)建一個(gè)啟動(dòng)SubActivity的Intent對象proxyIntent,用來替換原來的intent。同時(shí)將intent保存在proxyIntent中,用于之后還原TargetActivity。然后通過反射調(diào)用構(gòu)造方法中傳進(jìn)來的instrumentation對象的execStartActivity方法,這樣就實(shí)現(xiàn)了用SubActivity來通過AMS的驗(yàn)證。
重寫newActivity方法,實(shí)現(xiàn)TargetActivity的還原。判斷也很簡單。如果傳進(jìn)來的參數(shù)intent里包含了啟動(dòng)TargetActivity的Intent,調(diào)用父類Instrumentation的newActivity方法時(shí),就傳入TargetActivity的類名。這樣發(fā)就完成了TargetActivity的還原。
然后,編寫initHookIntrumentation方法,用自定義的ProxyInstrumentation,替換代表當(dāng)前應(yīng)用程序主線程的ActivtyThread類型的對象sCurrentActivityThread的mInstrumentation。代碼如下:
private void initHookIntrumentation() throws Exception{
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField =
activityThreadClass.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
Object sCurrentActivityThread = sCurrentActivityThreadField.get(activityThreadClass);
Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(sCurrentActivityThread);
ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(mInstrumentation,getPackageManager());
mInstrumentationField.set(sCurrentActivityThread,proxyInstrumentation);
}
筆者這里同樣是在自定義Application的onCreate方法中調(diào)用initHookIntrumentation方法。然后運(yùn)行程序,如點(diǎn)擊按鈕啟動(dòng)TargetActivity,查看Logcat可以看到程序不但沒有報(bào)錯(cuò),TargetActivity也成功創(chuàng)建并啟動(dòng)。
總結(jié)
實(shí)現(xiàn)Activity的插件化,實(shí)際上就是啟動(dòng)插件中未在AndroidManifest文件中注冊的Activity。主要的方案就是先用一個(gè)已在AndroidManifest.xml中注冊的Activity來占坑,用來通過AMS的校驗(yàn),接著在合適的時(shí)機(jī)用插件Activity替換占坑的Activity。
本文參考:
《Android進(jìn)階解密》
(Android 9.0)Activity啟動(dòng)流程源碼分析