前面两篇文章《 Android 4.0 ICS SystemUI浅析——SystemUI启动流程》、《 Android 4.0 ICS SystemUI浅析——StatusBar结构分析》SystemUI和StatusBar的冰山一角,那么本文将从代码的角度来分析StatusBar的加载流程。

本文来自:http://blog.csdn.net/yihongyuelan欢迎转载 请务必注明出处!

在《Android 4.0 ICS SystemUI浅析——SystemUI启动流程》中,我们提到了在Phone中,整个StatusBar和NavigationBar都是在/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java的start()方法中,完成初始化并显示到界面上的。因此,我们回到这段代码中查看:

[java] view plaincopy
  1. @Override
  2. publicvoidstart(){
  3. mDisplay=((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  4. .getDefaultDisplay();
  5. mWindowManager=IWindowManager.Stub.asInterface(
  6. ServiceManager.getService(Context.WINDOW_SERVICE));
  7. super.start();//callsmakeStatusBarView()这里会调用父类StatusBar.java中的start()方法
  8. addNavigationBar();//加载导航栏,本文因以StatusBar为主,因此暂不分析NavigationBar
  9. //addIntruderView();
  10. //Lastly,calltotheiconpolicytoinstall/updatealltheicons.
  11. mIconPolicy=newPhoneStatusBarPolicy(mContext);//用于初始化以及更新StatusBar上的icons
  12. }

我们继续跟踪super.start()方法,来到/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java的start()方法中,如代码所示:

[java] view plaincopy
  1. publicvoidstart(){
  2. //Firstsetupourviewsandstuff.首先准备我们需要显示的view以及原材料
  3. //我们先跟踪这里的makeStatusBarView
  4. Viewsb=makeStatusBarView();
  5. //Connectintothestatusbarmanagerservice
  6. StatusBarIconListiconList=newStatusBarIconList();
  7. ArrayList<IBinder>notificationKeys=newArrayList<IBinder>();
  8. ArrayList<StatusBarNotification>notifications=newArrayList<StatusBarNotification>();
  9. ......

通过Open Implementation跳转到makeStatusBarView的实现,因为我们是针对Phone来分析的,因此选择PhoneStatusBar,代码如下:

[java] view plaincopy
  1. //================================================================================
  2. //Constructingtheview
  3. //================================================================================
  4. protectedViewmakeStatusBarView(){
  5. finalContextcontext=mContext;
  6. Resourcesres=context.getResources();
  7. //获取ExpandedView的尺寸
  8. updateDisplaySize();//populatesmDisplayMetrics
  9. //定义icon的大小,缩放率和彼此间距
  10. loadDimens();
  11. mIconSize=res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
  12. //设置ExpandedView的布局
  13. ExpandedViewexpanded=(ExpandedView)View.inflate(context,
  14. R.layout.status_bar_expanded,null);
  15. if(DEBUG){
  16. expanded.setBackgroundColor(0x6000FF80);
  17. }
  18. expanded.mService=this;
  19. //前面已注释,以下三段代码没有用到
  20. mIntruderAlertView=View.inflate(context,R.layout.intruder_alert,null);
  21. mIntruderAlertView.setVisibility(View.GONE);
  22. mIntruderAlertView.setClickable(true);
  23. PhoneStatusBarViewsb;
  24. //这里根据是否是双卡来加载不同的布局文件
  25. if(TelephonyManager.getDefault().isMultiSimEnabled()){
  26. sb=(PhoneStatusBarView)View.inflate(context,
  27. R.layout.msim_status_bar,null);
  28. }else{
  29. sb=(PhoneStatusBarView)View.inflate(context,
  30. R.layout.status_bar,null);
  31. }
  32. sb.mService=this;
  33. mStatusBarView=sb;
  34. //是否显示NavigationBar
  35. try{
  36. booleanshowNav=mWindowManager.hasNavigationBar();
  37. if(showNav){
  38. mNavigationBarView=
  39. (NavigationBarView)View.inflate(context,R.layout.navigation_bar,null);
  40. mNavigationBarView.setDisabledFlags(mDisabled);
  41. }
  42. }catch(RemoteExceptionex){
  43. //nowindowmanager?goodluckwiththat
  44. }
  45. //figureoutwhichpixel-formattouseforthestatusbar.
  46. mPixelFormat=PixelFormat.OPAQUE;
  47. //系统状态图标布局初始化
  48. mStatusIcons=(LinearLayout)sb.findViewById(R.id.statusIcons);
  49. //通知图标布局初始化
  50. mNotificationIcons=(IconMerger)sb.findViewById(R.id.notificationIcons);
  51. mMoreIcon=sb.findViewById(R.id.moreIcon);
  52. mNotificationIcons.setOverflowIndicator(mMoreIcon);
  53. //icons布局初始化,该布局用于装载除开tiker外的所有控件
  54. mIcons=(LinearLayout)sb.findViewById(R.id.icons);
  55. //ticker布局初始化
  56. mTickerView=sb.findViewById(R.id.ticker);
  57. //以上几个重要布局的关系在上一篇文章有详细分析
  58. //以下几段代码是在设置ExpandedView,ExpandedDialog通过加载ExpandedView显示,其中包括了ExpanedView上的清除按钮,
  59. //设置按钮,滚动条,日期显示等等
  60. mExpandedDialog=newExpandedDialog(context);
  61. mExpandedView=expanded;
  62. mPile=(NotificationRowLayout)expanded.findViewById(R.id.latestItems);
  63. mExpandedContents=mPile;//was:expanded.findViewById(R.id.notificationLinearLayout);
  64. mNoNotificationsTitle=(TextView)expanded.findViewById(R.id.noNotificationsTitle);
  65. mNoNotificationsTitle.setVisibility(View.GONE);//disablingfornow
  66. mClearButton=expanded.findViewById(R.id.clear_all_button);
  67. mClearButton.setOnClickListener(mClearButtonListener);
  68. mClearButton.setAlpha(0f);
  69. mClearButton.setEnabled(false);
  70. mDateView=(DateView)expanded.findViewById(R.id.date);
  71. mSettingsButton=expanded.findViewById(R.id.settings_button);
  72. mSettingsButton.setOnClickListener(mSettingsButtonListener);
  73. mScrollView=(ScrollView)expanded.findViewById(R.id.scroll);
  74. //tickerView的初始化
  75. mTicker=newMyTicker(context,sb);
  76. TickerViewtickerView=(TickerView)sb.findViewById(R.id.tickerText);
  77. tickerView.mTicker=mTicker;
  78. //TrackingView初始化
  79. mTrackingView=(TrackingView)View.inflate(context,R.layout.status_bar_tracking,null);
  80. mTrackingView.mService=this;
  81. mCloseView=(CloseDragHandle)mTrackingView.findViewById(R.id.close);
  82. mCloseView.mService=this;
  83. mEdgeBorder=res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
  84. //settheinitalviewvisibility
  85. setAreThereNotifications();
  86. //Othericons
  87. //以下是对其他icons的加载(信号及电量图标的加载)
  88. mLocationController=newLocationController(mContext);//willpostanotification
  89. mBatteryController=newBatteryController(mContext);
  90. mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
  91. SignalClusterViewsignalCluster;
  92. LinearLayoutmSimSignalView;
  93. //根据是否是双卡加载不同的布局文件
  94. if(TelephonyManager.getDefault().isMultiSimEnabled()){
  95. mMSimNetworkController=newMSimNetworkController(mContext);
  96. mSimSignalView=(LinearLayout)sb.findViewById(R.id.msim_signal_cluster);
  97. if(FeatureQuery.FEATURE_ANNUCIATOR_NEW_STATUSBAR_STYLE){
  98. CUMSimSignalClusterViewcuMSimSignalCluster=(CUMSimSignalClusterView)View.inflate(context,R.layout.msim_signal_cluster_view_cu,null);
  99. mSimSignalView.addView(cuMSimSignalCluster);
  100. for(inti=0;i<TelephonyManager.getDefault().getPhoneCount();i++){
  101. mMSimNetworkController.addSignalCluster(cuMSimSignalCluster,i);
  102. }
  103. cuMSimSignalCluster.setNetworkController(mMSimNetworkController);
  104. }else{
  105. MSimSignalClusterViewmSimSignalCluster=(MSimSignalClusterView)View.inflate(context,R.layout.msim_signal_cluster_view,null);
  106. mSimSignalView.addView(mSimSignalCluster);
  107. for(inti=0;i<TelephonyManager.getDefault().getPhoneCount();i++){
  108. mMSimNetworkController.addSignalCluster(mSimSignalCluster,i);
  109. }
  110. mSimSignalCluster.setNetworkController(mMSimNetworkController);
  111. }
  112. }else{
  113. mNetworkController=newNetworkController(mContext);
  114. signalCluster=(SignalClusterView)sb.findViewById(R.id.signal_cluster);
  115. mNetworkController.addSignalCluster(signalCluster);
  116. signalCluster.setNetworkController(mNetworkController);
  117. //finalImageViewwimaxRSSI=
  118. //(ImageView)sb.findViewById(R.id.wimax_signal);
  119. //if(wimaxRSSI!=null){
  120. //mNetworkController.addWimaxIconView(wimaxRSSI);
  121. //}
  122. }
  123. //RecentsPanel
  124. //最近使用界面初始化
  125. mRecentTasksLoader=newRecentTasksLoader(context);
  126. updateRecentsPanel();
  127. //receivebroadcasts
  128. //注册广播监听器
  129. IntentFilterfilter=newIntentFilter();
  130. filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
  131. filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
  132. filter.addAction(Intent.ACTION_SCREEN_ON);
  133. filter.addAction(Intent.ACTION_SCREEN_OFF);
  134. context.registerReceiver(mBroadcastReceiver,filter);
  135. returnsb;
  136. }

通过makeStatusBarView()我们可以看到,在该方法中整个StatusBar根据获取的设备配置信息进行了布局,就好比我们去吃饭,先要安排好座位和桌子,最后才上菜,我们需要加载的icons——就是菜!!:D

我们回到StatusBar.java的start()方法中继续分析,代码如下:

[java] view plaincopy
  1. publicvoidstart(){
  2. //Firstsetupourviewsandstuff.首先准备我们需要显示的view以及原材料
  3. //我们先跟踪这里的makeStatusBarView
  4. Viewsb=makeStatusBarView();
  5. //Connectintothestatusbarmanagerservice
  6. //初始化各个存储器,用于存储各类信息,这些信息通过StatusBarManagerService获取
  7. //iconsList用于存放icons
  8. StatusBarIconListiconList=newStatusBarIconList();
  9. //nodificationKeys保存以Binder为Key的notification
  10. ArrayList<IBinder>notificationKeys=newArrayList<IBinder>();
  11. //保存StatusBarNotification类型的notifications
  12. ArrayList<StatusBarNotification>notifications=newArrayList<StatusBarNotification>();
  13. //mCommandQueue是和IStatusBarService进行交互的IBinder
  14. mCommandQueue=newCommandQueue(this,iconList);
  15. //这里实际上获取的是StatusBarManagerService
  16. mBarService=IStatusBarService.Stub.asInterface(
  17. ServiceManager.getService(Context.STATUS_BAR_SERVICE));
  18. int[]switches=newint[7];
  19. ArrayList<IBinder>binders=newArrayList<IBinder>();
  20. try{
  21. //通过StatusBarManagerService中的registerStatusBar来获取初始设置
  22. mBarService.registerStatusBar(mCommandQueue,iconList,notificationKeys,notifications,
  23. switches,binders);
  24. }catch(RemoteExceptionex){
  25. //Ifthesystemprocessisn'ttherewe'redoomedanyway.
  26. }
  27. //对SystemUI上像Clock、ExpandedView、NavigationBar等进行初始化设置
  28. disable(switches[0]);
  29. setSystemUiVisibility(switches[1]);
  30. topAppWindowChanged(switches[2]!=0);
  31. //StatusBarManagerServicehasabackupofIMEtokenandit'srestoredhere.
  32. setImeWindowStatus(binders.get(0),switches[3],switches[4]);
  33. setHardKeyboardStatus(switches[5]!=0,switches[6]!=0);
  34. //Setuptheinitialiconstate
  35. //icon加载(注:陷阱!!!一不注意就会迷失!!后文详将细分析)
  36. intN=iconList.size();
  37. intviewIndex=0;
  38. for(inti=0;i<N;i++){
  39. StatusBarIconicon=iconList.getIcon(i);
  40. if(icon!=null){
  41. addIcon(iconList.getSlot(i),i,viewIndex,icon);
  42. viewIndex++;
  43. }
  44. }
  45. //Setuptheinitialnotificationstate
  46. //加载notifications
  47. N=notificationKeys.size();
  48. if(N==notifications.size()){
  49. for(inti=0;i<N;i++){
  50. addNotification(notificationKeys.get(i),notifications.get(i));
  51. }
  52. }else{
  53. Log.wtf(TAG,"Notificationlistlengthmismatch:keys="+N
  54. +"notifications="+notifications.size());
  55. }
  56. //Putuptheview
  57. //获取StatusBar高度
  58. finalintheight=getStatusBarHeight();
  59. //设置lp各种属性
  60. finalWindowManager.LayoutParamslp=newWindowManager.LayoutParams(
  61. ViewGroup.LayoutParams.MATCH_PARENT,
  62. height,
  63. WindowManager.LayoutParams.TYPE_STATUS_BAR,
  64. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  65. |WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  66. |WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
  67. //WeuseapixelformatofRGB565forthestatusbartosavememorybandwidthand
  68. //toensurethatthelayercanbehandledbyHWComposer.Onsomedevicesthe
  69. //HWComposerisunabletohandleSW-renderedRGBX_8888layers.
  70. PixelFormat.RGB_565);
  71. //thestatusbarshouldbeinanoverlayifpossible
  72. finalDisplaydefaultDisplay
  73. =((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  74. .getDefaultDisplay();
  75. //enablehardwareaccelerationbasedondevice
  76. //使能硬件加速,不过只有Tablet上才有用
  77. setHardwareAcceleration(lp);
  78. lp.gravity=getStatusBarGravity();
  79. lp.setTitle("StatusBar");
  80. lp.packageName=mContext.getPackageName();
  81. lp.windowAnimations=R.style.Animation_StatusBar;
  82. //在Window上显示StatusBar界面
  83. WindowManagerImpl.getDefault().addView(sb,lp);
  84. mDoNotDisturb=newDoNotDisturb(mContext);
  85. }

可能大多数朋友都还是云里雾里的,先别急,我们只是把大致流程走完了,细节还没有去分析,接下来我们再来看看StatusBar到底是如何加载Icons的。

需要分析如何加载Icons,首先我们根据StatusBar.java中的start()方法可以看到,addIcon()方法完成了Icons的加载。那么通过Open Implementation跳转到PhoneStatusBar.java中的addIcon()方法中(因为我们之前传递的Context是PhoneStatusBar的),代码如下:

[java] view plaincopy
  1. publicvoidaddIcon(Stringslot,intindex,intviewIndex,StatusBarIconicon){
  2. if(SPEW)Slog.d(TAG,"addIconslot="+slot+"index="+index+"viewIndex="+viewIndex
  3. +"icon="+icon);
  4. //初始化StatusBarIconView
  5. StatusBarIconViewview=newStatusBarIconView(mContext,slot,null);
  6. //加载icon
  7. view.set(icon);
  8. //这里的FEATURE_ANNUCIATOR_NEW_STATUSBAR_STYLE是高通自己定义的,默认为true
  9. if(FeatureQuery.FEATURE_ANNUCIATOR_NEW_STATUSBAR_STYLE){
  10. //将设置好icon的StatusBarIconView加载到mStatusIcons布局中
  11. mStatusIcons.addView(view,viewIndex,newLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,mIconSize));
  12. }else{
  13. mStatusIcons.addView(view,viewIndex,newLinearLayout.LayoutParams(mIconSize,mIconSize));
  14. }
  15. }

整个加载过程从表面上看来只有3步,但从本质上来讲远远不止3步。首先,我们先来看看addIcon(...)方法所传递的值;我将这里Slog中的内容打印出来,结果如下:

[plain] view plaincopy
  1. addIconslot=ttyindex=6viewIndex=0icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f02012blevel=0visible=falsenum=0)
  2. addIconslot=cdma_eriindex=11viewIndex=1icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f0200d8level=0visible=falsenum=0)
  3. addIconslot=bluetoothindex=4viewIndex=0icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f020073level=0visible=falsenum=0)
  4. addIconslot=alarm_clockindex=18viewIndex=3icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f020060level=0visible=falsenum=0)
  5. addIconslot=sync_activeindex=2viewIndex=0icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f020129level=0visible=falsenum=0)
  6. addIconslot=sync_failingindex=1viewIndex=0icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f02012alevel=0visible=falsenum=0)
  7. addIconslot=volumeindex=9viewIndex=4icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f0200d6level=0visible=falsenum=0)
  8. addIconslot=headsetindex=16viewIndex=6icon=StatusBarIcon(pkg=com.android.systemuiid=0x7f02000alevel=0visible=falsenum=0)

