Android 4.0 ICS SystemUI浅析——StatusBar加载流程分析

转载至:http://blog.csdn.net/yihongyuelan/article/details/7710584

前面两篇文章《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 plain copy
  1. @Override  
  2. public void start() {  
  3.     mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
  4.             .getDefaultDisplay();  
  5.   
  6.     mWindowManager = IWindowManager.Stub.asInterface(  
  7.             ServiceManager.getService(Context.WINDOW_SERVICE));  
  8.   
  9.     super.start(); // calls makeStatusBarView() 这里会调用父类StatusBar.java中的start()方法  
  10.   
  11.     addNavigationBar();//加载导航栏,本文因以StatusBar为主,因此暂不分析NavigationBar  
  12.   
  13.     //addIntruderView();  
  14.   
  15.     // Lastly, call to the icon policy to install/update all the icons.  
  16.     mIconPolicy = new PhoneStatusBarPolicy(mContext);//用于初始化以及更新StatusBar上的icons  
  17. }  

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

[java]  view plain copy
  1.   public void start() {  
  2.  // First set up our views and stuff.首先准备我们需要显示的view以及原材料  
  3.  //我们先跟踪这里的makeStatusBarView  
  4.  View sb = makeStatusBarView();  
  5.  // Connect in to the status bar manager service  
  6.  StatusBarIconList iconList = new StatusBarIconList();  
  7.  ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();  
  8.  ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();  
  9. ... ...  

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

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

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

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

[java]  view plain copy
  1.      public void start() {  
  2.     // First set up our views and stuff.首先准备我们需要显示的view以及原材料  
  3.     //我们先跟踪这里的makeStatusBarView  
  4.     View sb = makeStatusBarView();  
  5.   
  6.     // Connect in to the status bar manager service  
  7.     //初始化各个存储器,用于存储各类信息,这些信息通过StatusBarManagerService获取  
  8.     //iconsList用于存放icons  
  9.     StatusBarIconList iconList = new StatusBarIconList();  
  10.     //nodificationKeys保存以Binder为Key的notification  
  11.     ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();  
  12.     //保存StatusBarNotification类型的notifications  
  13.     ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();  
  14.     //mCommandQueue是和IStatusBarService进行交互的IBinder  
  15.     mCommandQueue = new CommandQueue(this, iconList);  
  16.     //这里实际上获取的是StatusBarManagerService  
  17.     mBarService = IStatusBarService.Stub.asInterface(  
  18.             ServiceManager.getService(Context.STATUS_BAR_SERVICE));  
  19.     int[] switches = new int[7];  
  20.     ArrayList<IBinder> binders = new ArrayList<IBinder>();  
  21.     try {  
  22.         //通过StatusBarManagerService中的registerStatusBar来获取初始设置  
  23.         mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,  
  24.                 switches, binders);  
  25.     } catch (RemoteException ex) {  
  26.         // If the system process isn't there we're doomed anyway.  
  27.     }  
  28.     //对SystemUI上像Clock、ExpandedView、NavigationBar等进行初始化设置  
  29.     disable(switches[0]);  
  30.     setSystemUiVisibility(switches[1]);  
  31.     topAppWindowChanged(switches[2] != 0);  
  32.     // StatusBarManagerService has a back up of IME token and it's restored here.  
  33.     setImeWindowStatus(binders.get(0), switches[3], switches[4]);  
  34.     setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);  
  35.   
  36.     // Set up the initial icon state  
  37.     //icon加载(注:陷阱!!!一不注意就会迷失!!后文详将细分析)  
  38.     int N = iconList.size();  
  39.     int viewIndex = 0;  
  40.     for (int i=0; i<N; i++) {  
  41.         StatusBarIcon icon = iconList.getIcon(i);  
  42.         if (icon != null) {  
  43.             addIcon(iconList.getSlot(i), i, viewIndex, icon);  
  44.             viewIndex++;  
  45.         }  
  46.     }  
  47.   
  48.     // Set up the initial notification state  
  49.     //加载notifications  
  50.     N = notificationKeys.size();  
  51.     if (N == notifications.size()) {  
  52.         for (int i=0; i<N; i++) {  
  53.             addNotification(notificationKeys.get(i), notifications.get(i));  
  54.         }  
  55.     } else {  
  56.         Log.wtf(TAG, "Notification list length mismatch: keys=" + N  
  57.                 + " notifications=" + notifications.size());  
  58.     }  
  59.   
  60.     // Put up the view  
  61.     //获取StatusBar高度  
  62.     final int height = getStatusBarHeight();  
  63.     //设置lp各种属性  
  64.     final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(  
  65.             ViewGroup.LayoutParams.MATCH_PARENT,  
  66.             height,  
  67.             WindowManager.LayoutParams.TYPE_STATUS_BAR,  
  68.             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE  
  69.                 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING  
  70.                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,  
  71.             // We use a pixel format of RGB565 for the status bar to save memory bandwidth and  
  72.             // to ensure that the layer can be handled by HWComposer.  On some devices the  
  73.             // HWComposer is unable to handle SW-rendered RGBX_8888 layers.  
  74.             PixelFormat.RGB_565);  
  75.       
  76.     // the status bar should be in an overlay if possible  
  77.     final Display defaultDisplay   
  78.         = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
  79.             .getDefaultDisplay();  
  80.     //enable hardware acceleration based on device  
  81.     //使能硬件加速,不过只有Tablet上才有用  
  82.     setHardwareAcceleration(lp);  
  83.     lp.gravity = getStatusBarGravity();  
  84.     lp.setTitle("StatusBar");  
  85.     lp.packageName = mContext.getPackageName();  
  86.     lp.windowAnimations = R.style.Animation_StatusBar;  
  87.     //在Window上显示StatusBar界面  
  88.     WindowManagerImpl.getDefault().addView(sb, lp);  
  89.     mDoNotDisturb = new DoNotDisturb(mContext);  
  90. }  

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

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

