Android10.0 StatusBar之状态栏

学习笔记:参考资源 https://zhuanlan.zhihu.com/p/142596265https://blog.csdn.net/Bill_xiao/article/details/108244267

一、StatusBar简介

Statusbar包含导航栏(NavigationBar, 位于左侧、右侧或者底部)和状态栏(StatusBar, 位于顶部, 可下拉)两个部分。

StatusBar顶部状态栏由三部分组成:
  1、最左边的一部分显示运营商,时间,通知图标。
  2、最右边的一部分显示系统图标,它由状态图标(例如 wifi ,bt)和电池图标组成。
  3、中间还有一块区域。


status_bar 结构图.png

  从上图可以比较直观的看出来顶层树是super_status_bar,之后会走两个分支status_bar_container和status_bar_expanded,status_bar_container这个分支主要呈现的是状态栏界面,状态栏细分左边和右边,左边是通知栏,右边是系统功能的状态图标显示,status_bar_expanded这个分支主要呈现的下拉菜单界面,其实下拉菜单中又分快捷图标和短信通知栏。

二、StatuBar的创建

  StatusBar也是继承SystemUI,启动流程和SystemUI一致。并在start的时候添加创建StatusBar相关的view。
  我们从StatusBar的start()方法开始看。
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java

@Override
public void start() {
    
    // 省略部分代码......

    // 创建整个SystemUI视图并添加到WindowManager中
    createAndAddWindows();//这个重点方法,创建相关的视图

    // 省略部分代码......
}


public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {

    // 创建整个SystemUI视图

    makeStatusBarView(result);

    // 把视图添加到Window中

    mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);  //管理状态栏,导航栏、面板的状态切换

    mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}

根据上面代码可知,视图的创建在makeStatusbarView(result)方法中;

     //创建状态栏,即初始化通知图标区域
    protected void makeStatusBarView(RegisterStatusBarResult result) {
        //1.初始化资源文件:导航栏高度 状态栏高度 通知面板位置和高度等
        final Context context = mContext;
        updateDisplaySize(); // populates mDisplayMetrics
        updateResources();
        updateTheme();
        //加载layout文件super_status_bar,mStatusBarWindow
        inflateStatusBarWindow(context);
       
         // ...

        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    // 把控制器中的通知容器加入到状态栏的容器中
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);

                    // ...
                }).getFragmentManager()
                .beginTransaction()
                // CollapsedStatusBarFragment实现了状态栏的添加
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)
                .commit(); 
          // ...
    }

  注意在这里添加了CollapsedStatusBarFragment,这个CollapsedStatusBarFragment就是作为加载状态栏的Fragment。
  在看 CollapsedStatusBarFragment 之前,我们先看看 NotificationIconAreaController(通知图标区域控制器),在 NotificationIconAreaController 的构造函数中会调用如下方法来创建通知图标的容器,即初始化布局。

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java

    protected void initializeNotificationAreaViews(Context context) {
        // ...

        LayoutInflater layoutInflater = LayoutInflater.from(context);
        // 实例化通知图标区域视图
        mNotificationIconArea = inflater.inflate(R.layout.notification_icon_area, null);
        // 这个才是真正存放通知图标的父容器
        mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);

        // ...
    }

  这个类加载的是 R.layout.notification_icon_are.xml 布局。这里先回到 makeStatusBarView(RegisterStatusBarResult result) 方法,我们看 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) ,这句话的作用就是 将 NotificationIconAreaController 自己创建的通知图标容器,加入到状态栏视图中。

  接下来继续看 CollapsedStatusBarFragment,在看之前我们应该带着一个问题去看:这些图标都是动态添加的,是怎么被添加和移除的。
  由 CollapsedStatusBarFragment onCreateView() 可知,加载的布局是 R.layout.status_bar。
  在该布局里包含了这么几个关键的视图:

  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,这部分就是状态栏右边部分,里面包含了电池图标和系统状态图标等等。

