學習筆記:
我們根據實際問題進行分析:
設置多用戶后,點擊切換到新用戶,在準備階段返回主用戶,移除新用戶,概率出現返回鍵消失。
大家可以根據自己的經驗判斷下問題出在哪?
BackButton
是否顯示在 NavigationBarView#updateNavButtonIcons() 中進行更新:
// NavigationBarView.java
public void updateNavButtonIcons() {
// 我們必須分別在退出或進入汽車模式時替換或恢復后退和主頁按鈕圖標。
// 最近在導航欄的 CarMode 中不可用,因此不需要更改為最近圖標
final boolean useAltBack =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
KeyButtonDrawable backIcon = mBackIcon;
orientBackButton(backIcon);
KeyButtonDrawable homeIcon = mHomeDefaultIcon;
if (!mUseCarModeUi) {
orientHomeButton(homeIcon);
}
getHomeButton().setImageDrawable(homeIcon);
getBackButton().setImageDrawable(backIcon);
updateRecentsIcon();
// Update IME button visibility, a11y and rotate button always overrides the appearance
boolean disableImeSwitcher =
(mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
|| isImeRenderingNavButtons();
mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
mBarTransitions.reapplyDarkIntensity();
boolean disableHome = isGesturalMode(mNavBarMode)
|| ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
// 當備用汽車模式 UI 處于活動狀態(tài)和輔助顯示時,始終禁用最近使用。
boolean disableRecent = isRecentsButtonDisabled();
// 如果 hone 和 recents 都被禁用,則禁用 home handle
boolean disableHomeHandle = disableRecent
&& ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
// ***********重點關注*******mDisabledFlags**********
boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
|| ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0))
|| isImeRenderingNavButtons();
final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
if (mOverviewProxyEnabled) {
disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
disableBack = disableHome = false;
}
} else if (pinningActive) {
disableBack = disableRecent = false;
}
ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons);
if (navButtons != null) {
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
if (!lt.getTransitionListeners().contains(mTransitionListener)) {
lt.addTransitionListener(mTransitionListener);
}
}
}
getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE);
getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
notifyActiveTouchRegions();
}
根據上述代碼可知 BackButton
的顯示取決于 disableBack
變量,而這個變量,我通過日志打印,發(fā)現取決于 mDisabledFlags
的值,這里 mDisabledFlags
我打印出來了3個值(當 mDisabledFlags = 4194304
時,BackButton
將不會顯示。)
而 mDisabledFlags
值得更新在 NavigationBarView#setDisabledFlags() 方法中:
// NavigationBarView.java
void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
if (mDisabledFlags == disabledFlags) return;
final boolean overviewEnabledBefore = isOverviewEnabled();
mDisabledFlags = disabledFlags;
// 如果剛剛啟用概覽,則更新圖標以確保顯示正確的圖標
if (!overviewEnabledBefore && isOverviewEnabled()) {
reloadNavIcons();
}
updateNavButtonIcons();
updateSlippery();
updateDisabledSystemUiStateFlags(sysUiState);
}
上述的 setDisabledFlags()
方法在 NavigationBar#disable() 中被調用:
// NavigationBar.java
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
if (displayId != mDisplayId) {
return;
}
// Navigation bar flags are in both state1 and state2.
final int masked = state1 & (StatusBarManager.DISABLE_HOME
| StatusBarManager.DISABLE_RECENT
| StatusBarManager.DISABLE_BACK
| StatusBarManager.DISABLE_SEARCH);
if (masked != mDisabledFlags1) {
mDisabledFlags1 = masked;
mView.setDisabledFlags(state1, mSysUiFlagsContainer);
updateScreenPinningGestures();
}
// 省略部分代碼......
}
在 disable()
方法中,有兩個 state
值,這里只關注 state1。
通過打印堆棧,發(fā)現 disable()
在 CommandQueue.java
中的 handleMessage()
里被調;
CommandQueue#disable()
// CommandQueue.java
public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
boolean animate) {
synchronized (mLock) {
setDisabled(displayId, state1, state2);
mHandler.removeMessages(MSG_DISABLE);
final SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
args.argi2 = state1;
args.argi3 = state2;
args.argi4 = animate ? 1 : 0;
// 重點關注,Handler 消息,將會在handleMessage()里面處理
Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
if (Looper.myLooper() == mHandler.getLooper()) {
// If its the right looper execute immediately so hides can be handled quickly.
mHandler.handleMessage(msg);
msg.recycle();
} else {
msg.sendToTarget();
}
}
}
這里還是要關注 state1
,因為我們就是在朔源,找源頭;通過分析可以看:StatusBarManagerService#disableLocked()
// StatusBarManagerService.java
private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
int whichFlag) {
// 該方法里面會進行 setFlags(),set 的其實就是 what。后面講到
setFlags()
manageDisableListLocked(userId, what, token, pkg, whichFlag);
// 重點關注
final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
final UiState state = getUiState(displayId);
if (!state.disableEquals(net1, net2)) {
state.setDisabled(net1, net2);
mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
if (mBar != null) {
try {
// 這里我們只需關注 net1 的來源;這里會回調到:CommandQueue#disable()
mBar.disable(displayId, net1, net2);
} catch (RemoteException ex) {
}
}
}
}
通過上述代碼,我們留意到 StatusBarManagerService#gatherDisableActionsLocked()
int gatherDisableActionsLocked(int userId, int which) {
final int N = mDisableRecords.size();
// gather the new net flags
int net = 0;
for (int i=0; i<N; i++) {
final DisableRecord rec = mDisableRecords.get(i);
if (rec.userId == userId) {
net |= rec.getFlags(which);
}
}
return net;
}
在這里,我們發(fā)現 net
是通過 rec.getFlags(which)
取的;那么就需要找對應的 setFlags()
方法。
這里面如何 setFlags()
的,不做過多分析,其實就是在StatusBarManagerService#manageDisableListLocked()
方法里。
根據上述分析,接下來就需要跟蹤 what
值得來源。
下面看個堆棧:
02-16 13:17:20.210965 1268 1316 D yexiao : java.lang.Throwable
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.statusbar.StatusBarManagerService.disableLocked(StatusBarManagerService.java:1021)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.statusbar.StatusBarManagerService.setDisableFlags(StatusBarManagerService.java:1175)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.statusbar.StatusBarManagerService.-$$Nest$msetDisableFlags(Unknown Source:0)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.statusbar.StatusBarManagerService$1.setDisableFlags(StatusBarManagerService.java:383)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.wm.DisplayPolicy.lambda$updateSystemBarAttributes$14(DisplayPolicy.java:2376)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.wm.DisplayPolicy$$ExternalSyntheticLambda0.accept(Unknown Source:8)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.wm.DisplayPolicy.lambda$callStatusBarSafely$16$com-android-server-wm-DisplayPolicy(DisplayPolicy.java:2409)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.wm.DisplayPolicy$$ExternalSyntheticLambda16.run(Unknown Source:4)
02-16 13:17:20.210965 1268 1316 D yexiao : at android.os.Handler.handleCallback(Handler.java:942)
02-16 13:17:20.210965 1268 1316 D yexiao : at android.os.Handler.dispatchMessage(Handler.java:99)
02-16 13:17:20.210965 1268 1316 D yexiao : at android.os.Looper.loopOnce(Looper.java:209)
02-16 13:17:20.210965 1268 1316 D yexiao : at android.os.Looper.loop(Looper.java:296)
02-16 13:17:20.210965 1268 1316 D yexiao : at android.os.HandlerThread.run(HandlerThread.java:67)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.ServiceThread.run(ServiceThread.java:44)
02-16 13:17:20.210965 1268 1316 D yexiao : at com.android.server.UiThread.run(UiThread.java:45)
上面堆棧,就是 what
值得來源,一旦有值改變,一定會通過 StatusBarManagerService#setDisableFlags()
方法回調到 StatusBarManagerService#disableLocked()
。
根據堆棧看 DisplayPolicy#updateSystemBarAttributes()
// DisplayPolicy.java
void updateSystemBarAttributes() {
WindowState winCandidate = mFocusedWindow;
// 省略部分代碼......
final WindowState win = winCandidate;
mSystemUiControllingWindow = win;
final int displayId = getDisplayId();
// ******重點關注****** 1
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
navColorWin) | opaqueAppearance;
final int behavior = win.mAttrs.insetsFlags.behavior;
final String focusedApp = win.mAttrs.packageName;
final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
|| !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
final AppearanceRegion[] statusBarAppearanceRegions =
new AppearanceRegion[mStatusBarAppearanceRegionList.size()];
mStatusBarAppearanceRegionList.toArray(statusBarAppearanceRegions);
// ******重點關注****** 2
Log.d("yexiao","mLastDisableFlags = "+ mLastDisableFlags +"----------- disableFlags = "+disableFlags );
if (mLastDisableFlags != disableFlags) {
mLastDisableFlags = disableFlags;
final String cause = win.toString();
callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
cause));
}
// 省略部分代碼......
}
上述代碼注釋1
處,disableFlags
的值來源其實是在:WindowManagerService#relayoutWindow()
,還可以往上朔源,這里就不在深入了。
而wm
這邊的值,是在注釋2
處,設置到StatusBarManagerService
那邊去的,在按前面的流程,一步一步設置到SystemUI
。
回到開頭那實際問題,當用戶切換時有一個廣播發(fā)出,最終在 DisplayPolicy.java
這邊執(zhí)行 DisplayPolicy#resetSystemBarAttributes()
// DisplayPolicy.java
void resetSystemBarAttributes() {
mLastDisableFlags = 0;
updateSystemBarAttributes();
}
這里將 mLastDisableFlags
置 0
,而 DisplayPolicy#updateSystemBarAttributes()
會一直被某個方法不停回調,這里沒用去查看是哪個方法。當置 0
時,會出現時序問題;類似這樣的變化:
mLastDisableFlags = 0 ----------- disableFlags = 4194304
mLastDisableFlags = 4194304 ----------- disableFlags = 4194304
mLastDisableFlags = 4194304 ----------- disableFlags = 4194304
// mLastDisableFlags = 4194304 ----------- disableFlags = 0 正常情況;
mLastDisableFlags = 0 ----------- disableFlags = 0 // 出現時序問題的情況
導致 disableFlags = 0
這種情況,無法設置到 StatusBarManagerService
那邊去,SystemUI
那邊也就無法更改。
該問題:需要修改注釋2
處的判斷條件,并只在執(zhí)行 DisplayPolicy#resetSystemBarAttributes()
且 mLastDisableFlags
異常 時觸發(fā)。