通过以上log,其中的tty表示语音图标,cdma_eri表示CDMA漫游时显示的图标,其他的大家应该都认识吧,这里就不多讲了。也就是说addIcon()加载的是系统的状态图标。那么我们继续查看它到底是如何加载的,继续查看StatusBarIconView()方法,代码如下:

[java] view plaincopy
  1. publicStatusBarIconView(Contextcontext,Stringslot,Notificationnotification){
  2. super(context);
  3. //获取资源
  4. finalResourcesres=context.getResources();
  5. mSlot=slot;
  6. //画笔初始化
  7. mNumberPain=newPaint();
  8. mNumberPain.setTextAlign(Paint.Align.CENTER);
  9. mNumberPain.setColor(res.getColor(R.drawable.notification_number_text_color));
  10. mNumberPain.setAntiAlias(true);
  11. mNotification=notification;
  12. setContentDescription(notification);
  13. //Wedonotresizeandscalesystemicons(ontheright),onlynotificationicons(onthe
  14. //left).
  15. if(notification!=null){
  16. finalintouterBounds=res.getDimensionPixelSize(R.dimen.status_bar_icon_size);
  17. finalintimageBounds=res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);
  18. finalfloatscale=(float)imageBounds/(float)outerBounds;
  19. setScaleX(scale);
  20. setScaleY(scale);
  21. finalfloatalpha=res.getFraction(R.dimen.status_bar_icon_drawing_alpha,1,1);
  22. setAlpha(alpha);
  23. }
  24. //按图片原来的size居中显示
  25. setScaleType(ImageView.ScaleType.CENTER);
  26. }