[java]  view plain copy
  1. public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {  
  2.     if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex  
  3.             + " icon=" + icon);  
  4.     //初始化StatusBarIconView  
  5.     StatusBarIconView view = new StatusBarIconView(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, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, mIconSize));  
  12.     } else {  
  13.         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));  
  14.     }  
  15. }  

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

[plain]  view plain copy
  1. addIcon slot=tty index=6 viewIndex=0 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f02012b level=0 visible=false num=0 )  
  2. addIcon slot=cdma_eri index=11 viewIndex=1 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f0200d8 level=0 visible=false num=0 )  
  3. addIcon slot=bluetooth index=4 viewIndex=0 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f020073 level=0 visible=false num=0 )  
  4. addIcon slot=alarm_clock index=18 viewIndex=3 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f020060 level=0 visible=false num=0 )  
  5. addIcon slot=sync_active index=2 viewIndex=0 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f020129 level=0 visible=false num=0 )  
  6. addIcon slot=sync_failing index=1 viewIndex=0 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f02012a level=0 visible=false num=0 )  
  7. addIcon slot=volume index=9 viewIndex=4 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f0200d6 level=0 visible=false num=0 )  
  8. addIcon slot=headset index=16 viewIndex=6 icon=StatusBarIcon(pkg=com.android.systemui id=0x7f02000a level=0 visible=false num=0 )  
        通过以上log,其中的tty表示语音图标,cdma_eri表示CDMA漫游时显示的图标,其他的大家应该都认识吧,这里就不多讲了。也就是说addIcon()加载的是系统的状态图标。那么我们继续查看它到底是如何加载的,继续查看StatusBarIconView()方法,代码如下:

[java]  view plain copy
  1. public StatusBarIconView(Context context, String slot, Notification notification) {  
  2.     super(context);  
  3.     //获取资源  
  4.     final Resources res = context.getResources();  
  5.     mSlot = slot;  
  6.     //画笔初始化  
  7.     mNumberPain = new Paint();  
  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.   
  14.     // We do not resize and scale system icons (on the right), only notification icons (on the  
  15.     // left).  
  16.     if (notification != null) {  
  17.         final int outerBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_size);  
  18.         final int imageBounds = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size);  
  19.         final float scale = (float)imageBounds / (float)outerBounds;  
  20.         setScaleX(scale);  
  21.         setScaleY(scale);  
  22.         final float alpha = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 11);  
  23.         setAlpha(alpha);  
  24.     }  
  25.     //按图片原来的size居中显示  
  26.     setScaleType(ImageView.ScaleType.CENTER);  
  27. }  

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

