前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android插件化在9.0上插件activity的theme失效问题(VirtualAPK)

android插件化在9.0上插件activity的theme失效问题(VirtualAPK)

作者头像
用户2929716
发布2021-03-23 10:57:07
6890
发布2021-03-23 10:57:07
举报
文章被收录于专栏:流媒体流媒体

android插件化在9.0上插件activity的theme失效问题(VirtualApk) 在使用VirtualApk的时候,发现在android 9.0上,插件中的Activity配置的theme失效

这个问题和Android系统代码修改有关,我们看下9.0前后设置theme的变化在哪里。

看到ActivityThread中

代码语言:javascript
复制
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中进行了Activity的创建和theme的设置。而这个theme是从参数ActivityClientRecord r获取,看到performLaunchActivity被调用的地方

代码语言:javascript
复制
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...省略代码
    Activity a = performLaunchActivity(r, customIntent);
}

继续找到

代码语言:javascript
复制
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中调用的handleLaunchActivity。正是如此,给了我们hook修改的机会,看到VirtualApk的处理VAInstrumentation

代码语言:javascript
复制
@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,也就是在系统处理LAUNCH_ACTIVITY消息时,virtualApk会先处理,获取到对应的ActivityClientRecord,然后修改activityInfo中的theme为插件的theme。

那为什么在9.0后就不行了呢,我们看下9.0这部分的源码 呵,好家伙,根本就没有LAUNCH_ACTIVITY这个定义了,所以hook失效,根本就没有设置插件的theme 那系统是怎么调用的handleLaunchActivity呢? 我们找到LaunchActivityItem

代码语言:javascript
复制
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);
    }

这个execute又是在哪执行的呢?找到ActivityStackSupervisor

代码语言:javascript
复制
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创建的地方,而mService.getLifecycleManager().scheduleTransaction(clientTransaction);是如何执行的呢

代码语言:javascript
复制
    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();
        }
    }

其实就是执行了 transaction的client的schedule()方法。继续看到

代码语言:javascript
复制
    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

这里个client是什么,会到创建的地方看到

代码语言:javascript
复制
    final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
            r.appToken);

咱们的IApplicationThread嘛。看到他的scheduleTransaction

代码语言:javascript
复制
        @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }

继续看到 ActivityThread

代码语言:javascript
复制
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

这下是不是很清晰了,就是把 ClientTransaction 通过Handler发送给ActivityThread来处理

代码语言:javascript
复制
    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

代码语言:javascript
复制
    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

代码语言:javascript
复制
    @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下。我们在回顾下设置的地方

代码语言:javascript
复制
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。而execute执行的就是handleLaunchActivity

代码语言:javascript
复制
    @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);
    }

到这里整个流程就穿起来了。 所以知道为什么VirtualApk在9.0上为什么设置的theme没有效果,因为系统启动的调用方式已经发生了改变。

那现在我们如何去修改呢。同理看到Handler处理的地方

代码语言:javascript
复制
    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。 继续反射获取到ActivityInfo。这里附上完整的修改代码

代码语言:javascript
复制
    @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实际为clienttransaction
            Object r = msg.obj;
            try {
                Class clientClazz = r.getClass();
                Field fCallbacks = clientClazz.getDeclaredField("mActivityCallbacks");
                fCallbacks.setAccessible(true);
                //得到transactionz中的callbacks,为一个list,获取其中元素为LaunchActivityItem
                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;
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档