学习笔记:Android小白,这位置网上没资料,通过自己打日志阅读代码走的流程,可能有理解错误的地方。欢迎指正,大家共同进步。
深色主题设置方法:两种设置方法流程是一样的。
- 通过下拉状态栏的快捷按钮
深色主题
切换;- 通过 设置→显示→深色主题开关 切换;
本文以下拉状态栏的快捷按钮深色主题
切换为例;
该快捷按钮类为 UiModeNightTile.java
,直接看点击事件:
// UiModeNightTile.java @Override protected void handleClick(@Nullable View view) { if (getState().state == Tile.STATE_UNAVAILABLE) { return; } boolean newState = !mState.value; // 设置主题模式 mUiModeManager.setNightModeActivated(newState); // 更新该开关的状态 refreshState(newState); }
根据上面的我们直接找 UiModeManager.java 的 setNightModeActivated() 方法:
UiModeManager#setNightModeActivated()
// UiModeManager.java @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivated(boolean active) { if (mService != null) { try { // mService 为 UiModeManagerService 的对象 return mService.setNightModeActivated(active); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return false; }
UiModeManagerService#setNightModeActivated()
// UiModeManagerService.java @Override public boolean setNightModeActivated(boolean active) { return setNightModeActivatedForModeInternal(mNightModeCustomType, active); } private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) { // 省略部分代码...... synchronized (mLock) { final long ident = Binder.clearCallingIdentity(); try { // 自动、自定义的主题切换 if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { unregisterScreenOffEventLocked(); mOverrideNightModeOff = !active; mOverrideNightModeOn = active; mOverrideNightModeUser = mCurrentUser; persistNightModeOverrides(mCurrentUser); } else if (mNightMode == UiModeManager.MODE_NIGHT_NO && active) { // 夜间模式 mNightMode = UiModeManager.MODE_NIGHT_YES; } else if (mNightMode == UiModeManager.MODE_NIGHT_YES && !active) { // 日间模式 mNightMode = UiModeManager.MODE_NIGHT_NO; } // 更新 Configuration updateConfigurationLocked(); // 应用 Configuration applyConfigurationExternallyLocked(); // 为当前用户 Secure.putIntForUser 配置 persistNightMode(mCurrentUser); return true; } finally { Binder.restoreCallingIdentity(ident); } } }
根据上述代码这里主要看下 applyConfigurationExternallyLocked()、persistNightMode() 两个方法;
persistNightMode() 方法简单,先看 UiModeManagerService#persistNightMode()
// UiModeManagerService.java private void persistNightMode(int user) { // 如果不处于汽车模式,才去设置 if (mCarModeEnabled || mCar) return; // 这里主要是将 mNightMode 的值 put 到数据库。 Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, user); // 夜间模式自定义类型 Secure.putLongForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user); // 定义自动夜间模式启动毫秒数 Secure.putLongForUser(getContext().getContentResolver(), Secure.DARK_THEME_CUSTOM_START_TIME, mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user); // 自定义自动夜间模式结束毫秒 Secure.putLongForUser(getContext().getContentResolver(), Secure.DARK_THEME_CUSTOM_END_TIME, mCustomAutoNightModeEndMilliseconds.toNanoOfDay() / 1000, user); }
再接着看 UiModeManagerService#applyConfigurationExternallyLocked() 方法:
// UiModeManagerService.java private void applyConfigurationExternallyLocked() { if (mSetUiMode != mConfiguration.uiMode) { mSetUiMode = mConfiguration.uiMode; // 清除快照缓存, mWindowManager.clearSnapshotCache(); try { // 这位置很重要,如果把这里注释了 就无法开机了,估计是没用主题了 ActivityTaskManager.getService().updateConfiguration(mConfiguration); } catch (RemoteException e) { Slog.w(TAG, "Failure communicating with activity manager", e); } catch (SecurityException e) { Slog.e(TAG, "Activity does not have the ", e); } } }
上述代码将调到 ActivityTaskManagerService#updateConfiguration()
// ActivityTaskManagerService.java @Override public boolean updateConfiguration(Configuration values) { mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()"); synchronized (mGlobalLock) { if (mWindowManager == null) { Slog.w(TAG, "Skip updateConfiguration because mWindowManager isn't set"); return false; } if (values == null) { // 从窗口管理器获取当前配置 values = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY); } mH.sendMessage(PooledLambda.obtainMessage( ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal, DEFAULT_DISPLAY)); final long origId = Binder.clearCallingIdentity(); try { if (values != null) { Settings.System.clearConfiguration(values); } // 重点关注这里。更新配置 updateConfigurationLocked(values, null, false, false /* persistent */, UserHandle.USER_NULL, false /* deferResume */, mTmpUpdateConfigurationResult); return mTmpUpdateConfigurationResult.changes != 0; } finally { Binder.restoreCallingIdentity(origId); } } } boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale, boolean persistent, int userId, boolean deferResume, ActivityTaskManagerService.UpdateConfigurationResult result) { int changes = 0; boolean kept = true; deferWindowLayout(); try { if (values != null) { // 更新全局配置 changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId); } if (!deferResume) { // 更新后确保配置和可见性 kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); } } finally { continueWindowLayout(); } if (result != null) { result.changes = changes; result.activityRelaunched = !kept; } return kept; }
这里直接看更新 updateGlobalConfigurationLocked() 方法:
// ActivityTaskManagerService.java int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale, boolean persistent, int userId) { // 省略部分代码...... SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap(); for (int i = pidMap.size() - 1; i >= 0; i--) { final int pid = pidMap.keyAt(i); final WindowProcessController app = pidMap.get(pid); ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new " + "config %s", app.mName, mTempConfig); // 通知每个进程配置更改 // 这句话注释掉 将无法切换主题模式,不管用。 // 只需要关注这里就行 app.onConfigurationChanged(mTempConfig); } final Message msg = PooledLambda.obtainMessage( ActivityManagerInternal::broadcastGlobalConfigurationChanged, mAmInternal, changes, initLocale); mH.sendMessage(msg); // 更新存储的全局配置并通知所有人有关更改。 // 这里注释了将无法开机,没用任何主题。 mRootWindowContainer.onConfigurationChanged(mTempConfig); return changes; }
上述代码跟进去,将会回调 WindowProcessController#onConfigurationChanged()
// WindowProcessController.java @Override public void onConfigurationChanged(Configuration newGlobalConfig) { // super 父类 ConfigurationContainer super.onConfigurationChanged(newGlobalConfig); updateConfiguration(); }
接着看 ConfigurationContainer#onConfigurationChanged()
// ConfigurationContainer.java public void onConfigurationChanged(Configuration newParentConfig) { //临时configuration变量,保存更新之前的mResolvedOverrideConfiguration mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration); //更新mResolvedOverrideConfiguration,可重写此方法增添特殊配置 resolveOverrideConfiguration(newParentConfig); mFullConfiguration.setTo(newParentConfig); // mFullConfiguration 是实际更新后 configuration 的最终状态 mFullConfiguration.windowConfiguration.unsetAlwaysOnTop(); //根据差异更新最终状态:mFullConfiguration mFullConfiguration.updateFrom(mResolvedOverrideConfiguration); //合并此次修改,保存至mMergedOverrideConfiguration onMergedOverrideConfigurationChanged(); if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) { //进入此分支,代表特殊配置修改,通知监听者, // 当第三方应用设置和默认系统主题不一样的时候,该位置是应用主动请求 for (int i = mChangeListeners.size() - 1; i >= 0; --i) { mChangeListeners.get(i).onRequestedOverrideConfigurationChanged( mResolvedOverrideConfiguration); } } Log.d("yexiao","yexiao:"+mChangeListeners.size()); for (int i = mChangeListeners.size() - 1; i >= 0; --i) { Log.d("yexiao","yexiao:"+mChangeListeners.get(i)); //通知监听者,已经合并父配置修改 mChangeListeners.get(i).onMergedOverrideConfigurationChanged( mMergedOverrideConfiguration); } for (int i = getChildCount() - 1; i >= 0; --i) { //传达configuration最终状态到子类 // 这个方法也很重要,但目前不清楚作用,注释掉将无法开机, dispatchConfigurationToChild(getChildAt(i), mFullConfiguration); } }
这里看一个堆栈:
12-18 15:29:40.717 3118 3118 D yexiao : java.lang.Throwable 12-18 15:29:40.717 3118 3118 D yexiao : at android.content.res.AssetManager.rebaseTheme(AssetManager.java:1243) 12-18 15:29:40.717 3118 3118 D yexiao : at android.content.res.ResourcesImpl$ThemeImpl.rebase(ResourcesImpl.java:1457) 12-18 15:29:40.717 3118 3118 D yexiao : at android.content.res.Resources$Theme.rebase(Resources.java:1874) 12-18 15:29:40.717 3118 3118 D yexiao : at android.content.res.Resources.setImpl(Resources.java:372) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ResourcesManager.updateResourcesForActivity(ResourcesManager.java:1224) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ResourcesManager.createBaseTokenResources(ResourcesManager.java:867) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ContextImpl.createActivityContext(ContextImpl.java:3148) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.createBaseContextForActivity(ActivityThread.java:3799) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3605) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3853) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5832) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5723) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2345) 12-18 15:29:40.717 3118 3118 D yexiao : at android.os.Handler.dispatchMessage(Handler.java:106) 12-18 15:29:40.717 3118 3118 D yexiao : at android.os.Looper.loopOnce(Looper.java:208) 12-18 15:29:40.717 3118 3118 D yexiao : at android.os.Looper.loop(Looper.java:295) 12-18 15:29:40.717 3118 3118 D yexiao : at android.app.ActivityThread.main(ActivityThread.java:7941) 12-18 15:29:40.717 3118 3118 D yexiao : at java.lang.reflect.Method.invoke(Native Method) 12-18 15:29:40.717 3118 3118 D yexiao : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:569) 12-18 15:29:40.717 3118 3118 D yexiao : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1015)
配置改变后通过 binder 调用 IApplicationThread.scheduleTransaction() 方法。
留下问题:配置改变后通过 binder 调用,这中间的流程是怎样的?
ClientTransaction#schedule()
// ClientTransaction.java public void schedule() throws RemoteException { mClient.scheduleTransaction(this); }
然后配置改变消息会进入应用进程,经过ActivityThread.H发送消息,执行 mTransactionExecutor.execute(transaction),进入 TransactionExecutor#executeCallbacks() 方法:
// TransactionExecutor.java @VisibleForTesting public void executeCallbacks(ClientTransaction transaction) { // 省略部分代码...... final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); // 省略部分代码...... // 重点关注这里, item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); // 省略部分代码...... } }
ClientTransactionItem 的实现方法在 ActivityTransactionItem 中,所以进入到 ActivityTransactionItem#execute()
// ActivityTransactionItem.java @Override public final void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { final ActivityClientRecord r = getActivityClientRecord(client, token); // 抽象方法,在 ActivityRelaunchItem.java 中被实现。 execute(client, r, pendingActions); }
ActivityRelaunchItem#execute()
// ActivityRelaunchItem.java @Override public void execute(ClientTransactionHandler client, ActivityClientRecord r, PendingTransactionActions pendingActions) { if (mActivityClientRecord == null) { if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled"); return; } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); // 重点关注这里 client.handleRelaunchActivity(mActivityClientRecord, pendingActions); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }
后面就不详细分析了;
后续流程:ActivityThread#handleRelaunchActivity() → ActivityThread#handleRelaunchActivityInner() → ActivityThread#handleLaunchActivity() → ActivityThread#performLaunchActivity()→ ActivityThread#createBaseContextForActivity() → ContextImpl #createActivityContext() → ResourcesManager#createBaseTokenResources() → ResourcesManager#updateResourcesForActivity() → Resources #setImpl() → Resources#Theme.rebase() → AssetManager#rebaseTheme()
这里注意:
在ActivityThread.java
中有performActivityConfigurationChanged()
和performLaunchActivity()
两个方法,都可以更新资源主题,我个人认为一个是配置单独某个应用的,一个是配置全局的。
到此完成应用进程回调。
那么系统进程如何传送配置信息到应用进程?
这里回到 ActivityTaskManagerService.java。通过ensureConfigAndVisibilityAfterUpdate方法,确保目前启动的activity,重启来加载新的资源
ActivityTaskManagerService#ensureConfigAndVisibilityAfterUpdate()
// ActivityTaskManagerService.java boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { boolean kept = true; final Task mainRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); // mainRootTask is null during startup. if (mainRootTask != null) { if (changes != 0 && starting == null) { // 如果配置改变了,并且调用者还没有在启动一个活动的过程中, // 那么找到最上面的活动来检查它的配置是否需要改变 starting = mainRootTask.topRunningActivity(); } if (starting != null) { // 重点关注这里 kept = starting.ensureActivityConfiguration(changes, false /* preserveWindow */); mRootWindowContainer.ensureActivitiesVisible(starting, changes, !PRESERVE_WINDOWS); } } return kept; }
starting 是 ActivityRecord 对象,所有看 ActivityRecord #ensureActivityConfiguration()
// ActivityRecord.java boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow, boolean ignoreVisibility) { // 省略部分代码...... if (changes == 0 && !forceNewConfig) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s", this); // 不relaunch 时需要去走 scheduleConfigurationChanged让Activity执行onConfiguration的流程 if (displayChanged) { scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig); } else { scheduleConfigurationChanged(newMergedOverrideConfig); } return true; } // 省略部分代码...... return true; }
displayChanged未改变的前提下,走 scheduleConfigurationChanged(),通知应用进程。
**ActivityRecord#scheduleConfigurationChanged
// ActivityRecord.java private void scheduleConfigurationChanged(Configuration config) { if (!attachedToProcess()) { ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration " + "update - client not running, activityRecord=%s", this); return; } try { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, " + "config: %s", this, config); mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token, ActivityConfigurationChangeItem.obtain(config)); } catch (RemoteException e) { // If process died, whatever. } }
至此,应用进程可以根据新配置更新布局等信息。