Android插件化的兼容性(中):Android P的適配

     Android系統的每次版本升級,都會對原有代碼進行重構,這就爲插件化帶來了麻煩。java

     Android P對插件化的影響,主要體如今兩方面,一是它重構了H類中Activity相關的邏輯,另外一個是它重構了Instrumentation。android

 

     3.1 H類的變身

     3.1.1 從Message和Handler提及面試

     對於App開發人員而言,Message和Handler是耳熟能詳的兩個概念。咱們簡單回顧一下,一條消息是怎麼發送和接收的。設計模式

     首先,在App啓動的時候,會建立ActivityThread,這就是主線程,也叫UI線程。App的入口——main函數,就藏在ActivityThread中,架構

     在main函數中,會建立MainLooper。MainLooper是一個死循環,專門負責接收消息,也就是Message類。app

     Message類的定義以下,除了耳熟能詳的what和obj屬性外,還有一個不對App開放的變量target,它是一個Handler:ide

public final class Message implements Parcelable {
    public int what;
    public Object obj;
     Handler target;

    //如下省略不少代碼哦
}

 

     在App進程中,Application和四大組件的每一個生命週期函數,都要和AMS進程進行跨進程通訊。函數

     1)App進程把數據傳給AMS進程,是經過ActivityManagerNative完成的。oop

     2)AMS進程把數據傳給App進程,App進程這邊接收數據的是ApplicationThread。測試

 

     ApplicationThread在接收到數據後,會調用sendMessage方法。這就把消息Message對象發送給了MainLooper這個死循環。

     在MainLooper死循環中,處理這個Message對象。怎麼處理呢,取出它的target字段,這是一個Handler類型的對象,調用這個Handler對象的dispatchMessage方法。

     是時候看一下Handler類的結構了,我簡化了它的代碼,爲的是易於理解:

public class Handler {
    final Callback mCallback;

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    public void handleMessage(Message msg) {
    }
    
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
}

 

     Handler類中有一個mCallback變量,這個變量是插件化技術的核心。書接上文,MainLooper調用了Handler的dispatchMessage方法,這個方法的邏輯是,要麼執行mCallback的handleMessage方法,要麼執行Handler類本身的handleMessage方法。

     Handler類本身的handleMessage方法,是一個空方法,因此咱們通常寫一個Handler的子類,而後實現這個handleMessage方法。

     在Android系統底層,這個Handler類的子類,就是H類,咱們在ActivityThread.java中能夠找到這個類。H類的handleMessage方法中,定義了全部消息的分發,以下所示:

public final class ActivityThread {
    private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        public static final int PAUSE_ACTIVITY           = 101;
        public static final int PAUSE_ACTIVITY_FINISHING= 102;
        public static final int STOP_ACTIVITY_SHOW      = 103;
        public static final int STOP_ACTIVITY_HIDE      = 104;
        public static final int SHOW_WINDOW             = 105;
        public static final int HIDE_WINDOW             = 106;
        public static final int RESUME_ACTIVITY         = 107;
        public static final int SEND_RESULT             = 108;
        public static final int DESTROY_ACTIVITY        = 109;
        public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111;
        public static final int NEW_INTENT              = 112;
        public static final int RECEIVER                = 113;

        //如下省略不少代碼

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                } break;
            
            //如下省略不少代碼
            }
        }
    }
}

 

     在H類的handleMessage方法中,會根據msg參數的what值,來判斷究竟是哪一種消息,以及相應的執行什麼邏輯,好比說,啓動Activity。

     在H類中,定義了幾十種消息,好比說LAUNCH_ACTIVITY的值是100,PAUSE_ACTIVITY的值是101。從100到109,都是給Activity的生命週期函數準備的。從110開始,纔是給Application、Service、ContentProvider、BroadcastReceiver使用的。

     至此,咱們簡單回顧了Android系統內部Message的發送和接收流程。其中比較重要的是:

     1)Handler類中有一個mCallback變量。

     2)H類中定義了各類消息。

 

     3.1.2 Android P以前的插件化解決方案

     在Android P(API level 28)以前,咱們作插件化,都是Hook掉H類的mCallback對象,攔截這個對象的handleMessage方法。在此以前,咱們把插件中的Activity替換爲StubActtivty,那麼如今,咱們攔截到handleMessage方法,再把StubActivity換回爲插件中的Activity,代碼以下所示:

class MockClass2 implements Handler.Callback {

