Android SystemUI 状态栏——系统状态图标显示与管理

2017/08/04 SystemUI

状态栏、导航栏整体概括

状态栏和导航栏一般会放在一起说明,他们都属于window中的装饰窗口(DecorView),状态栏主要显示系统图标、应用通知图标以及系统时间等。在systemui中,他们都是通过SystemBars这个内部类来初始化的。下面下来直观的来看一下状态栏的结构。

StatusBar整体布局

StatusBar的整体布局是在status_bar.xml中实现的,整体的结构如下:

可以看到,statusbar主要可以拆分为三部分:status_bar_contents(时间、信号等)、notification_icon_area(应用通知)、system_icon_area(电池、蓝牙等系统图标)

StatusBar图标加载

在前一篇文章,已经介绍而来整体StatusBar的启动流程: Android SystemUI 启动流程 下面我们来看StatusBar具体图标的加载过程。 首先来看PhoneStatusBar的Start函数:

    public void start() {
        ......
        super.start(); // calls createAndAddWindows()
        ......
        addNavigationBar();

        // Lastly, call to the icon policy to install/update all the icons.
        mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCastController,
                mHotspotController, mUserInfoController, mBluetoothController,
                mRotationLockController, mNetworkController.getDataSaverController());
        mIconPolicy.setCurrentUserSetup(mUserSetup);
        ......
    }

super.start()即调用父类BaseStatusBar中的Start函数,主要实现如下:

    public void start() {
      .......
      //获取IStatusBarService的实例
      mBarService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
      .......
        // Connect in to the status bar manager service
        //mCommandQueue是继承自IStatusBar.Stub的CommandQueue实例,它的构造函数有一个CallBack参数,即是this-BaseStatusBar
        mCommandQueue = new CommandQueue(this);

        //这些变量都是注册到IStatusBarService的参数,主要用于获取存储在IStatusBarService的状态栏的相关显示信息。SystemUI很有可能会发生Crash等情况,而状态栏所显示的信息都是从其他应用程序或者系统服务,所以在重启后需要恢复以避免信息的丢失。在IStatusBarService就保存了显示信息的副本,在重新Start的时候,我们就通过这些参数获取恢复。
        //switches存储了一些杂项:禁用功能列表、systemUIVisiblity、输入法窗口是否可见等等
        int[] switches = new int[9];
        ArrayList<IBinder> binders = new ArrayList<IBinder>();
        //iconSlots存储图标意图
        ArrayList<String> iconSlots = new ArrayList<>();
        //icons存储图标资源
        ArrayList<StatusBarIcon> icons = new ArrayList<>();
        Rect fullscreenStackBounds = new Rect();
        Rect dockedStackBounds = new Rect();
        try {
            //将mCommandQueue注册到IStatusBarService中,因此mCommandQueue是BaseStatusBar和IStatusBarService沟通的桥梁
            mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                    fullscreenStackBounds, dockedStackBounds);
        } catch (RemoteException ex) {
            // If the system process isn't there we're doomed anyway.
        }
        ........
        //创建状态栏和导航栏的窗口
        createAndAddWindows();

        //应用IStatusBarService获取到的状态栏信息
        disable(switches[0], switches[6], false /* animate */);//禁用某些功能
        setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
                fullscreenStackBounds, dockedStackBounds);//设置SystemUIVisiblity
        topAppWindowChanged(switches[2] != 0);//设置菜单键的可见性
        // StatusBarManagerService has a back up of IME token and it's restored here.
        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);//根据输入法窗口的可见性调整导航栏的样式

        // Set up the initial icon state
        //设置状态栏图标
        int N = iconSlots.size();
        int viewIndex = 0;
        for (int i=0; i < N; i++) {
            setIcon(iconSlots.get(i), icons.get(i));
        }

    }

在这里,我们看到了一个比较重要的类——IStatusBarService,它是一个系统服务,由ServerThread启动并常驻system_server中。IStatusBarService为其他系统服务定义了一些列的API接口,用于操作状态栏。其实现者为StatusBarManagerService,其他系统服务可以通过该Service,并最终调用到BaseStatusBar,而他们之间通信的桥梁就是CommandQueue实例。具体可通过分析StatusBarManagerService里的registerStatusBar函数:

    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
            Rect fullscreenStackBounds, Rect dockedStackBounds) {
        enforceStatusBarService();

        Slog.i(TAG, "registerStatusBar bar=" + bar);
        mBar = bar;
        synchronized (mIcons) {
            for (String slot : mIcons.keySet()) {
                iconSlots.add(slot);
                iconList.add(mIcons.get(slot));
            }
        }
        synchronized (mLock) {
            switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
            switches[1] = mSystemUiVisibility;
            switches[2] = mMenuVisible ? 1 : 0;
            switches[3] = mImeWindowVis;
            switches[4] = mImeBackDisposition;
            switches[5] = mShowImeSwitcher ? 1 : 0;
            switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
            switches[7] = mFullscreenStackSysUiVisibility;
            switches[8] = mDockedStackSysUiVisibility;
            binders.add(mImeToken);
            fullscreenStackBounds.set(mFullscreenStackBounds);
            dockedStackBounds.set(mDockedStackBounds);
        }
    }

