学习笔记:
我们根据实际问题进行分析:
设置多用户后,点击切换到新用户,在准备阶段返回主用户,移除新用户,概率出现返回键消失。
大家可以根据自己的经验判断下问题出在哪?
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 处于活动状态和辅助显示时,始终禁用最近使用。
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
变量,而这个变量,我通过日志打印,发现取决于 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。
通过打印堆栈,发现 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;
}
在这里,我们发现 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
。
回到开头那实际问题,当用户切换时有一个广播发出,最终在 DisplayPolicy.java
这边执行 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
处的判断条件,并只在执行 DisplayPolicy#resetSystemBarAttributes()
且 mLastDisableFlags
异常 时触发。