通过查看StatusBarIconView的继承关系我们可以发现public class StatusBarIconView extends AnimatedImageView ,继续跟踪可以看到public class AnimatedImageView extends ImageView,也就是说我们的StatusBarIconView其实就是一个封装过的ImageView,这样自然能够盛放我们的Icons了。继续查看StatusBarIconView的set()方法,代码如下:

[java] view plaincopy
  1. /**
  2. *Returnswhetherthesetsucceeded.
  3. */
  4. publicbooleanset(StatusBarIconicon){
  5. finalbooleaniconEquals=mIcon!=null
  6. &&streq(mIcon.iconPackage,icon.iconPackage)
  7. &&mIcon.iconId==icon.iconId;
  8. finalbooleanlevelEquals=iconEquals
  9. &&mIcon.iconLevel==icon.iconLevel;
  10. finalbooleanvisibilityEquals=mIcon!=null
  11. &&mIcon.visible==icon.visible;
  12. finalbooleannumberEquals=mIcon!=null
  13. &&mIcon.number==icon.number;
  14. //将icon的信息copy到mIcon中
  15. mIcon=icon.clone();
  16. setContentDescription(icon.contentDescription);
  17. //获取icon图片并设置到StatusBarIconView中去
  18. if(!iconEquals){
  19. Drawabledrawable=getIcon(icon);
  20. if(drawable==null){
  21. Slog.w(StatusBar.TAG,"Noiconforslot"+mSlot);
  22. returnfalse;
  23. }
  24. setImageDrawable(drawable);
  25. }
  26. //设置需要显示的图片
  27. if(!levelEquals){
  28. setImageLevel(icon.iconLevel);
  29. }
  30. //这个不太清楚具体是干嘛的
  31. if(!numberEquals){
  32. if(icon.number>0&&mContext.getResources().getBoolean(
  33. R.bool.config_statusBarShowNumber)){
  34. if(mNumberBackground==null){
  35. mNumberBackground=getContext().getResources().getDrawable(
  36. R.drawable.ic_notification_overlay);
  37. }
  38. placeNumber();
  39. }else{
  40. mNumberBackground=null;
  41. mNumberText=null;
  42. }
  43. invalidate();
  44. }
  45. //设置是否可见
  46. if(!visibilityEquals){
  47. setVisibility(icon.visible?VISIBLE:GONE);
  48. }
  49. returntrue;
  50. }