    Handler mBase;

    public MockClass2(Handler base) {
        mBase = base;
    }

    @Override
    public boolean handleMessage(Message msg) {

        switch (msg.what) {
            // ActivityThread裏面 "LAUNCH_ACTIVITY" 這個字段的值是100
            // 原本使用反射的方式獲取最好, 這裏爲了簡便直接使用硬編碼
            case 100:
                handleLaunchActivity(msg);
                break;
        }

        mBase.handleMessage(msg);
        return true;
    }

    private void handleLaunchActivity(Message msg) {
        // 這裏簡單起見,直接取出TargetActivity;

        Object obj = msg.obj;

        // 把替身恢復成真身
        Intent raw = (Intent) RefInvoke.getFieldObject(obj, "intent");

        Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
        raw.setComponent(target.getComponent());
    }
}

 

     3.1.3 Android P對Activity消息機制的改造

     Android系統升級到P,它重構了H類,把100到109這10個用於Activity的消息,都合併爲159這個消息,消息名爲EXECUTE_TRANSACTION。

     爲何要這麼修改呢?相信你們面試Android工程師崗位的時候,都會被問及Activity的生命週期圖。這實際上是一個由Create、Pause、Resume、Stop、Destory、Restart組成的狀態機。按照設計模式中狀態模式的定義,能夠把每一個狀態都定義成一個類,因而便有了以下的類圖:

 

 

     就拿LaunchActivity來講吧,在Android P以前,是在H類的handleMessage方法的switch分支語句中,編寫啓動一個Activity的邏輯:

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                } break;
            
            //如下省略不少代碼
            }
        }

 

     在Android P中,啓動Activity的這部分邏輯,被轉移到了LaunchActivityItem類的execute方法中。

public class LaunchActivityItem extends ClientTransactionItem {

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        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 */);
    }
}

 

     從架構的角度來講,此次重構的效果很好。使用狀態模式,使得Android這部分代碼,就是OOP的了。咱們把寫在H類的handleMessage方法中的switch分支語句,拆分到不少類中,這就符合了五大設計原則中的開閉原則,寧可有100個類,每一個類有100行代碼,也不要有一個一萬行代碼的類。好處是,當我想改動Resume這個狀態的業務邏輯時,我只要在ResumeActivityItem類中修改並進行測試就能夠了,影響的範圍很小。

     可是此次重構也有缺點,OOP的缺點就是代碼會讓人看不懂,由於只有在運行時才知道到底實例化的是哪一個類,這讓本來清晰的Android Activity消息邏輯變得支離破碎。

     按照這個趨勢,四大組件之一的Service,它在H類中也有不少消息,也是有不少生命週期函數,Android的下個版本,極有可能把Service也重構爲狀態模式。

 

     3.1.4 Android P針對於H的Hook

     Android P把H類中的100-109這10個消息都刪除了,取而代之的是159這個消息,名爲EXECUTE_TRANSACTION。

     這就致使咱們以前的插件化解決方案,在Android P上是不生效的,會由於找不到100這個消息,而不能把StubActiivty換回爲插件中的Activity。爲此,咱們須要攔截159這個消息。攔截後,咱們又要面對如何判斷當前這個消息究竟是Launch,仍是Pause或者Resume。

     關鍵在於H類的handleMessage方法的Message參數。這個Message的obj字段,在Message是159的時候,返回的是ClientTransacion類型對象,它內部有一個mActivityCallbacks集合:

public class ClientTransaction implements Parcelable, ObjectPoolItem {

      private List<ClientTransactionItem> mActivityCallbacks;

}

 

     這個mActivityCallbacks集合中,存放的是ClientTransactionItem的各類子類對象,好比LaunchActivityItem、DestoryActivityListItem。咱們能夠判斷這個集合中的值,發現有某個元素是LaunchActivityItem類型的,那麼就至關於捕獲到了啓動Activity的那個消息。

     定位到LaunchActivityItem類的對象,它內部有一個mIntent字段,裏面存放的就是要啓動的Activity名稱,目前值是StubActivity。在這裏把它替換爲真正要啓動的插件Activity,代碼以下所示:

class MockClass2 implements Handler.Callback {

    Handler mBase;

    public MockClass2(Handler base) {
        mBase = base;
    }

