android插件化在9.0上插件activity的theme失效问题(VirtualApk)
在使用VirtualApk的时候,发现在android 9.0上,插件中的Activity配置的theme失效
这个问题和Android系统代码修改有关,我们看下9.0前后设置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
中进行了Activity的创建和theme的设置。而这个theme是从参数ActivityClientRecord r
获取,看到performLaunchActivity
被调用的地方
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...省略代码
Activity a = performLaunchActivity(r, customIntent);
}
继续找到
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
中
@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
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
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);
是如何执行的呢
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()
方法。继续看到
public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}
这里个client是什么,会到创建的地方看到
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
咱们的IApplicationThread嘛。看到他的scheduleTransaction
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
继续看到 ActivityThread
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
这下是不是很清晰了,就是把 ClientTransaction 通过Handler发送给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下。我们在回顾下设置的地方
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
@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处理的地方
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。这里附上完整的修改代码
@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;
}