到这一步时,我们已经将系统的Icons设置到了mStatusIcons这个mStatusIcons的LinearLayout上了。

但是,事实真的如此吗?这样就完成icons的初始化了吗?还是那句话,实践是检验真理是否正确的唯一标准!那么我们在PhoneStatusBar.java的addIcon()方法中,加入代码,将程序调用栈打印出来,结果一看便知(当然,也可以用Eclipse的远程调试)!加入代码如下:

[java] view plaincopy
  1. StackTraceElementst[]=Thread.currentThread().getStackTrace();
  2. for(inti=0;i<st.length;i++)
  3. System.out.println(i+":"+st[i]);

重新编译SystemUI,push到/system/app目录下,重新启动,打印部分log如下:

[plain] view plaincopy
  1. I/System.out(420):0:dalvik.system.VMStack.getThreadStackTrace(NativeMethod)
  2. I/System.out(420):1:java.lang.Thread.getStackTrace(Thread.java:591)
  3. I/System.out(420):2:com.android.systemui.statusbar.phone.PhoneStatusBar.addIcon(PhoneStatusBar.java:562)
  4. I/System.out(420):3:com.android.systemui.statusbar.CommandQueue$H.handleMessage(CommandQueue.java:212)
  5. I/System.out(420):4:android.os.Handler.dispatchMessage(Handler.java:99)
  6. I/System.out(420):5:android.os.Looper.loop(Looper.java:137)
  7. I/System.out(420):6:android.app.ActivityThread.main(ActivityThread.java:4450)
  8. I/System.out(420):7:java.lang.reflect.Method.invokeNative(NativeMethod)
  9. I/System.out(420):8:java.lang.reflect.Method.invoke(Method.java:511)
  10. I/System.out(420):9:com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:787)
  11. I/System.out(420):10:com.android.internal.os.ZygoteInit.main(ZygoteInit.java:554)
  12. I/System.out(420):11:dalvik.system.NativeStart.main(NativeMethod)
  13. ......