[java]  view plain copy
  1. /** 
  2.  * Returns whether the set succeeded. 
  3.  */  
  4. public boolean set(StatusBarIcon icon) {  
  5.     final boolean iconEquals = mIcon != null  
  6.             && streq(mIcon.iconPackage, icon.iconPackage)  
  7.             && mIcon.iconId == icon.iconId;  
  8.     final boolean levelEquals = iconEquals  
  9.             && mIcon.iconLevel == icon.iconLevel;  
  10.     final boolean visibilityEquals = mIcon != null  
  11.             && mIcon.visible == icon.visible;  
  12.     final boolean numberEquals = 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.         Drawable drawable = getIcon(icon);  
  20.         if (drawable == null) {  
  21.             Slog.w(StatusBar.TAG, "No icon for slot " + mSlot);  
  22.             return false;  
  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.     return true;  
  50. }  

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

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

[java]  view plain copy
  1. StackTraceElement st[]= Thread.currentThread().getStackTrace();  
  2. for(int i=0;i<st.length;i++)  
  3. System.out.println(i+":"+st[i]);  
         重新编译SystemUI,push到/system/app目录下,重新启动,打印部分log如下:

[plain]  view plain copy
  1. I/System.out(420): 0:dalvik.system.VMStack.getThreadStackTrace(Native Method)  
  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(Native Method)  
  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(Native Method)  
  13. ... ...  

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

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

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

[java]  view plain copy
  1. public void setIcon(int index, StatusBarIcon icon) {  
  2.     synchronized (mList) {  
  3.         int what = 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 plain copy
  1. public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,  
  2.         String contentDescription) {  
  3.     enforceStatusBar();  
  4.   
  5.     synchronized (mIcons) {  
  6.         int index = mIcons.getSlotIndex(slot);//这里会检查是否是未定义的icon,如果是则抛出异常  
  7.         if (index < 0) {  
  8.             throw new SecurityException("invalid status bar icon slot: " + slot);  
  9.         }  
  10.   
  11.         StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel, 0,  
  12.                 contentDescription);  
  13.         //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);  
  14.         mIcons.setIcon(index, icon);  
  15.   
  16.         if (mBar != null) {  
  17.             try {  
  18.                 mBar.setIcon(index, icon);//这里调用的  
  19.             } catch (RemoteException ex) {  
  20.             }  
  21.         }  
  22.     }  
  23. }  

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


图 2

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

[java]  view plain copy
  1. public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {  
  2.     try {  
  3.         final IStatusBarService svc = getService();  
  4.         if (svc != null) {  
  5.             svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,  
  6.                 contentDescription);//这里调用的  
  7.         }  
  8.     } catch (RemoteException ex) {  
  9.         // system process is dead anyway.  
  10.         throw new RuntimeException(ex);  
  11.     }  
  12. }  

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


图 3

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

[java]  view plain copy
  1. mIconPolicy = new PhoneStatusBarPolicy(mContext);  
        该代码不正是调用了PhoneStatusBarPolicy()构造方法吗?直接跟进去,代码如下:

[java]  view plain copy
  1.      
  2.     public PhoneStatusBarPolicy(Context context) {  
  3.        ... ...  
  4.        // listen for broadcasts  
  5.        IntentFilter filter = new IntentFilter();  
  6.        filter.addAction(Intent.ACTION_ALARM_CHANGED);  
  7.        filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);  
  8.        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);  
  9.        filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);  
  10.        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
  11.     filter.addAction(Intent.ACTION_HEADSET_PLUG); // ted add 2021-4-24  
  12.        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);  
  13.        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);  
  14.        filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);  
  15.        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);  
  16.        ... ...  
  17.   
  18.        // TTY status  
  19.        mService.setIcon("tty",  R.drawable.stat_sys_tty_mode, 0null);//这里调用  
  20.        mService.setIconVisibility("tty"false);  
  21.   
  22.        // Cdma Roaming Indicator, ERI  
  23.        mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0null);//这里调用  
  24.        mService.setIconVisibility("cdma_eri"false);  
  25.   
  26.        ... ...  
  27.        mService.setIcon("bluetooth", bluetoothIcon, 0null);//这里调用  
  28.        mService.setIconVisibility("bluetooth", mBluetoothEnabled);  
  29.   
  30.        // Alarm clock  
  31.        mService.setIcon("alarm_clock", R.drawable.stat_sys_alarm, 0null);//这里调用  
  32.        mService.setIconVisibility("alarm_clock"false);  
  33.   
  34.        // Sync state  
  35.        mService.setIcon("sync_active", R.drawable.stat_sys_sync, 0null);//这里调用  
  36.        mService.setIcon("sync_failing", R.drawable.stat_sys_sync_error, 0null);//这里调用  
  37.        mService.setIconVisibility("sync_active"false);  
  38.        mService.setIconVisibility("sync_failing"false);  
  39.   
  40.        // volume  
  41.        mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0null);//这里调用  
  42.        mService.setIconVisibility("volume"false);  
  43.        updateVolume();  
  44.       
  45. // headset  
  46. mService.setIcon("headset", R.drawable.ckt_headset_with_mic, 0null);//这里调用  
  47. mService.setIconVisibility("headset"false);  
  48.    }  

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

[java]  view plain copy
  1. ... ...  
  2. try {  
  3.     Slog.i(TAG, "Status Bar");  
  4.     statusBar = new StatusBarManagerService(context, wm);  
  5.     ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);  
  6. catch (Throwable e) {  
  7.     reportWtf("starting StatusBarManagerService", e);  
  8. }  
  9. ... ...  
         在StatusBarManagerService的构造方法中,代码如下:
[java]  view plain copy
  1. public StatusBarManagerService(Context context, WindowManagerService windowManager) {  
  2.     mContext = context;  
  3.     mWindowManager = windowManager;  
  4.     mWindowManager.setOnHardKeyboardStatusChangeListener(this);  
  5.   
  6.     final Resources res = context.getResources();  
  7.     //这里加载了系统预置的所有icon,路径在:Sourcecode/framework/base/core/res/res/values/config.xml中  
  8.     mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));  
  9. }  

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

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

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

Update 20120810 时序图和UML图:


图 4


图 5

       小结

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值