您的位置:首页 > 产品设计 > UI/UE

Android 5.1 SystemUI 之 Status Bar 加载流程

2015-09-14 23:01 330 查看
So,继上篇我们大致看到了图标的记载的xml布局,不过有人会问,这有个吊用啊? 是的,我也问过我自己,不过还真有点吊用,因为这帮我解决问题了

假象我们要分析锁屏界面的battery_level为什么会在充电的时候才显示,我们现在在布局中找到了对应的ID,那么不就很轻易找到上下文 。

OK,今天我们看下Status Bar的图标是如何加载到布局中的,当然会更加详细。

还是那句话,我们往往是站在巨人的JJ上的,我参考了一篇博客Android
4.0 ICS SystemUI浅析——StatusBar加载流程分析

SystemServer.java -> main() - > run()

if (!disableSystemUI) {
                try {
                    Slog.i(TAG, "Status Bar");
                    statusBar = new StatusBarManagerService(context, wm);
                    ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
                } catch (Throwable e) {
                    reportWtf("starting StatusBarManagerService", e);
                }
    }


我们看到我们获取了StatusBarManagerService,然后把它加入到了ServiceManager中,其中以Context.STATUS_BAR_SERVIER为标志,

为方便了后面的获取

现在看看StatusBarManagerService是什么叼玩意

StatusBarManagerService.java

StatusBarManagerService.java			
    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
        mContext = context;
        mWindowManager = windowManager;
		
        final Resources res = context.getResources();
		//private StatusBarIconList mIcons = new StatusBarIconList();
		//这里把com.android.internal.R.array.config_statusBarIcons 序列化存储
        mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));

        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);

        /// M: DM Lock Feature.
        registerDMLock();
    }


StatusBarManagerService extends IStatusBarService.Stub 就是一个IBinder对象 , Android AIDL 对这个有详细解释,它用来在进程传递,用来实现进程间的通信,如果不熟悉的话,推荐一篇博客Android
aidl Binder框架浅析

mIcons
= new StatusBarIconList(); 这个StatusBarIconList 实现 Parcelable 接口 ,也就是序列化数据 ,用于序列化存储和传递数据

和Java中的Serializable的某些情况下要高效,推荐一篇博客Android中Parcelable接口用法

这里我就不贴出StatusBarIconList的外码了,总之就是存储和读取数据,知道Java序列化的应该就明白。

现在看下com.android.internal.R.array.config_statusBarIcons

<string-array name="config_statusBarIcons">
       <item><xliff:g id="id">ime</xliff:g></item>
       <item><xliff:g id="id">sync_failing</xliff:g></item>
       <item><xliff:g id="id">sync_active</xliff:g></item>
       <item><xliff:g id="id">cast</xliff:g></item>
       <item><xliff:g id="id">hotspot</xliff:g></item>
       <item><xliff:g id="id">location</xliff:g></item>
       <item><xliff:g id="id">bluetooth</xliff:g></item>
       <item><xliff:g id="id">nfc</xliff:g></item>
       <!-- M: Support "SystemUI Headset icon" feature. -->
       <item><xliff:g id="id">headset</xliff:g></item>
       <item><xliff:g id="id">tty</xliff:g></item>
       <item><xliff:g id="id">speakerphone</xliff:g></item>
       <item><xliff:g id="id">zen</xliff:g></item>
       <item><xliff:g id="id">mute</xliff:g></item>
       <item><xliff:g id="id">volume</xliff:g></item>
       <item><xliff:g id="id">wifi</xliff:g></item>
       <item><xliff:g id="id">cdma_eri</xliff:g></item>
       <item><xliff:g id="id">data_connection</xliff:g></item>
       <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
       <item><xliff:g id="id">phone_signal</xliff:g></item>
       <item><xliff:g id="id">battery</xliff:g></item>
       <item><xliff:g id="id">alarm_clock</xliff:g></item>
       <item><xliff:g id="id">secure</xliff:g></item>
       <item><xliff:g id="id">clock</xliff:g></item>
    </string-array>