从打印出来的调用栈,我们可以发现,实际条用addIcon()方法的,并不是我们之前分析的StatusBar,而是来自CommandQueue中的handleMessage,这和我们之前的分析大相径庭啊!也就是说实际上addIcon()在CommandQueue中被调用了,那么我们就反过来分析,直接去CommandQueue.java中的handleMessage找到addIcon()。代码如下:

[java] view plaincopy
  1. caseOP_SET_ICON:{
  2. StatusBarIconicon=(StatusBarIcon)msg.obj;
  3. StatusBarIconold=mList.getIcon(index);
  4. if(old==null){
  5. mList.setIcon(index,icon);
  6. mCallbacks.addIcon(mList.getSlot(index),index,viewIndex,icon);
  7. }else{
  8. mList.setIcon(index,icon);
  9. mCallbacks.updateIcon(mList.getSlot(index),index,viewIndex,
  10. old,icon);
  11. }
  12. break;
  13. }

通过case OP_SET_ICON我们可以继续追查,我们可以查到是谁发送的这个message,代码如下:

[java] view plaincopy
  1. publicvoidsetIcon(intindex,StatusBarIconicon){
  2. synchronized(mList){
  3. intwhat=MSG_ICON|index;
  4. mHandler.removeMessages(what);
  5. mHandler.obtainMessage(what,OP_SET_ICON,0,icon.clone()).sendToTarget();
  6. }
  7. }

