TaskBar 是如何启动的

1,087 阅读7分钟

以AOSP为例,如果没有使用AOSP的Launcher3,那应该在各自厂商的vendor目录下 /packages/apps/Launcher3/quickstep/src/com/android/launcher3/taskbar/

从Android 12.1之后,大屏设备上TaskBar替换了传统的NavigationBar

Taskbar.JPG

TaskBar的启动过程

启动TaskBar的一个核心工具类是TaskbarManager,TaskbarManager是在Launcher3中的TouchInteractionService创建时被初始化的,TouchInteractionService及之前的流程不在本篇的讨论范围之内。 /packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java

382      @Override
383      public void onCreate() {
384          super.onCreate();
385          // Initialize anything here that is needed in direct boot mode.
386          // Everything else should be initialized in onUserUnlocked() below.
387          mMainChoreographer = Choreographer.getInstance();
388          mAM = ActivityManagerWrapper.getInstance();
389          mDeviceState = new RecentsAnimationDeviceState(this, true);
390          mTaskbarManager = new TaskbarManager(this);
391          mRotationTouchHelper = mDeviceState.getRotationTouchHelper();

在TaskbarManager被创建出来之后,就会注册监听config的变化 /packages/apps/Launcher3/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java

120              @Override
121              public void onConfigurationChanged(Configuration newConfig) {
                     //判断是否创建Taskbar的关键点就是DeviceProfile
122                  DeviceProfile dp = mUserUnlocked
123                          ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
124                          : null;
125                  int configDiff = mOldConfig.diff(newConfig);
126                  int configsRequiringRecreate = ActivityInfo.CONFIG_ASSETS_PATHS
127                          | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_UI_MODE
128                          | ActivityInfo.CONFIG_SCREEN_SIZE;
129                  boolean requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
130                  if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
131                          && mTaskbarActivityContext != null && dp != null) {
132                      // Additional check since this callback gets fired multiple times w/o
133                      // screen size changing, or when simply rotating the device.
134                      DeviceProfile oldDp = mTaskbarActivityContext.getDeviceProfile();
135                      boolean isOrientationChange =
136                              (configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0;
137                      int oldWidth = isOrientationChange ? oldDp.heightPx : oldDp.widthPx;
138                      int oldHeight = isOrientationChange ? oldDp.widthPx : oldDp.heightPx;
139                      if (dp.widthPx == oldWidth && dp.heightPx == oldHeight) {
140                          configDiff &= ~ActivityInfo.CONFIG_SCREEN_SIZE;
141                          requiresRecreate = (configDiff & configsRequiringRecreate) != 0;
142                      }
143                  }
144  
145                  if (requiresRecreate) {
                         //提前进入recreateTaskbar的流程
146                      recreateTaskbar();
147                  } else {
148                      // Config change might be handled without re-creating the taskbar
149                      if (mTaskbarActivityContext != null) {
150                          if (dp != null && dp.isTaskbarPresent) {
151                              mTaskbarActivityContext.updateDeviceProfile(dp);
152                          }
153                          mTaskbarActivityContext.onConfigurationChanged(configDiff);
154                      }
155                  }
156                  mOldConfig = newConfig;
157              }
158  
159              @Override
160              public void onLowMemory() { }
161          };
162          mShutdownReceiver = new SimpleBroadcastReceiver(i -> destroyExistingTaskbar());
163          mDispInfoChangeListener = (context, info, flags) -> {
164              if ((flags & CHANGE_FLAGS) != 0) {
165                  recreateTaskbar();
166              }
167          };
168          mDisplayController.addChangeListener(mDispInfoChangeListener);
169          SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
170                  mUserSetupCompleteListener);
171          SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
172                  mNavBarKidsModeListener);
173          mContext.registerComponentCallbacks(mComponentCallbacks);
174          mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
175          //这里可以看到每次configChange其实都会走recreateTaskbar
176          recreateTaskbar();
177      }

在TaskbarManager的onConfigurationChange()回调中,是对config的很多综合判断,也有很多地方在调用recreateTaskbar(),基本上是取决于config中的一些device信息。

263      private void recreateTaskbar() {
             //每次调用之前都会先清除 pre-Taskbar
264          destroyExistingTaskbar();
265  
266          DeviceProfile dp =
267                  mUserUnlocked ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
268          //判断TaskBar的关键信息
269          boolean isTaskBarEnabled = dp != null && dp.isTaskbarPresent;
270  
271          if (!isTaskBarEnabled) {
                 //如果当前的config配置不需要taskBar就返回
272              SystemUiProxy.INSTANCE.get(mContext)
273                      .notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
274              return;
275          }
             //能走到这里就说明需要TaskBar了,正式开始创建
276          //TaskbarActivityContext是TaskBar的另一个重要工具类,这里先创建
277          mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp, mNavButtonController,
278                  mUnfoldProgressProvider);
279          //对TaskbarActivityContext初始化
280          mTaskbarActivityContext.init(mSharedState);
281          if (mActivity != null) {
282              mTaskbarActivityContext.setUIController(
283                      createTaskbarUIControllerForActivity(mActivity));
284          }
285      }

Taskbar的相关内容实际上是在TouchInteractionService创建时就已经全部初始化完毕了,并且狠毒oconfig change都会回调到TaskbarManager当中,每次回调都会去走recreateTaskbar,先清除之前的TaskBar,再根据是否需要来重新创建。

所以TaskBar是一个持续动态消失与重构的过程。比如我们在Launcher页面上滑时,Taskbar是有变化的,返回home页面时Taskbar又再次更新。作为从系统的角度来讲,Taskbar相对于NavigationBar的loading显然加重了很多。不过Android 版本的升级带来性能drop是可以理解的。只是针对个别高版本低配置的平台,这里可能也是一个小的优化方向。