现在我们来总结下,在SystemServer进程中,维系了一个StatusBarManagerService对象statusBar,而statusBar对象维系了一个StatusBarIconList对象
mIcons,这样就实现了SystemServer 就维系了一个mIcons ,而这个mIcons存储了status bar中图标(String) 和 顺序 。

在上篇文章中,加载status bar的入口就是PhoneStatusBar.java

PhoneStatusBar.java

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

        // Lastly, call to the icon policy to install/update all the icons.
	//注释上看,最终调用这个PhoneStatusBarPolicy.java来安装或者更新Icon
        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);

	}


先看第一步,super.start()调用BaseStatusBar.java

public void start() {
		//...
		mBarService = IStatusBarService.Stub.asInterface(
        ServiceManager.getService(Context.STATUS_BAR_SERVICE));
		//..
		// Connect in to the status bar manager service
        StatusBarIconList iconList = new StatusBarIconList();
		//传入了this,也就是说这个类或者子类实现了接口
        mCommandQueue = new CommandQueue(this, iconList);

        int[] switches = new int[8];
        ArrayList<IBinder> binders = new ArrayList<IBinder>();
        try {
            mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
        } catch (RemoteException ex) {
            // If the system process isn't there we're doomed anyway.
        }
		//...
		// Set up the initial icon state
        int N = iconList.size();
        int viewIndex = 0;
        for (int i=0; i<N; i++) {
            StatusBarIcon icon = iconList.getIcon(i);
            if (icon != null) {
                addIcon(iconList.getSlot(i), i, viewIndex, icon);
                viewIndex++;
            }
        }
				
		
	}


mCommandQueue = new CommandQueue(this,iconList);

CommandQueue extends IStatusBar.Stub 也就是一个IBinder对象,其中也定义了接口,不过接口实现是在PhoneStatusBar.java中

CommandQueue.java

public interface Callbacks {
        public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);
        public void updateIcon(String slot, int index, int viewIndex,
                StatusBarIcon old, StatusBarIcon icon);
        public void removeIcon(String slot, int index, int viewIndex);
        public void disable(int state, boolean animate);
        public void animateExpandNotificationsPanel();
        public void animateCollapsePanels(int flags);
        public void animateExpandSettingsPanel();
        public void setSystemUiVisibility(int vis, int mask);
        public void topAppWindowChanged(boolean visible);
        public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
                boolean showImeSwitcher);
        public void showRecentApps(boolean triggeredFromAltTab);
        public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
        public void toggleRecentApps();
        public void preloadRecentApps();
        public void cancelPreloadRecentApps();
        public void showSearchPanel();
        public void hideSearchPanel();
        public void setWindowState(int window, int state);
        public void buzzBeepBlinked();
        public void notificationLightOff();
        public void notificationLightPulse(int argb, int onMillis, int offMillis);
        public void showScreenPinningRequest();
        /// M: [SystemUI] Support "SIM indicator". @{
        public void showSimIndicator(String businessType);
        public void hideSimIndicator();
        /// @}
        /// M: [SystemUI] Support Smartbook Feature. @{
        public void dispatchStatusBarKeyEvent(KeyEvent event);
        /// @}
        ///M:BMW
        public void showRestoreButton(boolean flag);
    }

    public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
        mCallbacks = callbacks;
        mList = list;
    }


mBarService.registerStatusBar(mCommandQueue,
iconList, switches, binders); 这个mBarService就是StatusBarManagerService对象

StatusBarManagerService.java

@Override
    public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
            int switches[], List<IBinder> binders) {
        enforceStatusBarService();

        Slog.i(TAG, "registerStatusBar bar=" + bar);
        mBar = bar;
        synchronized (mIcons) {
	    //把mIcons的index和icon拷贝到iconList中
            iconList.copyFrom(mIcons);
        }
        synchronized (mLock) {
            switches[0] = gatherDisableActionsLocked(mCurrentUserId);
            switches[1] = mSystemUiVisibility;
            switches[2] = mMenuVisible ? 1 : 0;
            switches[3] = mImeWindowVis;
            switches[4] = mImeBackDisposition;
            switches[5] = mShowImeSwitcher ? 1 : 0;
            binders.add(mImeToken);
        }
    }