在CommandQueue中的setIcon发送了这个消息,通过Open Call Hierarchy继续追查,可以看到如图1:

图 1

其中第一个onTransact是一个IBinder,后两者是StatusBarManagerService中的方法。通过方法名称,我们可以大致猜测,setIcon是设置Icon,setIconVisibility是这是Icon是否可见,我们从逻辑上分析,如果是开机第一次调用我们应该先设置Icon然后再考虑设置其可见性(看完后文就会明白了:D)。我们直接跳转到StatusBarManagerService中的setIcon()方法,代码如下:

[java] view plaincopy
  1. publicvoidsetIcon(Stringslot,StringiconPackage,inticonId,inticonLevel,
  2. StringcontentDescription){
  3. enforceStatusBar();
  4. synchronized(mIcons){
  5. intindex=mIcons.getSlotIndex(slot);//这里会检查是否是未定义的icon,如果是则抛出异常
  6. if(index<0){
  7. thrownewSecurityException("invalidstatusbariconslot:"+slot);
  8. }
  9. StatusBarIconicon=newStatusBarIcon(iconPackage,iconId,iconLevel,0,
  10. contentDescription);
  11. //Slog.d(TAG,"setIconslot="+slot+"index="+index+"icon="+icon);
  12. mIcons.setIcon(index,icon);
  13. if(mBar!=null){
  14. try{
  15. mBar.setIcon(index,icon);//这里调用的
  16. }catch(RemoteExceptionex){
  17. }
  18. }
  19. }
  20. }

继续用Open Call Hierarchy查找哪里调用的setIcon方法,如图2:

图 2

这里一看就知道应该是StatusBarManager中的setIcon嘛,继续跟踪,代码如下:

[java] view plaincopy
  1. publicvoidsetIcon(Stringslot,inticonId,inticonLevel,StringcontentDescription){
  2. try{
  3. finalIStatusBarServicesvc=getService();
  4. if(svc!=null){
  5. svc.setIcon(slot,mContext.getPackageName(),iconId,iconLevel,
  6. contentDescription);//这里调用的
  7. }
  8. }catch(RemoteExceptionex){
  9. //systemprocessisdeadanyway.
  10. thrownewRuntimeException(ex);
  11. }
  12. }

目前看来还不是很清晰,继续查找,如图3:

图 3

看到这里请不要头晕,因为真相就在眼前!那么这里应该是谁在调用呢?在本文的一开始,我们就分析了/SourceCode/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java的start()方法,在该方法中有如下代码:

[java] view plaincopy
  1. mIconPolicy=newPhoneStatusBarPolicy(mContext);

该代码不正是调用了PhoneStatusBarPolicy()构造方法吗?直接跟进去,代码如下:

