作为一个Android系统开发工程师,StatusBar是永远无法绕开的一个部分,今天来好好的分析一下这个模块

一、简介

从结构上来说,下拉菜单和状态栏都属于StatusBar,在Android9.0之后,可以对状态栏进行变色操作或者添加图标,真正实现了自定义!

在最顶部的super_status_bar结构之后下属有两个分支,分别是

status_bar_expanded下拉菜单,分快捷图标和通知栏。
status_bar_container状态栏,分左边通知栏和右边系统功能的状态图标显示。

二、启动

首先我们来看一下start()方法的代码,在代码中去分析

@Overridepublic void start() {//在这里注册了很多监听事件并进行了一些控制类和服务的获取mScreenLifecycle.addObserver(mScreenObserver);mWakefulnessLifecycle.addObserver(mWakefulnessObserver);mUiModeManager = mContext.getSystemService(UiModeManager.class);mBypassHeadsUpNotifier.setUp();mBubbleController.setExpandListener(mBubbleExpandListener);mActivityIntentHelper = new ActivityIntentHelper(mContext);mColorExtractor.addOnColorsChangedListener(this);mStatusBarStateController.addCallback(this,SysuiStatusBarStateController.RANK_STATUS_BAR);mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);//dream是在充电设备空闲或停靠在桌面底座时启动的交互式屏幕保护程序。 Dreams 为应用程序提供了另一种表达自我的方式,专为展示后背体验而量身定制//这里是引入外部框架并检查服务是否开启mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.checkService(DreamService.DREAM_SERVICE));//进行手机省电模式的监听(如果设备不支持,将不会生效)/* UNISOC: Bug 1074234, 885650, Super power feature @{ */mDataSaverController = Dependency.get(DataSaverController.class);if (UnisocPowerManagerUtil.SUPPORT_SUPER_POWER_SAVE) {int mode = UnisocPowerManagerUtil.getPowerSaveModeInternal();mPowerSaveMode = mode;curMode = mode;}/* @} */mDisplay = mWindowManager.getDefaultDisplay();mDisplayId = mDisplay.getDisplayId();//创建StatusBar时和显现的时候调用updateDisplaySize();//振动器是否开启mVibrateOnOpening = mContext.getResources().getBoolean(R.bool.config_vibrateOnIconAnimation);// start old BaseStatusBar.start().//注册功能管理mWindowManagerService = WindowManagerGlobal.getWindowManagerService();mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);mAccessibilityManager = (AccessibilityManager)mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);mWallpaperSupported =mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();// Connect in to the status bar manager service//这里类似消息队列,让线程不会堆积堵塞mCommandQueue.addCallback(this);RegisterStatusBarResult result = null;try {result = mBarService.registerStatusBar(mCommandQueue);} catch (RemoteException ex) {ex.rethrowFromSystemServer();}createAndAddWindows(result);if (mWallpaperSupported) {// Make sure we always have the most current wallpaper info.IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);//注册广播接收mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,null /* handler */, UserHandle.ALL);mWallpaperChangedReceiver.onReceive(mContext, null);} else if (DEBUG) {Log.v(TAG, "start(): no wallpaper service ");}// Set up the initial notification state. This needs to happen before CommandQueue.disable()setUpPresenter();if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) {showTransientUnchecked();}onSystemBarAppearanceChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,result.mNavbarColorManagedByIme);mAppFullscreen = result.mAppFullscreen;mAppImmersive = result.mAppImmersive;// StatusBarManagerService has a back up of IME token and it's restored here.setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,result.mImeBackDisposition, result.mShowImeSwitcher);// Set up the initial icon state//这里mIcons是一个arrayMap,所以可以根据他的键值去寻找int numIcons = result.mIcons.size();for (int i = 0; i < numIcons; i++) {mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));}if (DEBUG) {Log.d(TAG, String.format("init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",numIcons,result.mDisabledFlags1,result.mAppearance,result.mImeWindowVis));}IntentFilter internalFilter = new IntentFilter();internalFilter.addAction(BANNER_ACTION_CANCEL);internalFilter.addAction(BANNER_ACTION_SETUP);mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,null);if (mWallpaperSupported) {IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(ServiceManager.getService(Context.WALLPAPER_SERVICE));try {wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);} catch (RemoteException e) {// Just pass, nothing critical.}}// end old BaseStatusBar.start().// Lastly, call to the icon policy to install/update all the icons.mIconPolicy.init();mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);mKeyguardStateController.addCallback(this);startKeyguard();mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);mDozeServiceHost.initialize(this, mNotificationIconAreaController,mStatusBarKeyguardViewManager, mNotificationShadeWindowViewController,mNotificationPanelViewController, mAmbientIndicationContainer);mConfigurationController.addCallback(this);// set the initial view visibilityint disabledFlags1 = result.mDisabledFlags1;int disabledFlags2 = result.mDisabledFlags2;mInitController.addPostInitTask(() -> setUpDisableFlags(disabledFlags1, disabledFlags2));//注册插件监听mPluginManager.addPluginListener(new PluginListener<OverlayPlugin>() {private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();@Overridepublic void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {mMainThreadHandler.post(() -> plugin.setup(getNotificationShadeWindowView(),getNavigationBarView(),new Callback(plugin), mDozeParameters));}@Overridepublic void onPluginDisconnected(OverlayPlugin plugin) {mMainThreadHandler.post(() -> {mOverlays.remove(plugin);mNotificationShadeWindowController.setForcePluginOpen(mOverlays.size() != 0);});}class Callback implements OverlayPlugin.Callback {private final OverlayPlugin mPlugin;Callback(OverlayPlugin plugin) {mPlugin = plugin;}@Overridepublic void onHoldStatusBarOpenChange() {if (mPlugin.holdStatusBarOpen()) {mOverlays.add(mPlugin);} else {mOverlays.remove(mPlugin);}mMainThreadHandler.post(() -> {mNotificationShadeWindowController.setStateListener(b -> mOverlays.forEach(o -> o.setCollapseDesired(b)));mNotificationShadeWindowController.setForcePluginOpen(mOverlays.size() != 0);});}}}, OverlayPlugin.class, true /* Allow multiple plugins */);// For longscreenshot featuresetRegionOrLongshotMode(mContext, 0);// UNISOC: BUG 1353950 add screen resolution observer to control NavigationBarViewmContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(ACTION_SR_CHANGING),false, mScreenResolutionObserver);//UNISOC: Add for bug 1359220registerNavBarModeSettingChange(mContext);}

在start()方法中
1.获取到了很多的服务以及一些控制器
2.判断对壁纸的支持
3.初始化图标按键
4.将自身注册到StatusBarService中供其他app使用

mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));