该函数主要做了两件事:

  • 保存CommandQueue的BP端到mBar
  • 将信息副本填充到参数中

而在StatusBarManagerService中,相关接口的实现也主要实现两件事:

  • 保存图标信息到副本
  • 将操作请求发送给BaseStatusBar

如下代码实例:

    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
            String contentDescription) {
        enforceStatusBar();

        synchronized (mIcons) {
            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
                    iconLevel, 0, contentDescription);
            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
            mIcons.put(slot, icon);

            if (mBar != null) {
                try {
                    mBar.setIcon(slot, icon);
                } catch (RemoteException ex) {
                }
            }
        }
    }

所以总结一下StatusBarManagerService的作用:

  • 它是SystemUI状态栏和导航栏在system_server中的代理,其他系统服务可通过它操作状态栏和导航栏
  • 保存状态栏和导航栏所需的信息副本,用于意外退出的恢复

下面来看createAndAddWindows,其直接调用的addStatusBarWindow:

    private void addStatusBarWindow() {
        makeStatusBarView();
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
                mHeadsUpManager);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }

其中makeStatusBarView代码比较长,但是逻辑也很简单。简单的说,该函数就是创建状态栏的控件树,其根布局为super_status_bar.xml,根控件为一个名为StatusBarWindowView的控件,如下图,为主要的子布局和控件:

由于状态栏的窗口不属于任何一个Activity,所以需要通过WindowManager进行窗口的创建添加,即是接下来的逻辑,来看StatusBarWindowManager的add函数:

    public void add(View statusBarView, int barHeight) {

        // Now that the status bar window encompasses the sliding panel and its
        // translucent backdrop, the entire thing is made TRANSLUCENT and is
        // hardware-accelerated.
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,//状态栏的宽度充满整个屏幕
                barHeight,//状态栏的高度,来自getStatusBarHeight方法
                WindowManager.LayoutParams.TYPE_STATUS_BAR,//窗口类型
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE//状态栏不接受按键事件
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);//状态栏的Surface像素格式支持透明度
        mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;//启用硬件加速
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);//通过WindowManager.addView()创建状态栏的窗口,其中mStatusBarView为前面makeStatusBarView方法创建的控件树
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
    }

至此,状态栏控件树的窗口已经创建完成,整个控件树也加载好了,下面就需要具体分析状态栏图标的显示和更新。