[java] view plaincopy
  1. publicPhoneStatusBarPolicy(Contextcontext){
  2. ......
  3. //listenforbroadcasts
  4. IntentFilterfilter=newIntentFilter();
  5. filter.addAction(Intent.ACTION_ALARM_CHANGED);
  6. filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
  7. filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
  8. filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
  9. filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
  10. filter.addAction(Intent.ACTION_HEADSET_PLUG);//tedadd2021-4-24
  11. filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
  12. filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
  13. filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
  14. mContext.registerReceiver(mIntentReceiver,filter,null,mHandler);
  15. ......
  16. //TTYstatus
  17. mService.setIcon("tty",R.drawable.stat_sys_tty_mode,0,null);//这里调用
  18. mService.setIconVisibility("tty",false);
  19. //CdmaRoamingIndicator,ERI
  20. mService.setIcon("cdma_eri",R.drawable.stat_sys_roaming_cdma_0,0,null);//这里调用
  21. mService.setIconVisibility("cdma_eri",false);
  22. ......
  23. mService.setIcon("bluetooth",bluetoothIcon,0,null);//这里调用
  24. mService.setIconVisibility("bluetooth",mBluetoothEnabled);
  25. //Alarmclock
  26. mService.setIcon("alarm_clock",R.drawable.stat_sys_alarm,0,null);//这里调用
  27. mService.setIconVisibility("alarm_clock",false);
  28. //Syncstate
  29. mService.setIcon("sync_active",R.drawable.stat_sys_sync,0,null);//这里调用
  30. mService.setIcon("sync_failing",R.drawable.stat_sys_sync_error,0,null);//这里调用
  31. mService.setIconVisibility("sync_active",false);
  32. mService.setIconVisibility("sync_failing",false);
  33. //volume
  34. mService.setIcon("volume",R.drawable.stat_sys_ringer_silent,0,null);//这里调用
  35. mService.setIconVisibility("volume",false);
  36. updateVolume();
  37. //headset
  38. mService.setIcon("headset",R.drawable.ckt_headset_with_mic,0,null);//这里调用
  39. mService.setIconVisibility("headset",false);
  40. }

这里也同时验证了我们前面的猜想,即先设置Icon再设置其可见性。也许你会认为本文到这里已经完结了(太长了,一次看不完...o(╯□╰)o),但有个别地方我还得再提一下,Android 启动之后,在SystemServer.java的run方法中,调用了StatusBarManagerService的构造方法,代码如下:

[java] view plaincopy
  1. ......
  2. try{
  3. Slog.i(TAG,"StatusBar");
  4. statusBar=newStatusBarManagerService(context,wm);
  5. ServiceManager.addService(Context.STATUS_BAR_SERVICE,statusBar);
  6. }catch(Throwablee){
  7. reportWtf("startingStatusBarManagerService",e);
  8. }
  9. ......

在StatusBarManagerService的构造方法中,代码如下:

[java] view plaincopy
  1. publicStatusBarManagerService(Contextcontext,WindowManagerServicewindowManager){
  2. mContext=context;
  3. mWindowManager=windowManager;
  4. mWindowManager.setOnHardKeyboardStatusChangeListener(this);
  5. finalResourcesres=context.getResources();
  6. //这里加载了系统预置的所有icon,路径在:Sourcecode/framework/base/core/res/res/values/config.xml中
  7. mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
  8. }

其中Sourcecode/framework/base/core/res/res/values/config.xml中的预置图标定义如下:

[html] view plaincopy
  1. <string-arrayname="config_statusBarIcons">
  2. <item><xliff:gid="id">ime</xliff:g></item>
  3. <item><xliff:gid="id">sync_failing</xliff:g></item>
  4. <item><xliff:gid="id">sync_active</xliff:g></item>
  5. <item><xliff:gid="id">gps</xliff:g></item>
  6. <item><xliff:gid="id">bluetooth</xliff:g></item>
  7. <item><xliff:gid="id">nfc</xliff:g></item>
  8. <item><xliff:gid="id">tty</xliff:g></item>
  9. <item><xliff:gid="id">speakerphone</xliff:g></item>
  10. <item><xliff:gid="id">mute</xliff:g></item>
  11. <item><xliff:gid="id">volume</xliff:g></item>
  12. <item><xliff:gid="id">wifi</xliff:g></item>
  13. <item><xliff:gid="id">cdma_eri</xliff:g></item>
  14. <item><xliff:gid="id">phone_signal_second_sub</xliff:g></item>
  15. <item><xliff:gid="id">data_connection</xliff:g></item>
  16. <item><xliff:gid="id">phone_evdo_signal</xliff:g></item>
  17. <item><xliff:gid="id">phone_signal</xliff:g></item>
  18. <item><xliff:gid="id">headset</xliff:g></item>
  19. <item><xliff:gid="id">battery</xliff:g></item>
  20. <item><xliff:gid="id">alarm_clock</xliff:g></item>
  21. <item><xliff:gid="id">secure</xliff:g></item>
  22. <item><xliff:gid="id">clock</xliff:g></item>
  23. </string-array>

这些东西在后文判断时都会用到,在此记录用以备忘。

Update 20120810 时序图和UML图:

图 4

图 5

小结

