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()
我们看到我们获取了StatusBarManagerService,然后把它加入到了ServiceManager中,其中以Context.STATUS_BAR_SERVIER为标志,
为方便了后面的获取
现在看看StatusBarManagerService是什么叼玩意
StatusBarManagerService.java
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
现在我们来总结下,在SystemServer进程中,维系了一个StatusBarManagerService对象statusBar,而statusBar对象维系了一个StatusBarIconList对象
mIcons,这样就实现了SystemServer 就维系了一个mIcons ,而这个mIcons存储了status bar中图标(String) 和 顺序 。
在上篇文章中,加载status bar的入口就是PhoneStatusBar.java
PhoneStatusBar.java
先看第一步,super.start()调用BaseStatusBar.java
mCommandQueue = new CommandQueue(this,iconList);
CommandQueue extends IStatusBar.Stub 也就是一个IBinder对象,其中也定义了接口,不过接口实现是在PhoneStatusBar.java中
CommandQueue.java
mBarService.registerStatusBar(mCommandQueue,
iconList, switches, binders); 这个mBarService就是StatusBarManagerService对象
StatusBarManagerService.java
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()中的
因为这部分对我么的分析没太大影响
由于BaseStatusBar implements CommandQueue.CallBacks,但是实现是在子类PhoneStatusBar.java中,所以addIcon也就是在PhoneStatusBar.java中调用
好了,super.start()分析完毕,总结下就是初始化了各个图标的加载到布局,但是,只是加载了名字,而没有图片
现在继续PhoneStatusBar.java中的 mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);
这个类其实就是通过接收BroadcastReceiver来实现更新Icon
PhoneStatusBar.java
我们可以看到在构造方法中,注册了一系列的广播,然后通过广播来更新icon, 并在构造方法中初始化了icon以及可见性
private final StatusBarManager mService;
mService.setIcon 和 mService.setIconVisibility 设置了Icon和Visibility , 注意,这里icon出现了
StatusBarManager.java
这里涉及到AIDL的只是,具体的自己脑补,我就不详细解释了,svc.setIcon说白了就是调用StatusBarManagerService.java中setIcon
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
这里布局加载了(指的是status_icon,这里到底指的是什么,参考前面文章),icon名字加载了,可见性加载了,icon的Drawable资源也加载了,是不是完美了,有点晚了,UML图明天补上~
假象我们要分析锁屏界面的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图明天补上~
相关文章推荐
- uva 11995 I Can Guess the Data Structure!
- ZOJ 2747 Windows Message Queue
- 约束,在ios8 没问题,在ios7崩溃的问题,UItextField
- 利用UIScrollView实现几个页面的切换
- UICollectionView简介
- Android Bluetooth4.0(BLE是Bluetooth Low Energy的简称) 官方API
- 限制UITextView中的字数
- ugui脚本添加button相应事件
- UIAlertView使用全解
- Implement Queue using Stacks
- Android UI设计——布局方法
- UITextFiled
- 学习 easyui 之一:easyloader 分析与使用
- Easyui-Datagrid—表头灵活拖动
- uigrid.reposition 多个ITEM不会重叠
- Java中String、StringBuilder和StringBuffer类的区别
- 有关apue.h头文件的疑问???
- UITextField 限制输入长度
- Android 解决非UI线程操作UI的两种办法 :Handler 和 AsynTask
- UILabel