由 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) 可知,会进入到CollapsedStatusBarFragment 的 initNotificationIconArea 方法里

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java

    public void initNotificationIconArea(NotificationIconAreaController
            notificationIconAreaController) {
        // 通知图标区域
        ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
        // 这个才是通知图标的父容器
        mNotificationIconAreaInner =
                notificationIconAreaController.getNotificationInnerAreaView();
        //移除自己,再添加自己
        if (mNotificationIconAreaInner.getParent() != null) {
            ((ViewGroup) mNotificationIconAreaInner.getParent())
                    .removeView(mNotificationIconAreaInner);
        }
        // 把通知图标父容器添加到通知图标区域里
        notificationIconArea.addView(mNotificationIconAreaInner);

        // 省略处理中心图标区域的代码

        // 这里其实显示了通知图标区域和中心图标区域
        showNotificationIconArea(false);
    }

三、监听通知的服务端

  当一条新通知发送后,它会存储到通知服务端,也就是NotificationManagerService,那么SystemUI是如何知道新通知来临的。这就需要SystemUI向NotificationManagerService注册一个"服务"(一个Binder)。
  这个"服务"就相当于客户端 SystemUI 在服务端 NotificationManagerService 注册的一个回调。当有通知来临的时候,就会通过这个"服务"通知SystemUI。通过 notificationListener.registerAsSystemService(),进行注册。
  可以来看下这个注册的实现:

 /**
     * Directly register this service with the Notification Manager.
     *
     * <p>Only system services may use this call. It will fail for non-system callers.
     * Apps should ask the user to add their listener in Settings.
     *
     * @param context Context required for accessing resources. Since this service isn't
     *    launched as a real Service when using this method, a context has to be passed in.
     * @param componentName the component that will consume the notification information
     * @param currentUser the user to use as the stream filter
     * @hide
     * @removed
     */
    @SystemApi
    public void registerAsSystemService(Context context, ComponentName componentName,
            int currentUser) throws RemoteException {
        if (mWrapper == null) {  
            // 这就是要注册的Binder,也就一个回调
            mWrapper = new NotificationListenerWrapper();
        }
        mSystemContext = context;
        INotificationManager noMan = getNotificationInterface();
         
        // ...
        
        // 向通知的服务端注册回调
        noMan.registerListener(mWrapper, componentName, currentUser);
    }

SystemUI注册的这个“服务”其实就是:NotificationListenerWithPlugins。

注册成功后,就会回调NotificationListenerWithPlugins中的相关方法,并会附带所有的通知信息。

四、通知图标的显示

当一条新的通知来临的时候,通知的服务端会通过NotificationListenerWithPlugins中的onNotificationPosted()进行回调,而最终会调用NotificationListener中的onNotificationPosted()。

@Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
             // 省略部分代码...
                    // 进行回调
                    handler.onNotificationPosted(sbn, rankingMap);
             // 省略部分代码 ...
        }
    }

这个回调,将调到 NotificationEntryManager 类里的 onNotificationPosted()方法。

public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
            final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
            if (isUpdateToInflatedNotif) {
                updateNotification(sbn, rankingMap);
            } else {
                addNotification(sbn, rankingMap);
            }
        }

这里只关注 addNotification(sbn, rankingMap) ,而它内部时调用 addNotificationInternal() 方法实现的。

 private void addNotificationInternal(
            StatusBarNotification notification,
            RankingMap rankingMap) throws InflationException {
       
        // 省略部分代码 ...

        NotificationEntry entry = mPendingNotifications.get(key);

        // 省略部分代码 ...

        // Construct the expanded view.
        if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
            mNotificationRowBinderLazy.get()
                    .inflateViews(
                            entry,
                            () -> performRemoveNotification(notification, REASON_CANCEL),
                            mInflationCallback);
        }
        // 省略部分代码 ...
    }

首先为通知创建一个NotificationEntry实例,然后再通过NotificationRowBinderImpl中的inflateViews()加载通知视图,绑定通知信息,并在通知栏添加通知视图,以及在状态栏添加通知图标。

到此获取到了数据、图标后面就是更新视图进行显示了,由 NotificationRowBinderImpl#inflateViews → NotificationEntryManager#onAsyncInflationFinished → StatusBarNotificationPresenter#updateNotificationViews

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java

    public void updateNotificationViews() {
        // ...

        //  把通知视图添加到通知面版的通知栏中
        mViewHierarchyManager.updateNotificationViews();

        // 这里不仅仅更新了通知面版的通知视图,也更新了状态栏的通知图标
        mNotificationPanel.updateNotificationViews(reason);

        // ...
    }

最终会调用 NotificationIconAreaController#updateNotificationIcons()进行视图更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容