本文主要是对StatusBar上面的Icons的加载进行了较为细致的分析,后面将继续分析各个部件的加载以及工作流程。其中自己也走了不少弯路,但还是想记录下载,毕竟没有谁从一开始就能做正确吧!将此总结分享出来希望能给各位一些帮助,同时也给自己一些激励,希望自己后面能做的更好。本文主要是枯燥的代码调用与追踪,但对于需要的朋友,我想还是很有帮助的,后面会将相关的时序图以及UML图贴出来,以供参考。

Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析相关推荐

  1. Android 4.0 ICS SystemUI浅析——StatusBar结构分析

    在上一篇文章<Android 4.0 ICS SystemUI浅析--SystemUI启动流程>中以及提到了SystemUI的组成,本文主要分析其中的StatusBar结构. 1.布局概览 ...

  2. Android 4.0 ICS SystemUI浅析——SystemUI启动流程

    阅读Android 4.0源码也有一段时间了,这次是针对SystemUI的一个学习过程.本文只是对SystemUI分析的一个开始--启动流程的分析,网上有很多关于2.3的SystemUI的分析,可4. ...

  3. Android6.0 keyguard锁屏加载流程分析

    锁屏界面的加载通常在android中有两种方式触发:android系统开机和screenOff(灭屏)后,再screenOn; 先来看 android系统开机时候的锁屏加载流程: 首先在系统启动过程中 ...

  4. Launcher3 桌面加载流程分析

    Launcher3 桌面加载流程分析 主入口Launcher 首先来看Launcher.java的onCreate方法,里面代码很多,只看主流程部分: @Override protected void ...

  5. MultiDex加载流程分析

    MultiDex加载流程分析 只介绍主要流程,multiDex源码已经上传到https://github.com/AlexSmille/google-android-support-source-an ...

  6. linux驱动加载流程分析

    linux驱动加载流程分析 内核是如何加载驱动的,有些是编译到内核里面,有些事编译成ko,让系统自动加载.总的说来,在Linux下可以通过两种方式加载驱动程序:静态加载和动态加载. 静态加载就是把驱动 ...

  7. Spring初始化加载流程分析

    关于Spring框架的介绍,网上有很多非常好的详细的文章,如果在本篇博客中没有了解到自己想要的东西,个人能力有限,只能使用博客记录一下自己目前了解的知识点了! 本篇博客将大致介绍一下Spring框架的 ...

  8. 【Android】恢复出厂后静态壁纸加载流程

    Android静态壁纸功能实现参与的类 /frameworks/base/core/java/android/app/WallpaperManager.java 给开发者提供方法调用.例:setBit ...

  9. Pinpoint Agent加载流程分析

    pinpoint 版本:2.0.3-SNAPSHOT pinpoint利用java agent 特性,提供了一个agent jar包,此jar包会在应用运行之前先运行,agent和应用在同一个进程.p ...

最新文章

  1. Hadoop 之父趣事:用儿子的大象玩偶为大数据项目命名
  2. android 回调函数二:应用实例
  3. Linux下Java开发环境的搭建Tomcat6+jdk6+eclipse3.5.2+Myeclipse9.0+mysql5.1.47
  4. Spring中Bean的概念
  5. 特征筛选10——MIC最大信息系数(有监督筛选)
  6. sql server 视图_轻松搜索SQL Server –搜索目录视图
  7. Java 获取两个日期之间的日期
  8. 中国网和七牛云达成战略合作,携手打造国际化融媒中心
  9. LDA线性判别原理解析<数学推导>
  10. 学小易电脑端——大学生搜题平台
  11. OpenCL “速成”冲刺【第二天】
  12. 一种基于BP神经网络的车牌字符识别方法
  13. linux操作系统安装自定义分区,CentOS linux操作系统安装图文教程
  14. part 8: comparison between OOL and FL
  15. flash flash页面的的全屏展示
  16. 电压压力蕊片_杀鸡取卵~拆SFAIF工业压力仪表取主控芯片ICL7126修液晶电压表头~更多高级仪表果照...
  17. 张牙舞爪的人,往往是很脆弱…
  18. 常用的国内外短网址生成服务
  19. [firefox浏览器插件]记录一下自己经常用的插件
  20. python实验 模块(运用jieba,词云wordcloud)

热门文章

  1. “赤膊贪凉”要不得 多喝蜜水防“秋燥”
  2. 产品经理业务流程图的绘制流程分享
  3. HoverTree开发日志之验证码
  4. 自已撸的一个小程序日历组件
  5. leetcode-453-Minimum Moves to Equal Array Elements
  6. Windows下Apache架站务实
  7. jquery-easyui中表格的行编辑功能
  8. 边界安全技术简要说明
  9. 光纤跳线的交叉连接注意点?
  10. [文章摘录] Is Cloud Computing Really Ready for Prime Time (Computer, 2009)