//消息队列加入回调
mCommandQueue.addCallback(this);
//以上
RegisterStatusBarResult result = null;
try {
//这是一个数据对象,用于服务与客服端之间的通讯
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}

三、创建

接下来我们来看createAndAddWindows()方法

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {makeStatusBarView(result);mNotificationShadeWindowController.attach();mStatusBarWindowController.attach();}

在这个方法里面,主要是调用了别的方法体,首先是makeStatusBarView(result),我们来看看里面都做了些什么

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {final Context context = mContext;updateDisplaySize(); // populates mDisplayMetrics//更新显示尺寸updateResources();//更新变化了的配置内容(进行重新加载)updateTheme();//切换主题颜色FragmentHostManager.get(mPhoneStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;PhoneStatusBarView oldStatusBarView = mStatusBarView;mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanelViewController);mStatusBarView.setScrimController(mScrimController);statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of// mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.// PhoneStatusBarView's new instance will set to be gone in// PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing// that will trigger PanelBar.updateVisibility. If there is a heads up showing,// it needs to notify PhoneStatusBarView's new instance to update the correct// status by calling mNotificationPanelViewController.notifyBarPanelExpansionChanged().if (mHeadsUpManager.hasPinnedHeadsUp()) {mNotificationPanelViewController.notifyBarPanelExpansionChanged();}mStatusBarView.setBouncerShowing(mBouncerShowing);if (oldStatusBarView != null) {float fraction = oldStatusBarView.getExpansionFraction();boolean expanded = oldStatusBarView.isExpanded();mStatusBarView.panelExpansionChanged(fraction, expanded);}HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;if (mHeadsUpAppearanceController != null) {// This view is being recreated, let's destroy the old onemHeadsUpAppearanceController.destroy();}// TODO: this should probably be scoped to the StatusBarComponent// TODO (b/136993073) Separate notification shade and status barmHeadsUpAppearanceController = new HeadsUpAppearanceController(mNotificationIconAreaController, mHeadsUpManager,mNotificationShadeWindowView,mStatusBarStateController, mKeyguardBypassController,mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,mNotificationPanelViewController, mStatusBarView);mHeadsUpAppearanceController.readFrom(oldController);mLightsOutNotifController.setLightsOutNotifView(mStatusBarView.findViewById(R.id.notification_lights_out));mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);checkBarModes();}).getFragmentManager().beginTransaction().replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG).commit();.......//创建导航栏createNavigationBar(result);if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {mLockscreenWallpaper = mLockscreenWallpaperLazy.get();}mKeyguardIndicationController.setIndicationArea(mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area));mNotificationPanelViewController.setKeyguardIndicationController(mKeyguardIndicationController);......// TODO: Find better place for this callback.//电池状态监听mBatteryController.addCallback(new BatteryStateChangeCallback() {@Overridepublic void onPowerSaveChanged(boolean isPowerSave) {mHandler.post(mCheckBarModes);if (mDozeServiceHost != null) {mDozeServiceHost.firePowerSaveChanged(isPowerSave);}}@Overridepublic void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {// noop}});mAutoHideController.setStatusBar(new AutoHideUiElement() {@Overridepublic void synchronizeState() {checkBarModes();}@Overridepublic boolean shouldHideOnTouch() {return !mRemoteInputManager.getController().isRemoteInputActive();}@Overridepublic boolean isVisible() {return isTransientShown();}@Overridepublic void hide() {clearTransient();}});......}

代码很长,截取了一部分。那么在这部分代码中干了些什么事情呢?

1.首先,对拥有的资源文件以及系统主题进行了更新同步
2.使用inflateStatusBarWindow()加载视图初始化资源文件,获取状态栏高度
3.我们注意到,其中有这样一段代码

FragmentHostManager.get(mPhoneStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

这个CollapsedStatusBarFragment就是作为StatusBar的Fragment去加载的。那么我们都知道,一个fragment的加载首先是从onCreateView方法中去加载layout,所以

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) {return inflater.inflate(R.layout.status_bar, container, false);}

所以我们找到status_bar.xml进行查看,这里就不贴代码了,我们会发现有以下几点

1.@+id/status_bar_contents 这个LinearLayout,里面包含了@+id/status_bar_left_side ,从名字来看就知道是状态栏左边部分。这个状态栏左边部分包含了时钟@+id/clock和短信通知@+id/notification_icon_area。

2.@+id/system_icon_area这个也是一个LinearLayout包含了@layout/system_icons,这部分就是状态栏右边部分,里面包含了电池图标和系统状态图标

字段 位置
status_bar_contents
system_icon_area

四、加载图标

接着我们发现不论是什么图标,他们基本都是动态加载的,那么他们是怎么被加载和移除的呢

public void initNotificationIconArea(NotificationIconAreaControllernotificationIconAreaController) {ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);//短信通知iconmNotificationIconAreaInner =notificationIconAreaController.getNotificationInnerAreaView();//移除自己,再添加自己if (mNotificationIconAreaInner.getParent() != null) {((ViewGroup) mNotificationIconAreaInner.getParent()).removeView(mNotificationIconAreaInner);}notificationIconArea.addView(mNotificationIconAreaInner);// Default to showing until we know otherwise.showNotificationIconArea(false);}

根据id找到,这是短信添加的代码。

在短信第一次被添加的时候,会调用NotificationEntryManager.addNotification方法

public void addNotification(StatusBarNotification notification, RankingMap ranking) {try {addNotificationInternal(notification, ranking);} catch (InflationException e) {handleInflationException(notification, e);}}
private void addNotificationInternal(StatusBarNotification notification,RankingMap rankingMap) throws InflationException {String key = notification.getKey();if (DEBUG) {Log.d(TAG, "addNotification key=" + key);}updateRankingAndSort(rankingMap, "addNotificationInternal");Ranking ranking = new Ranking();rankingMap.getRanking(key, ranking);NotificationEntry entry = mPendingNotifications.get(key);if (entry != null) {entry.setSbn(notification);} else {entry = new NotificationEntry(notification,ranking,mFgsFeatureController.isForegroundServiceDismissalEnabled(),SystemClock.uptimeMillis());mAllNotifications.add(entry);mLeakDetector.trackInstance(entry);for (NotifCollectionListener listener : mNotifCollectionListeners) {listener.onEntryInit(entry);}}for (NotifCollectionListener listener : mNotifCollectionListeners) {listener.onEntryBind(entry, notification);}// Construct the expanded view.if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {mNotificationRowBinderLazy.get().inflateViews(entry,() -> performRemoveNotification(notification, REASON_CANCEL),mInflationCallback);}mPendingNotifications.put(key, entry);mLogger.logNotifAdded(entry.getKey());for (NotificationEntryListener listener : mNotificationEntryListeners) {listener.onPendingEntryAdded(entry);}for (NotifCollectionListener listener : mNotifCollectionListeners) {listener.onEntryAdded(entry);}for (NotifCollectionListener listener : mNotifCollectionListeners) {listener.onRankingApplied();}}
public void addNotification(StatusBarNotification notification, RankingMap ranking) {try {addNotificationInternal(notification, ranking);} catch (InflationException e) {handleInflationException(notification, e);}}

在收到系统的通知之后,会在NotificationIconAreaController.updateIconsForLayout方法中更新手机通知栏布局的通知图标,在这里也就是把短信图标更新上去。

StatusBar浅析相关推荐

  1. Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

    前面两篇文章< Android 4.0 ICS SystemUI浅析--SystemUI启动流程>.< Android 4.0 ICS SystemUI浅析--StatusBar结构 ...

  2. Android 4.0 ICS SystemUI浅析——StatusBar结构分析

    在上一篇文章<Android 4.0 ICS SystemUI浅析--SystemUI启动流程>中以及提到了SystemUI的组成,本文主要分析其中的StatusBar结构. 1.布局概览 ...

  3. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  4. 浅析Python中bytes和str区别

    本博转载自:Chown-Jane-Y的浅析Python3中的bytes和str类型 Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示, ...

  5. 学习《Linux设备模型浅析之设备篇》笔记(深挖一)

    这篇文章既然说了是浅析,那就是跳过了一些东西,我们把这些跳过的东西给它尽可能的补回来 今天登陆 lxr.free-electrons.com 发现内核版本已经升级到3.15了,那以后都使用3.15的源 ...

  6. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

  7. iOS 关于UIView覆盖StatusBar的小知识点

    项目中有关于浏览图片的需求, 自己写了一套, 但是一直有个关于StatusBar的问题: 因为在查看图片时隐藏掉了StatusBar, 当结束查看后再显示sta会发现整个界面下滑了20px, 在IM聊 ...

  8. 架构周报| 浅析MySQL JDBC连接配置上的两个误区

    经典案例 \\ 浅析MySQL JDBC连接配置上的两个误区:相信使用MySQL的同学都配置过它的JDBC驱动,多数人会直接从哪里贴一段URL过来,然后稍作修改就上去了,对应的连接池配置也是一样的,很 ...

  9. 超级账本(Hyperledger Fabric)之权限管理浅析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 超级账本(Hyperledger Fabric)之权限管理浅析 超级账本是联盟链的代表,而其相对于共链(例如比特币,以太 ...

最新文章

  1. *Boosting*笔记
  2. Python sys.path详解
  3. 程序员面试题精选100题(07)-翻转句子中单词的顺序[算法]
  4. 直播 | EMNLP 2020:用语义分割的思路解决不完整话语重写任务
  5. 如何绘制caffe网络训练曲线
  6. http://www.vpython.org/index-ch.html
  7. Sql Server 中汉字处理排序规则,全角半角
  8. 开源、免费、企业级的SiteServer CMS .NET CORE 7.0 预览版发布
  9. Multi-thread--C++11中std::lock_guard的使用
  10. 计算两日期之间差多少天----日期格式为:yyyy-mm-dd
  11. Double 中的 NAN与INFINITY
  12. 可以使用TrafficMonitor查看本机的网速情况
  13. unity每次运行总是game窗口最大化怎么解决?
  14. 判断数组中是否包含某一项 indexof 重复的判断不准_「JavaScript」: 老生常谈,数组类型...
  15. 计算机二级九月试题office,9月计算机二级office题库及答案
  16. 爬虫小程序 - 单词量测试
  17. 江民KV2007离线包和安装包下载
  18. 某汽车轮渡口,过江渡船每次能载 10 辆车过江。
  19. layui实现导航栏目
  20. 走马观花之bug预防

热门文章

  1. 藏宝阁显示角色可买服务器,梦幻西游藏宝阁里面买角色时选的可转入服务器是不是一定可以转...
  2. 第39天:WEB攻防-通用漏洞CSRFSSRF协议玩法内网探针漏洞利用
  3. 浅析微信小程序生命周期之应用生命周期
  4. TeXstudio编译提示缺少.sty文件
  5. mysql查询名字叫小明的_MySQL(命令和查询语句)
  6. LEETCODE 136.Singel Number
  7. 服务器连接盘柜后盘符空间显示不对,服务器连接磁盘阵列柜
  8. 技术部负责人的爱恨情仇
  9. 傅里叶变换(真正的通俗易懂)
  10. Azure Messaging