zoukankan      html  css  js  c++  java
  • Android FrameWork——StatusBar

    Android系统顶上的状态栏是属于FrameWork的东东,由于项目上需要对状态栏进行一些修改调整,我对其作了一个初步研究,写出来大家共享一 下,其实这些早已写了,只是想等研究StatusBar中ExtendsView后再整理一个blog,不过现在已经没有时间了,目前深入研究 Android Binder机制,废话不多少,开始进入statusbar的探索

        1.先从StatusBar的布局文件入手,文件位置位置:frameworks/base/core/res/res/layout/status_bar.xml

        2.我对status_bar.xml布局文件进行分析,画出结构图,以便对StatusBar有一个整体的了解:

        3.com.android.server.status.StatusBarView--statusbar的最顶层view,直观上我们是看不到它的

        4.LinearLayout android:id="@+id/icons" 我们看到的状态栏,系统默认是左边放通知图标notificationIcons,右边放状态图标statusIcons

        --1.通知图标区域: IconMerger android:id="@+id/notificationIcons"

        --2.状态图标区域:LinearLayout android:id="@+id/statusIcons"

        我对status_bar.xml做了修改,notificationIcons的background="#ff0000",statusIcons的background="#0000ff",下面就是现实效果

                                                                  (图1)

        5.那么LinearLayout android:id="@+id/ticker"显示在哪里呢,在正常情况下ticker是不显示的,只有在StatusBarService收到通知 时它才显示,比如SD卡拔出时,我也截了一张图,在前面我已经修改了status_bar.xml并修改它 android:background="#0000ff"大家可以看一下效果:

                                                       (图2)

        6.最后一个是DateView,它是在点击statusbar时才显示的,默认是隐藏的

        7.StatusBar的ui框架就这些,是不是很简单,接下来,我们肯定还有些问题:StatusBarView是如何被创建的呢?上面那个状态时钟图标如何被加载的呢?通知图标如何被加载的?下面我将继续讲解。
        8.StatusBarView创建
            StatusBarView是如何被创建的呢,这得从system_process说起,在system_process构建时,会运行ServerThread.run方法加载各种系统服务,其中有一个服务就是StatusBarService:

           

            看上面调用堆栈图,StatusBarService会调用一个makeStatusBarView的方法,在里面它创建了StatusBarView

        9.状态图标的加载

            我们前面讲过,StatusBarView的子View LinearLayout android:id="@+id/icons"它包含一个通知图标栏,见(图1)中的红色部分,一个状态图标栏:见(图1)中的蓝色部分,但是这个并没 有包含上面的图标,如时间图标(注意右边显示的时间也是一个Text类型的图标,刚开始我也不理解,在status_bar.xml中找了半天没有找到 它),那么这些图标又是怎么样被加载上去的呢?还是看ServerThread.run,在ServerThread.run中调用了

            com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);//装载状态栏的图标

            进入这个函数看它如何实现的,installIcons静态方法直接调用了构造函数StatusBarPolicy去实现这个操作了:

        private StatusBarPolicy(Context context, StatusBarService service) {
           // 构建设置图标...
            // 注册广播接收器,接收各种图标状态变化消息,以此更新状态栏图标      
        }

            代码太多,我就以时间图标为例,进行代码说明,代码在StatusBarPolicy构造函数中:

            //构建时间图标的IconData对象,类型为TEXT
            IconData mClockData = IconData.makeText("clock", "");
                    --IconData.makeText(String slot, CharSequence text) {
                            IconData data = new IconData();
                            data.type = TEXT;
                            data.slot = slot;
                            data.text = text;
                            return data;
                                }
                        //调用addIcon方法添加一个状态图标到状态栏
            IBinder mClockIcon = service.addIcon(mClockData, null);

                    -->IBinder service.addIcon(IconData data, NotificationData n) {
                            int slot;
                            // assert early-on if they using a slot that doesn't exist.
                            if (data != null && n == null) {
                                slot = getRightIconIndex(data.slot);
                                if (slot < 0) {
                                    throw new SecurityException("invalid status bar icon slot: "
                            + (data.slot != null ? "'" + data.slot + "'" : "null"));
                                }
                            } else {
                                slot = -1;
                            }
                            IBinder key = new Binder();
                            addPendingOp(OP_ADD_ICON, key, data, n, -1);
                            return key;
                                }

                        //获取系统时间,并更新时间图标
            updateClock();
                    -->updateClock() {
                            mCalendar.setTimeInMillis(System.currentTimeMillis());
                            mClockData.text = getSmallTime();
                            mService.updateIcon(mClockIcon, mClockData, null);
                                }

            注意代码中我标注为绿色的一个StatusBarService.addPendingOp方法,它会创建一个PendingOp对象op,然后
    设置op.code=OP_ADD_ICON,然后交给StatusBarService.mHandler去处理,我们根据OP_ADD_ICON搜索mHandler.handleMessage方法:

                    //前面函数逻辑我省略了
                    if (doOp) {
                        switch (op.code) {
                            case OP_ADD_ICON:
                            case OP_UPDATE_ICON:
                                performAddUpdateIcon(op.key, op.iconData, op.notificationData);
                                break;

    根据op.code==OP_ADD_ICON,线程会调用performAddUpdateIcon方法:
    void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
    由于这个函数是更新状态图标和通知图标的统一入口函数,它提供了两个入口参数,IconData data,  NotificationData n,当我们添加的是时间图标的时候,n==null:

    void performAddUpdateIcon(IBinder key, IconData data, NotificationData n){      
            // n != null means to add or update notification
            if (n != null) {
                ...
            }
            // to add or update icon ,this also incluce Notification icons
            synchronized (mIconMap) {//mIconMap中缓存了所有显示的和不显示的通知图标和状态图标
                StatusBarIcon icon = mIconMap.get(key);//first to get icon from mIconMap
                if (icon == null) {//if get icon from mIconMap is null,we should to add it
                    // add icon
                    LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
                                                                    //创建一个状态图标
                    icon = new StatusBarIcon(mContext, data, v);
                    mIconMap.put(key, icon);
                    mIconList.add(icon);//mIconList应该是显示的状态图标

                    if (n == null) {//n==null,说明在添加状态图标
                        int slotIndex = getRightIconIndex(data.slot);
                        StatusBarIcon[] rightIcons = mRightIcons;
                        if (rightIcons[slotIndex] == null) {
                            int pos = 0;
                            for (int i=mRightIcons.length-1; i>slotIndex; i--) {
                                StatusBarIcon ic = rightIcons[i];
                                if (ic != null) {
                                    pos++;
                                }
                            }
                            rightIcons[slotIndex] = icon;
                            mStatusIcons.addView(icon.view, pos);//mStatusIcons==LinearLayout android:id="@+id/statusIcons"
                        } else {
                            Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
                            mIconMap.remove(key);
                            mIconList.remove(icon);
                            return ;
                        }
                    } else {// is notification add notification icon
                        ...
                    }
                } else {//icon!=null说明是图标更新
                    if (n == null) {//状态图标更新
                        // right hand side icons -- these don't reorder
                        icon.update(mContext, data);
                    } else {//is notification to update notification icon
                        ...
                    }
                }
            }

            结合我的红色部分的注释,认真看完该函数的代码,相信你已经知道状态图标添加的详细过程。

        10.通知图标的加载

            我以sd卡插入状态栏通知为例进行说明。当sd 插入,代码会执行怎样的逻辑,先看一下调用堆栈:

    StatusBarService.addIcon(IconData, NotificationData) line: 423       
    NotificationManagerService.enqueueNotificationWithTag(String, String, int, Notification, int[]) line: 745       
    NotificationManager.notify(String, int, Notification) line: 110       
    NotificationManager.notify(int, Notification) line: 90       
    StorageNotification.setMediaStorageNotification(int, int, int, boolean, boolean, PendingIntent) line: 478

            调用进入了StatusBarService.addIcon函数,跟前面添加状态图标调用的是同一个函数,只是参数IconData==null,而 NotificationData!=null。接下来执行逻辑差不多,StatusBarService.addPendingOp方法,它会创建一个 PendingOp对象op,然后
    设置op.code=OP_ADD_ICON,然后交给StatusBarService.mHandler去处理,根据op.code==OP_ADD_ICON,线程会调用performAddUpdateIcon方法:

    void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
                            throws StatusBarException {
           //省略无关代码
            if (n != null) {
                    //这里我省略了一段逻辑了,是跟ExpandedView相关,当我们点击statusbar往下拖动会展开一个ExpandedView,这个以后再说
                    //不深入展开了,当收到一个通知,这里判断通知view列表中是否存在这个通知view,若不存在添加,若存在,更新
               
                // 添加要显示的通知到队列中,tickerview会依次显示出来,效果如(图2)
                if (n.tickerText != null && mStatusBarView.getWindowToken() != null
                        && (oldData == null
                            || oldData.tickerText == null
                            || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
                    if (0 == (mDisabled &
                        (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
                        mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);//添加一个通知到tickerview
                    }
                }
            }

            // to add or update icon ,this also incluce Notification icons
            synchronized (mIconMap) {
                StatusBarIcon icon = mIconMap.get(key);//从缓存中查看是否已存在该图标
                if (icon == null) {//if get icon from mIconMap is null,we should to add it
                    // add
                    LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;

                    icon = new StatusBarIcon(mContext, data, v);
                    mIconMap.put(key, icon);
                    mIconList.add(icon);

                    if (n == null) {
                     //状态图标处理
                    } else {// is notification add notification icon
                            //添加左边通知图标,如sd卡,usb图标等,mNotificationIcons=IconMerger android:id="@+id/notificationIcons"
                        int iconIndex = mNotificationData.getIconIndex(n);
                        mNotificationIcons.addView(icon.view, iconIndex);
                    }
                } else {
                    if (n == null) {
                        // right hand side icons -- these don't reorder
                        icon.update(mContext, data);
                    } else {//is notification to update notification icon
                        //更新通知图标
                        // remove old
                        ViewGroup parent = (ViewGroup)icon.view.getParent();
                        parent.removeView(icon.view);
                        // add new
                        icon.update(mContext, data);
                        int iconIndex = mNotificationData.getIconIndex(n);
                        mNotificationIcons.addView(icon.view, iconIndex);
                    }
                }
            }
        }

       11.状态图标更新
            --1.通过广播接收器的方式

    当StatusBarPolicy被构建的时候,会注册一个广播消息接收器mIntentReceiver:
            IntentFilter filter = new IntentFilter();

            // Register for Intent broadcasts for...
            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_TIME_CHANGED);
           ......
            mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
    一旦接收到状态图标变化消息,就会通知StatusBarService去变更状态图标,还是以状态栏的时钟为例:
                if (action.equals(Intent.ACTION_TIME_TICK)) {
                    updateClock();//此处接收时间变化消息
                }
                else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
                    updateClock();//此处接收时间变更消息

                                            -->updateClock()
                                               -->StatusBarService.updateIcon(IBinder key, IconData data, NotificationData n)
                                                    -->addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
                }

    这个跟前面添加状态图标调用是一致的,只是addIcon中addPendingOp(OP_UPDATE_ICON, key, data, n, -1);操作是        OP_UPDATE_ICON,所以,同样,
    方法会执行到performAddUpdateIcon,后面逻辑见前面讲述的状态图标更新。

            --2.通过远程代理方式

            StatusBarManager有一个更新图标的方法: public void updateIcon(IBinder key, String slot, int iconId, int iconLevel),不过StatusBarManager并未把方法公开在sdk中,但是应该有方法可以访问的,
    像launcher就有访问framework中未公开在sdk中的方法,如何实现这里我不作讨论。
    //////////////////StatusBarManager.updateIcon//////////////////////////////
        public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
            try {
                mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
            } catch (RemoteException ex) {
                // system process is dead anyway.
                throw new RuntimeException(ex);
            }
        }
    mService是StatusBarManager的一个成员变量,StatusBarManager被构建的时候被赋值,他是IStatusBar的一个代理对象

        StatusBarManager(Context context) {
            mContext = context;

           //
            mService = IStatusBar.Stub.asInterface(
                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        }

  • 相关阅读:
    LeetCode(Weekly Contest 187)题解
    2021 暑期实习招聘思之未雨绸缪
    LeetCode(Weekly Contest 185)题解
    构建私有的 docker registry
    LeetCode(Weekly Contest 182)题解
    Win10图标变白
    远程连接linux上的mysql
    IDEA复制粘贴html文件打不进war包问题
    Git命令
    数据源收集
  • 原文地址:https://www.cnblogs.com/xiaoxiaoboke/p/2342886.html
Copyright © 2011-2022 走看看