回到前面PhoneStatusBar的Start函数,执行super.start后,我们看到创建了一个PhoneStatusBarPolicy实例对象,来看其构造函数:

    public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
            CastController cast, HotspotController hotspot, UserInfoController userInfoController,
            BluetoothController bluetooth, RotationLockController rotationLockController,
            DataSaverController dataSaver) {
        mContext = context;
        mIconController = iconController;
        mCast = cast;
        mHotspot = hotspot;
        mBluetooth = bluetooth;
        mBluetooth.addStateChangedCallback(this);
        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        mUserInfoController = userInfoController;
        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        mRotationLockController = rotationLockController;
        mDataSaver = dataSaver;

        mSlotCast = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_cast"));
        mSlotHotspot = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_hotspot"));
        mSlotBluetooth = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_bluetooth"));
        mSlotTty = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_tty"));
        mSlotZen = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_zen"));
        mSlotVolume = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_volume"));
        mSlotAlarmClock = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_alarm_clock"));
        mSlotManagedProfile = context.getString(
                SystemUiUtil.getIdentifier(context, "string", "status_bar_managed_profile"));
        mSlotRotate = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_rotate"));
        mSlotHeadset = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_headset"));
        mSlotDataSaver = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_data_saver"));
        mSlotNfc = context.getString(SystemUiUtil.getIdentifier(context, "string", "status_bar_nfc"));

        mRotationLockController.addRotationLockControllerCallback(this);

        // listen for broadcasts
        IntentFilter filter = new IntentFilter();
        filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
        filter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

        // listen for user / profile change.
        try {
            ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener, TAG);
        } catch (RemoteException e) {
            // Ignore
        }

        // TTY status
        mIconController.setIcon(mSlotTty,  R.drawable.stat_sys_tty_mode, null);
        mIconController.setIconVisibility(mSlotTty, false);

        // bluetooth status
        updateBluetooth();

        // Alarm clock
        mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
        mIconController.setIconVisibility(mSlotAlarmClock, false);

        // zen
        mIconController.setIcon(mSlotZen, CustomizationItem.STAT_SYS_ZEN_IMPORTANT, null);
        mIconController.setIconVisibility(mSlotZen, false);

        // volume
        mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
        mIconController.setIconVisibility(mSlotVolume, false);
        updateVolumeZen();

        // cast
        mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
        mIconController.setIconVisibility(mSlotCast, false);
        if (!ReflectionMethods.getPlayToExist(context)) {
            mCast.addCallback(mCastCallback);
        }

        // hotspot
        mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
                mContext.getString(R.string.accessibility_status_bar_hotspot));
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled() &&
                !ReflectionMethods.getWifiExtenderState(mWifiManager) &&
                SHOW_HOTSPOT_ICON);
        mHotspot.addCallback(mHotspotCallback);

        // managed profile
        mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
                mContext.getString(R.string.accessibility_managed_profile));
        mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);

        // data saver
        mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
                context.getString(R.string.accessibility_data_saver_on));
        mIconController.setIconVisibility(mSlotDataSaver, false);
        mDataSaver.addListener(this);

        mIconController.setIcon(mSlotNfc, R.drawable.stat_sys_nfc, null);
        NfcAdapter nfcAdapter = null;
        try {
            nfcAdapter = NfcAdapter.getNfcAdapter(context);
        } catch (Exception e) {
            Log.d(TAG, "NfcAdapter.getNfcAdapter Fail:" + e);
        }
        if (nfcAdapter != null) {
            mNfcVisible = (nfcAdapter.getAdapterState() == NfcAdapter.STATE_ON);
        }
        mIconController.setIconVisibility(mSlotNfc, mNfcVisible);
    }

StatusBarManagerService维护了一个准许显示在系统状态区的预定义的意图列表,这个列表由framework/base/coe/res/res/values/config.xml中的字符串数组资源config_statusBarIcons定义,StatusBarManagerService会拒绝使用者提交上述预定义的意图之外的图标。定义如下:

    <string-array name="config_statusBarIcons">
        <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
    </string-array>

config_statusBarIcons里面虽然定义了phone_signal、battery、clock等意图,但在状态栏这几个都不属于系统状态图标区,他们由SystemUI中的SignalCluster、BatterController和Clock单独维护,这个后面会介绍。

下面我们就来看其构造函数的具体实现:

  • 首先是将构造函数中的从PhoneStatusBar中创建的各个参数赋值到PhoneStatusBarPolicy的变量中,其中StatusBarIconController是状态栏图标的管理类,用于显示更新图标,而另外的这些Controller都是为了获取系统图标相关的各种信息状态。
  • 获取定义的意图字串,后续会根据对应的意图设置对应的图标资源。
  • 拦截系统状态相关的广播用于更新系统状态图标。
  • 最后通过StatusBarIconController设置对应意图的图标资源。

StatusBarIconController用于控制状态栏和锁屏上图标的所有内容,不仅仅是系统图标区域,也包括通知图标、信号图标、额外添加的系统图标以及时钟图标。其继承自StatusBarIconList,StatusBarIconList用于保存系统图标意图列表及对应的图标资源,将系统图标意图索引化,在StatusBarIconController中即可利用StatusBarIconList获取对应意图的插槽。

其构造函数中有两个函数,分别是在PhoneStatusBar中创建的StatusBar和KeyguardStatusBar,用于管理状态栏和锁屏上图标的所有内容

接着来看StatusBarIconController如何设置图标的,其调用的是setIcon函数。:

    public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
        int index = getSlotIndex(slot);
        StatusBarIcon icon = getIcon(index);
        if (icon == null) {
            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
            setIcon(slot, icon);
        } else {
            icon.icon = Icon.createWithResource(mContext, resourceId);
            icon.contentDescription = contentDescription;
            handleSet(index, icon);
        }
    }

可以看到在该函数中首先调用StatusBarIconList的getSlotIndex和getIcon函数,最终获取对应意图的StatusBarIcon。

getSlotIndex如果没有获取到对应意图的slot,则会存储到对应的数据结构中,其里面使用的是ArrayList.add(int index, E element)函数,区别与一般的add(E e),这个就是有个位置的概念,特殊位置之后的数据,依次往后移动就是了,所以每次新增的都是保持在0。

StatusBarIcon封装了和图标相关的信息,其中包括PackageName、图标资源ID等。如果获取的StatusBarIcon为空,则创建该StatusBarIcon,并调用setIcon函数添加图标;如果获取的StatusBarIcon不为空,则更新图标资源icon和描述,并调用handleSet更新图标。我们分别来看者两个函数的实现。

setIcon:

    public void setIcon(int index, StatusBarIcon icon) {
        if (icon == null) {
            removeIcon(index);
            return;
        }
        boolean isNew = getIcon(index) == null;
        super.setIcon(index, icon);
        if (isNew) {
            addSystemIcon(index, icon);
        } else {
            handleSet(index, icon);
        }
    }

首先看在StatusBarIconList是否存储该slot的StatusBarIcon,然后更新存储在StatusBarIconList该slot的StatusBarIcon,然后根据是否存储该slot的StatusBarIcon,调用addSystemIcon or handleSet,来看addSystemIcon的实现:

    private void addSystemIcon(int index, StatusBarIcon icon) {
        String slot = getSlot(index);
        int viewIndex = getViewIndex(index);
        boolean blocked = mIconBlacklist.contains(slot);
        StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked);
        view.set(icon);

        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                view.getIconWidth(), mIconSize);
        lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
        mStatusIcons.addView(view, viewIndex, lp);

        view = new StatusBarIconView(mContext, slot, null, blocked);
        view.set(icon);
        mStatusIconsKeyguard.addView(view, viewIndex, lp);
        applyIconTint();
    }

首先获取该slot对应的图标位置viewIndex,然后构建对应slot的StatusBarIconView,其是一个ImageView的子类,也是具体图标的真正显示类。来看其set函数:

   public boolean set(StatusBarIcon icon) {
        ...... 
        if (!iconEquals) {
            if (!updateDrawable(false /* no clear */)) return false;
        }
        ......
    }

其主要调用的是updateDrawable函数,主要实现如下:

    private boolean updateDrawable(boolean withClear) {
        ......
        Drawable drawable = getIcon(mIcon);
        ......
        updateIconScale(drawable);
        setImageDrawable(drawable);
        return true;
    }

可以看到先从StatusBarIcon中获取Drawable图标资源,然后通过updateIconScale缩放成指定尺寸比例,最后通过setImageDrawable设置图标资源到StatusBarIconView。到这里,具体的图标就设置成功了,然后我们回到前面看怎么添加到具体的布局中。 从代码中看,我们先设置自定义布局参数,然后添加到id为statusIcons的LinearLayout中,由前面的控件树,可知statusIcons即为系统图标区域,这样的话,就添加到相应的控件树中了。

系统图标的大小宽度并不是固定的,只能固定高度,对于一个图标资源,UI这边可能设计的图标不符合我们需要设置的大小,但是我们又不能设置图标自适应,因为这样的话,宽度缩小后间距很有可能会不符合我们的设计,所以这里我们先计算好按比例缩小后的宽度,然后赋值给自定义布局宽度参数,这样的话,整块View就符合我们的设计比例了。

最后通过applyIconTint对图标进行颜色渲染,调用的是ImageView的setImageTintList函数。

下面来看如果图标存在时调用handleSet的情况:

    private void handleSet(int index, StatusBarIcon icon) {
        int viewIndex = getViewIndex(index);
        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
        view.set(icon);
        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
        view.set(icon);
        applyIconTint();
    }

相比于addSystemIcon,就是少了创建StatusBarIconView和添加到布局的步骤,而是直接获取然后更新。

这是system UI启动时自己添加的对应意图系统图标,其他外界应用是通过StatusBarManagerService来更新替换,下面来看其实现:

    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
            String contentDescription) {
        enforceStatusBar();

        synchronized (mIcons) {
            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
                    iconLevel, 0, contentDescription);
            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
            mIcons.put(slot, icon);

            if (mBar != null) {
                try {
                    mBar.setIcon(slot, icon);
                } catch (RemoteException ex) {
                }
            }
        }
    }

由前面的介绍可知,是调用systemUI里BaseStatusBar的方法,在BaseStatusBar无setIcon的实现,所以最终实现是在子类PhoneStatusBar中:

    public void setIcon(String slot, StatusBarIcon icon) {
        mIconController.setIcon(slot, icon);
    }

我们看到最终也是调用StatusBarIconController的setIcon函数。

我们来总结一下系统状态图标的流程,如下时序图:

Search

    Table of Contents