上面有注释到boolean isTaskBarEnabled 是判断Taskbar的关键信息,但如果仅仅修改这里的参数,作用域仅仅局限于Taskbar,比如将这里置为false,在大屏设备上最终的结果是TaskBar没有创建,NavigationBar也没有创建。可以说明NavigationBar创建与否,和TaskBar没有必然联系。再来看另外一个重要的工具类 TaskbarActivityContext /packages/apps/Launcher3/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java

在创建TaskbarActivityContext时,是先new出这个对象,再去init的,我们就先来看构造函数

135      public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
136              TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
137              unfoldTransitionProgressProvider) {
138          super(windowContext);
             //保存了当前的DeviceProfile信息
139          mDeviceProfile = dp.copy(this);
140  
141          final Resources resources = getResources();
142  
             //区分3-Buttons导航或是虚拟导航
143          mNavMode = DisplayController.getNavigationMode(windowContext);
144          mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
145          mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
146                  () -> getPackageManager().isSafeMode());
147          mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
148                  Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
149          mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
150                  Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
151          mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
152                  Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
153  
             // Taskbar中显示的UI Icon大小
154          updateIconSize(resources);
155  
156          // Get display and corners first, as views might use them in constructor.
             //显示在哪个display上
157          Display display = windowContext.getDisplay();
158          Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
159                  ? windowContext.getApplicationContext()
160                  : windowContext.getApplicationContext().createDisplayContext(display);
161          mWindowManager = c.getSystemService(WindowManager.class);
162          mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
163          mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
164  
165          // Inflate出一些重要view,当遇到按键相关问题时,可以从这里debug
166          mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
167                  R.layout.taskbar, null, false);
168          TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
169          TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
170          FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
171          StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
172  
173          mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
174  
175          //new出来很多和Taskbar相关的Controller,各自分工明确
176          mControllers = new TaskbarControllers(this,
177                  new TaskbarDragController(this),
178                  buttonController,
179                  getPackageManager().hasSystemFeature(FEATURE_PC)
180                          ? new DesktopNavbarButtonsViewController(this, navButtonsView) :
181                          new NavbarButtonsViewController(this, navButtonsView),
182                  new RotationButtonController(this,
183                          c.getColor(R.color.taskbar_nav_icon_light_color),
184                          c.getColor(R.color.taskbar_nav_icon_dark_color),
185                          R.drawable.ic_sysbar_rotate_button_ccw_start_0,
186                          R.drawable.ic_sysbar_rotate_button_ccw_start_90,
187                          R.drawable.ic_sysbar_rotate_button_cw_start_0,
188                          R.drawable.ic_sysbar_rotate_button_cw_start_90,
189                          () -> getDisplay().getRotation()),
190                  new TaskbarDragLayerController(this, mDragLayer),
191                  new TaskbarViewController(this, taskbarView),
192                  new TaskbarScrimViewController(this, taskbarScrimView),
193                  new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
194                          mWindowManager, WindowManagerGlobal.getWindowManagerService()),
195                  new TaskbarKeyguardController(this),
196                  new StashedHandleViewController(this, stashedHandleView),
197                  new TaskbarStashController(this),
198                  new TaskbarEduController(this),
199                  new TaskbarAutohideSuspendController(this),
200                  new TaskbarPopupController(this),
201                  new TaskbarForceVisibleImmersiveController(this),
202                  new TaskbarAllAppsController(this, dp),
203                  new TaskbarInsetsController(this));
204      }

在构造函数中,有多个ime、NavBar等相关对象,在解决Taskbar相关问题时,如果有怀疑这些组件,可以从这里开始debug继续排查。比如遇到过TaskBar上的Activity UI显示异常,就可以从上面的updateIconSize(resources)来调查,一般都是xml适配方面的问题。此外,new 出来的Controllers是解决问题的重要工具,这些Controllers根据定义的名字,都很直观。构造函数之后,再看下 init 过程:

206      public void init(@NonNull TaskbarSharedState sharedState) {
207          mLastRequestedNonFullscreenHeight = getDefaultTaskbarWindowHeight();
208          mWindowLayoutParams = createDefaultWindowLayoutParams();
209  
210          // Initialize controllers after all are constructed.
211          mControllers.init(sharedState);
212          updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
213  
214          mWindowManager.addView(mDragLayer, mWindowLayoutParams);
215      }

这个就非常简单直接了,看到addView应该就知道,此时TaskBar已经显示出来了,当然算是创建完成了。显示创建了Taskbar显示区域的window属性createDefaultWindowLayoutParams(),然后也初始化了之前创建的很多Controllers,更新一个systemUI的状态,就算是完成了。

对于NavigationBar,我们可以通过getNavigationBarWidth 和getNavigationBarHeight来获取它的宽高,那么对于Taskbar,是否有提供给应用层的类似接口呢?

答案是没有的,但我们仍能通过类似的方式来获取。参考google开发者指南 developer.android.google.cn/develop/ui/…

Note:Although The Taskbar doesn't have its own inset type, its dimension can be retrieved using WindowInsetsCompat.Type.NavigationBars() or WindowInsetsCompat.Type.systemBars()

这是google 给出的一个demo code

ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
  Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
  // Apply the insets as a margin to the view. Here the system is setting
  // only the bottom, left, and right dimensions, but apply whichever insets are
  // appropriate to your layout. You can also update the view padding
  // if that's more appropriate.
  MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
  mlp.leftMargin = insets.left;
  mlp.bottomMargin = insets.bottom;
  mlp.rightMargin = insets.right;
  v.setLayoutParams(mlp);

  // Return CONSUMED if you don't want want the window insets to keep being
  // passed down to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

在Android 13的new feature也有关于Taskbar的简单介绍 developer.android.google.cn/about/versi…