iconList.copyFrom(mIcons) 这里把StatusBarManagerService中维系的一个StatusBarIconList对象mIcons 复制给传进来的iconList中

这里只需要注意mBar,这到底有啥用呢,我们知道mBar是IStatusBar的对象 ,也就是CommandQueue

的对象,而CommandQueue的接口的实现方法是在PhoneStatusBar.java(加载StatusBar的界面)实现的,

所以就很好来控制界面 ,只要获取了StatusBarManagerService这个IBinder的相应的Binder驱动(如何获取,相面源码中有) ,发送指令 ->

StatusBarManagerService extends IStatusBarService.Stub 调用CommandQueue extends IStatusBar.Stub的接口

->实现在PhoneStatusBar.java(加载Status Bar的界面)中,就可以达到控制界面的目的

所以这个mBar很关键 ,后面要用到

接着看,BaseStatusBar.java中的Start()中的

// Set up the initial icon state
        int N = iconList.size();
        int viewIndex = 0;
        for (int i=0; i<N; i++) {
            StatusBarIcon icon = iconList.getIcon(i);
            if (icon != null) {
                addIcon(iconList.getSlot(i), i, viewIndex, icon);
                viewIndex++;
            }
        }
看注释这里只是对初始化Icon state,为什么这样说呢,因为我们还是没有设置icon, 我们只设置了图标的名字,但是我们还没有设置对应的图片,所以这里你只要明白是初始化的加载就行了,

因为这部分对我么的分析没太大影响

由于BaseStatusBar implements CommandQueue.CallBacks,但是实现是在子类PhoneStatusBar.java中,所以addIcon也就是在PhoneStatusBar.java中调用

好了,super.start()分析完毕,总结下就是初始化了各个图标的加载到布局,但是,只是加载了名字,而没有图片

现在继续PhoneStatusBar.java中的 mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);

这个类其实就是通过接收BroadcastReceiver来实现更新Icon

PhoneStatusBar.java

public class PhoneStatusBarPolicy {
	private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Xlog.d(TAG, "onReceive:" + action);
            if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
                updateAlarm();
            }
            else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
                updateSyncState(intent);
            }
            else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
                    action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                updateBluetooth();
            }
            else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
                    action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
                updateVolumeZen();
            }
            else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                updateSimState(intent);
            }
            else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
                int currentTtyMode = intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
                    TelecomManager.TTY_MODE_OFF);
                updateTTY(currentTtyMode);
            }
            else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                updateAlarm();
                /// M: [Multi-User] register Alarm intent by user @{
                int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                registerAlarmClockChanged(newUserId, true);
                /// M: [Multi-User] register Alarm intent by user @}
            }
            /// M: [SystemUI] Support "Headset icon". @{
            else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
                updateHeadSet(intent);
            }
            /// @}
        }
    };
	
