android插件化在9.0上插件activity的theme失效問題(VirtualAPK)

android插件化在9.0上插件activity的theme失效問題(VirtualApk)
在使用VirtualApk的時(shí)候,發(fā)現(xiàn)在android 9.0上揽碘,插件中的Activity配置的theme失效

這個(gè)問題和Android系統(tǒng)代碼修改有關(guān)念恍,我們看下9.0前后設(shè)置theme的變化在哪里。

看到ActivityThread中

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
//...省略代碼
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

//...省略代碼
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);
//...省略代碼
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

}

performLaunchActivity中進(jìn)行了Activity的創(chuàng)建和theme的設(shè)置。而這個(gè)theme是從參數(shù)ActivityClientRecord r獲取翠订,看到performLaunchActivity被調(diào)用的地方

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...省略代碼
    Activity a = performLaunchActivity(r, customIntent);
}

繼續(xù)找到

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        } break;

在ActivityThread的Handler中調(diào)用的handleLaunchActivity。正是如此鼓鲁,給了我們hook修改的機(jī)會(huì)蕴轨,看到VirtualApk的處理VAInstrumentation

@Override
public boolean handleMessage(Message msg) {
    if (msg.what == LAUNCH_ACTIVITY) {
        // ActivityClientRecord r
        Object r = msg.obj;
        try {
            Reflector reflector = Reflector.with(r);
            Intent intent = reflector.field("intent").get();
            intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
            ActivityInfo activityInfo = reflector.field("activityInfo").get();

            if (PluginUtil.isIntentFromPlugin(intent)) {
                int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                if (theme != 0) {
                    Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                    activityInfo.theme = theme;
                }
            }
        } catch (Exception e) {
            Log.w(TAG, e);
        }

Virtual 給ActivityThread中的Handler增加了自己的callback,也就是在系統(tǒng)處理LAUNCH_ACTIVITY消息時(shí)骇吭,virtualApk會(huì)先處理橙弱,獲取到對(duì)應(yīng)的ActivityClientRecord,然后修改activityInfo中的theme為插件的theme燥狰。

那為什么在9.0后就不行了呢棘脐,我們看下9.0這部分的源碼
呵,好家伙龙致,根本就沒有LAUNCH_ACTIVITY這個(gè)定義了蛀缝,所以hook失效,根本就沒有設(shè)置插件的theme
那系統(tǒng)是怎么調(diào)用的handleLaunchActivity呢目代?
我們找到LaunchActivityItem

public class LaunchActivityItem extends ClientTransactionItem {

    private Intent mIntent;
    private int mIdent;
    private ActivityInfo mInfo;
    private Configuration mCurConfig;
    private Configuration mOverrideConfig;
    private CompatibilityInfo mCompatInfo;
    private String mReferrer;
    private IVoiceInteractor mVoiceInteractor;
    private int mProcState;
    private Bundle mState;
    private PersistableBundle mPersistentState;
    private List<ResultInfo> mPendingResults;
    private List<ReferrerIntent> mPendingNewIntents;
    private boolean mIsForward;
    private ProfilerInfo mProfilerInfo;

    @Override
    public void preExecute(ClientTransactionHandler client, IBinder token) {
        client.updateProcessState(mProcState, false);
        client.updatePendingConfiguration(mCurConfig);
    }

    @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);
    }


這個(gè)execute又是在哪執(zhí)行的呢屈梁?找到ActivityStackSupervisor

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            // TODO: Have this take the merged configuration instead of separate global
            // and override configs.
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
            r.persistentState, results, newIntents, mService.isNextTransitionForward(),
            profilerInfo));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);

我們找到LaunchActivityItem創(chuàng)建的地方,而mService.getLifecycleManager().scheduleTransaction(clientTransaction);是如何執(zhí)行的呢

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }
    }

其實(shí)就是執(zhí)行了 transaction的client的schedule()方法榛了。繼續(xù)看到

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

這里個(gè)client是什么在讶,會(huì)到創(chuàng)建的地方看到

    final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
            r.appToken);

咱們的IApplicationThread嘛∷螅看到他的scheduleTransaction

        @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }

繼續(xù)看到 ActivityThread

    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

這下是不是很清晰了构哺,就是把 ClientTransaction 通過Handler發(fā)送給ActivityThread來處理

    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        if (isSystem()) {
            // Client transactions inside system process are recycled on the client side
            // instead of ClientLifecycleManager to avoid being cleared before this
            // message is handled.
            transaction.recycle();
        }
        // TODO(lifecycler): Recycle locally scheduled transactions.
        break;

看到如何execute

    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
        log("End resolving transaction");
    }

看到executeCallbacks

    @VisibleForTesting
    public void executeCallbacks(ClientTransaction transaction) {
    //...省略代碼
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            item.execute(mTransactionHandler, token, mPendingActions);
    //...省略代碼
        }
    }

這下清楚了把,把添加的callbacks execute下战坤。我們?cè)诨仡櫹略O(shè)置的地方

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
            clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
            System.identityHashCode(r), r.info,
            // TODO: Have this take the merged configuration instead of separate global
            // and override configs.
            mergedConfiguration.getGlobalConfiguration(),
            mergedConfiguration.getOverrideConfiguration(), r.compat,
            r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
            r.persistentState, results, newIntents, mService.isNextTransitionForward(),
            profilerInfo));

    // Set desired final state.
    final ActivityLifecycleItem lifecycleItem;
    if (andResume) {
        lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
    } else {
        lifecycleItem = PauseActivityItem.obtain();
    }
    clientTransaction.setLifecycleStateRequest(lifecycleItem);

    // Schedule transaction.
    mService.getLifecycleManager().scheduleTransaction(clientTransaction);

設(shè)置了LaunchActivityItem曙强。而execute執(zhí)行的就是handleLaunchActivity

    @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);
    }

到這里整個(gè)流程就穿起來了。 所以知道為什么VirtualApk在9.0上為什么設(shè)置的theme沒有效果途茫,因?yàn)橄到y(tǒng)啟動(dòng)的調(diào)用方式已經(jīng)發(fā)生了改變碟嘴。

那現(xiàn)在我們?nèi)绾稳バ薷哪亍M砜吹紿andler處理的地方

    case EXECUTE_TRANSACTION:
        final ClientTransaction transaction = (ClientTransaction) msg.obj;
        mTransactionExecutor.execute(transaction);
        if (isSystem()) {
            // Client transactions inside system process are recycled on the client side
            // instead of ClientLifecycleManager to avoid being cleared before this
            // message is handled.
            transaction.recycle();
        }
        // TODO(lifecycler): Recycle locally scheduled transactions.
        break;

我們可以獲取到 ClientTransaction囊卜。 然后再反射獲取到 mActivityCallbacks臀防。判斷如果是LaunchActivityItem眠菇。 繼續(xù)反射獲取到ActivityInfo。這里附上完整的修改代碼

    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            // ActivityClientRecord r
            Object r = msg.obj;
            try {
                Reflector reflector = Reflector.with(r);
                Intent intent = reflector.field("intent").get();
                intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
                ActivityInfo activityInfo = reflector.field("activityInfo").get();

                if (PluginUtil.isIntentFromPlugin(intent)) {
                    int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                    if (theme != 0) {
                        Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                        activityInfo.theme = theme;
                    }
                }
            } catch (Exception e) {
                Log.w(TAG, e);
            }
        } else if (msg.what == 159) {
            //r實(shí)際為clienttransaction
            Object r = msg.obj;
            try {
                Class clientClazz = r.getClass();
                Field fCallbacks = clientClazz.getDeclaredField("mActivityCallbacks");
                fCallbacks.setAccessible(true);
                //得到transactionz中的callbacks,為一個(gè)list,獲取其中元素為L(zhǎng)aunchActivityItem
                List<?> lists = (List) fCallbacks.get(r);
                for (int i = 0; i < lists.size(); i++) {
                    Object item = lists.get(i);
                    Class itemClazz = item.getClass();
                    Log.w(TAG, "class--->" + itemClazz.getName());
                    if (!(itemClazz.getSimpleName().equals("LaunchActivityItem"))) {
                        return false;
                    }
                    //獲取成員 mIntent
                    Log.w(TAG, "=======get " + itemClazz.getName());
                    Field mIntent = itemClazz.getDeclaredField("mIntent");
                    mIntent.setAccessible(true);
                    Intent intent = (Intent) mIntent.get(item);
                    Log.w(TAG, "=======get intent " + intent);

                    //ActivityInfo mInfo
                    Field activityInfoField = itemClazz.getDeclaredField("mInfo");
                    activityInfoField.setAccessible(true);
                    ActivityInfo activityInfo = (ActivityInfo) activityInfoField.get(item);
                    Log.w(TAG, "=======get ActivityInfo " + activityInfoField);
                    intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
                    if (PluginUtil.isIntentFromPlugin(intent)) {
                        //獲取插件主題
                        int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                        if (theme != 0) {
                            Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                            activityInfo.theme = theme;
                        }
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袱衷,一起剝皮案震驚了整個(gè)濱河市捎废,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌致燥,老刑警劉巖登疗,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嫌蚤,居然都是意外死亡辐益,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門脱吱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智政,“玉大人,你說我怎么就攤上這事箱蝠⌒妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵宦搬,是天一觀的道長(zhǎng)牙瓢。 經(jīng)常有香客問我,道長(zhǎng)间校,這世上最難降的妖魔是什么矾克? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮憔足,結(jié)果婚禮上胁附,老公的妹妹穿的比我還像新娘。我一直安慰自己滓彰,他們只是感情好汉嗽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著找蜜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稳析。 梳的紋絲不亂的頭發(fā)上洗做,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音彰居,去河邊找鬼诚纸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛陈惰,可吹牛的內(nèi)容都是我干的畦徘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼井辆!你這毒婦竟也來了关筒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤杯缺,失蹤者是張志新(化名)和其女友劉穎蒸播,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萍肆,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袍榆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了塘揣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片包雀。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亲铡,靈堂內(nèi)的尸體忽然破棺而出才写,到底是詐尸還是另有隱情,我是刑警寧澤奴愉,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布琅摩,位于F島的核電站,受9級(jí)特大地震影響锭硼,放射性物質(zhì)發(fā)生泄漏房资。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一檀头、第九天 我趴在偏房一處隱蔽的房頂上張望轰异。 院中可真熱鬧,春花似錦暑始、人聲如沸搭独。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牙肝。三九已至,卻和暖如春嗤朴,著一層夾襖步出監(jiān)牢的瞬間配椭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工雹姊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留股缸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓吱雏,卻偏偏與公主長(zhǎng)得像敦姻,于是被迫代替她去往敵國(guó)和親瘾境。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容