    @Override
    public boolean handleMessage(Message msg) {

        switch (msg.what) {
            // ActivityThread裏面 "LAUNCH_ACTIVITY" 這個字段的值是100
            // 原本使用反射的方式獲取最好, 這裏爲了簡便直接使用硬編碼
            case 100:   //for API 28如下
                handleLaunchActivity(msg);
                break;
            case 159:   //for API 28
                handleActivity(msg);
                break;
        }

        mBase.handleMessage(msg);
        return true;
    }

    private void handleActivity(Message msg) {
        // 這裏簡單起見,直接取出TargetActivity;
        Object obj = msg.obj;

        List<Object> mActivityCallbacks = (List<Object>) RefInvoke.getFieldObject(obj, "mActivityCallbacks");
        if(mActivityCallbacks.size() > 0) {
            String className = "android.app.servertransaction.LaunchActivityItem";
            if(mActivityCallbacks.get(0).getClass().getCanonicalName().equals(className)) {
                Object object = mActivityCallbacks.get(0);
                Intent intent = (Intent) RefInvoke.getFieldObject(object, "mIntent");
                Intent target = intent.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
                intent.setComponent(target.getComponent());
            }
        }
    }
}

 

3.2 Instrumentation的變身

     在Android P以前,Instrumentation的newActivity方法。邏輯以下:

    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
}

 

     到了Android P,則改寫了Instrumentation類的部分邏輯。它會在newActivity方法中,檢查Instrumentation的mThread變量,若是爲空,就會拋出一個異常:

public class Instrumentation {
    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        String pkg = intent != null && intent.getComponent() != null
                ? intent.getComponent().getPackageName() : null;
        return getFactory(pkg).instantiateActivity(cl, className, intent);
    }

    private AppComponentFactory getFactory(String pkg) {
        if (pkg == null) {
            Log.e(TAG, "No pkg specified, disabling AppComponentFactory");
            return AppComponentFactory.DEFAULT;
        }
        if (mThread == null) {
            Log.e(TAG, "Uninitialized ActivityThread, likely app-created Instrumentation,"
                    + " disabling AppComponentFactory", new Throwable());
            return AppComponentFactory.DEFAULT;
        }
        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
        // This is in the case of starting up "android".
        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
        return apk.getAppFactory();
    }
}

 

     咱們在本書第5章介紹給一種Hook方案,攔截Instrumentation類的execStartActivity方法,以下所示:

public class HookHelper {

    public static void attachContext() throws Exception{
        // 先獲取到當前的ActivityThread對象
        Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread");

        // 拿到原始的 mInstrumentation字段
        Instrumentation mInstrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread, "mInstrumentation");

        // 建立代理對象
        Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);

        // 偷樑換柱
        RefInvoke.setFieldObject(currentActivityThread, "mInstrumentation", evilInstrumentation);
    }
}

public class EvilInstrumentation extends Instrumentation {

    private static final String TAG = "EvilInstrumentation";

    // ActivityThread中原始的對象, 保存起來
    Instrumentation mBase;

    public EvilInstrumentation(Instrumentation base) {
        mBase = base;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Log.d(TAG, "XXX到此一遊!");

        // 開始調用原始的方法, 調不調用隨你,可是不調用的話, 全部的startActivity都失效了.
        // 因爲這個方法是隱藏的,所以須要使用反射調用;首先找到這個方法
        Class[] p1 = {Context.class, IBinder.class,
                IBinder.class, Activity.class,
                Intent.class, int.class, Bundle.class};
        Object[] v1 = {who, contextThread, token, target,
                intent, requestCode, options};

        return (ActivityResult) RefInvoke.invokeInstanceMethod(
                mBase, "execStartActivity", p1, v1);
    }
}

 

     這段代碼,咱們把系統原先的Instrumentation替換成EvilInstrumentation,在Android P如下的系統是能夠運行的,可是在Android P上就會拋出Uninitialized ActivityThread, likely app-created Instrumentation的異常,顯然這是由於EvilInstrumentation的mThread爲空致使的。

     想要解決這個問題,就必須重寫EvilInstrumentation中的newActivity方法,以下所示:

public class EvilInstrumentation extends Instrumentation {
    //省略了部分代碼

    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {

        return mBase.newActivity(cl, className, intent);
    }
}

 

     這樣編碼,即便是EvilInstrumentation,在執行newActivity方法的時候,也會執行原先Instrumentation的newActivity方法,Instrumentation的mThread字段不是null,因此就不會拋出上述的異常信息了。

相關文章
相關標籤/搜索