public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot) {
        mContext = context;
        mCast = cast;
        mHotspot = hotspot;
        mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);

        // listen for broadcasts
        IntentFilter filter = new IntentFilter();
        //filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
        filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        /// M: [SystemUI] Support "Headset icon". @{
        filter.addAction(Intent.ACTION_HEADSET_PLUG);
        /// @}
        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
        /// M: [Multi-User] register Alarm intent by user
        registerAlarmClockChanged(UserHandle.USER_OWNER, false);

        // TTY status
        mService.setIcon(SLOT_TTY,  R.drawable.stat_sys_tty_mode, 0, null);
        mService.setIconVisibility(SLOT_TTY, false);
        /// M: [ALPS01870707] Get the TTY status when power on @{
        int settingsTtyMode = Settings.Secure.getInt(context.getContentResolver(),
                        Settings.Secure.TTY_MODE_ENABLED,
                        TelecomManager.TTY_MODE_OFF);
        updateTTY(settingsTtyMode);
        /// M: [ALPS01870707] Get the TTY status when power on @}

        // Cdma Roaming Indicator, ERI
        mService.setIcon(SLOT_CDMA_ERI, R.drawable.stat_sys_roaming_cdma_0, 0, null);
        mService.setIconVisibility(SLOT_CDMA_ERI, false);

        // bluetooth status
        updateBluetooth();

        // Alarm clock
        mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
        mService.setIconVisibility(SLOT_ALARM_CLOCK, false);

        // Sync state
        mService.setIcon(SLOT_SYNC_ACTIVE, R.drawable.stat_sys_sync, 0, null);
        mService.setIconVisibility(SLOT_SYNC_ACTIVE, false);
        // "sync_failing" is obsolete: b/1297963

        // zen
        mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null);
        mService.setIconVisibility(SLOT_ZEN, false);

        // volume
        mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_vibrate, 0, null);
        mService.setIconVisibility(SLOT_VOLUME, false);
        updateVolumeZen();

        // cast
        // M: Remove CastTile when WFD is not support in quicksetting
        if (mCast != null) {
            mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, null);
            mService.setIconVisibility(SLOT_CAST, false);
            mCast.addCallback(mCastCallback);
        }

        // hotspot
        mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0, null);
        mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled());
        mHotspot.addCallback(mHotspotCallback);
        /// M: [SystemUI] Support "Headset icon". @{
        mService.setIcon(SLOT_HEADSET, R.drawable.stat_sys_headset_with_mic, 0, null);
        mService.setIconVisibility(SLOT_HEADSET, false);
        /// @}
    }
	
}


我们可以看到在构造方法中,注册了一系列的广播,然后通过广播来更新icon, 并在构造方法中初始化了icon以及可见性

private final StatusBarManager mService;

mService.setIcon 和 mService.setIconVisibility 设置了Icon和Visibility , 注意,这里icon出现了

StatusBarManager.java

public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
        try {
            final IStatusBarService svc = getService();
            if (svc != null) {
                svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
                    contentDescription);
            }
        } catch (RemoteException ex) {
            // system process is dead anyway.
            throw new RuntimeException(ex);
        }
    }
	
	private synchronized IStatusBarService getService() {
        if (mService == null) {
            mService = IStatusBarService.Stub.asInterface(
                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
            if (mService == null) {
                Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
            }
        }
        return mService;
    }


这里涉及到AIDL的只是,具体的自己脑补,我就不详细解释了,svc.setIcon说白了就是调用StatusBarManagerService.java中setIcon

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

        synchronized (mIcons) {
            int index = mIcons.getSlotIndex(slot);
            if (index < 0) {
                throw new SecurityException("invalid status bar icon slot: " + slot);
            }

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

            if (mBar != null) {
                try {
		   //加载到布局中
                    mBar.setIcon(index, icon);
                } catch (RemoteException ex) {
                }
            }
        }
    }


StatusBarIconList implements Parcelable的对象mIcons,通过String slot获取了相应的index

然后通过mIcons.setIcon设置了图标的index和icon的值

而这个StatusBarIcon也是implements Parcelable,略....

mBar.setIcon(index, icon); 这个mBar哪里来的,前面已经进来了,其实就是调用了CommandQueue.java的setIcon,最后mCallbacks.addIcon

,而这个mCallbacks的实现函数,就是在PhoneStatusBar.java中,CommandQueue中代码就不贴出来了

PhoneStatusBar.java -> addIcon

public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
                + " icon=" + icon);
        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
        view.set(icon);
		//add in mStatusIcons
        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, mIconSize));
        view = new StatusBarIconView(mContext, slot, null);
        view.set(icon);
        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, mIconSize));
    }


这里布局加载了(指的是status_icon,这里到底指的是什么,参考前面文章),icon名字加载了,可见性加载了,icon的Drawable资源也加载了,是不是完美了,有点晚了,UML图明天补上~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: