Android KitKat SystemUI之Status Bar

一、启动流程

StatusBar的启动流程是一个View创建,状态设置,添加到WindowManager的过程:

SystemServer通过下面的代码启动SystemUIService:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static final void startSystemUi(Context context) {  
  2.     Intent intent = new Intent();  
  3.     intent.setComponent(new ComponentName("com.android.systemui",  
  4.                 "com.android.systemui.SystemUIService"));  
  5.     //Slog.d(TAG, "Starting service: " + intent);  
  6.     context.startServiceAsUser(intent, UserHandle.OWNER);  
  7. }  
SystemUIService注册在SystemUI package 的AndroidManifest.xml中,SystemUIService只是一个中间类,它负责启动如下的Service:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private final Class<?>[] SERVICES = new Class[] {  
  2.         com.android.systemui.recent.Recents.class,  
  3.         com.android.systemui.statusbar.SystemBars.class,  
  4.         com.android.systemui.usb.StorageNotification.class,  
  5.         com.android.systemui.power.PowerUI.class,  
  6.         com.android.systemui.media.RingtonePlayer.class,  
  7.         com.android.systemui.settings.SettingsUI.class,  
  8.     };  
从这儿我们可以看到Recent task, system bar, storage notification, PowerUI, Ringtone Player, SettingsUI都属于SystemUI的范畴。

那SystemBars是怎么启动了PhoneStatusBar的呢?分析如下的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void createStatusBarFromConfig() {  
  2.     if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");  
  3.     final String clsName = mContext.getString(R.string.config_statusBarComponent);  
  4.     if (clsName == null || clsName.length() == 0) {  
  5.         throw andLog("No status bar component configured"null);  
  6.     }  
  7.     Class<?> cls = null;  
  8.     try {  
  9.         cls = mContext.getClassLoader().loadClass(clsName);  
  10.     } catch (Throwable t) {  
  11.         throw andLog("Error loading status bar component: " + clsName, t);  
  12.     }  
  13.     try {  
  14.         mStatusBar = (BaseStatusBar) cls.newInstance();  
  15.     } catch (Throwable t) {  
  16.         throw andLog("Error creating status bar component: " + clsName, t);  
  17.     }  
  18.     mStatusBar.mContext = mContext;  
  19.     mStatusBar.mComponents = mComponents;  
  20.     mStatusBar.start();  
  21.     if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());  
  22. }  
它是从一个配置文件获取了一个BaseStatusBar类文件名,并创建

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>  

Android4.2中的statusBar包括:TabletStatusBarPhoneStatusBar, TvStatusBar,而在4.4PhoneTablet合并为PhoneStatusBar

我们再看一下PhoneStatusBar的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void start() {  
  2.     mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))  
  3.             .getDefaultDisplay();  
  4.     updateDisplaySize();//<span style="font-family:宋体;">JPH:</span>获取屏幕大小  
  5.   
  6.     super.start(); // calls createAndAddWindows()  
  7.   
  8.     addNavigationBar();  
  9.   
  10.     // Lastly, call to the icon policy to install/update all the icons.  
  11.     mIconPolicy = new PhoneStatusBarPolicy(mContext);  
  12.     <span style="font-family:宋体;">//JPH:在Status Bar上显示Notification</span>  
  13.     mHeadsUpObserver.onChange(true); // set up  
  14.     if (ENABLE_HEADS_UP) {  
  15.         mContext.getContentResolver().registerContentObserver(  
  16.                 Settings.Global.getUriFor(SETTING_HEADS_UP), true,  
  17.                 mHeadsUpObserver);  
  18.     }  
  19. }  
这个方法最重要的是调用了父类的start方法,其实父类就是BaseStatusBar,另外添加了Navigation Bar。

我们看一下BaseStatusBar的start方法:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void start() {  
  2.     mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);  
  3.     mWindowManagerService = WindowManagerGlobal.getWindowManagerService();  
  4.     mDisplay = mWindowManager.getDefaultDisplay();  
  5.   
  6.     mDreamManager = IDreamManager.Stub.asInterface(  
  7.             ServiceManager.checkService(DreamService.DREAM_SERVICE));  
  8.     mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
  9.   
  10.     mProvisioningObserver.onChange(false); // set up  
  11.     mContext.getContentResolver().registerContentObserver(  
  12.             Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,  
  13.             mProvisioningObserver);  
  14.   
  15.     mBarService = IStatusBarService.Stub.asInterface(  
  16.             ServiceManager.getService(Context.STATUS_BAR_SERVICE));  
  17.   
  18.     mRecents = getComponent(RecentsComponent.class);  
  19.   
  20.     mLocale = mContext.getResources().getConfiguration().locale;  
  21.     mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);  
  22.   
  23.     // Connect in to the status bar manager service  
  24.     StatusBarIconList iconList = new StatusBarIconList();  
  25.     ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();  
  26.     ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();  
  27.     mCommandQueue = new CommandQueue(this, iconList);  
  28.   
  29.     int[] switches = new int[7];  
  30.     ArrayList<IBinder> binders = new ArrayList<IBinder>();  
  31.     try {  
  32.         mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,  
  33.                 switches, binders);  
  34.     } catch (RemoteException ex) {  
  35.         // If the system process isn't there we're doomed anyway.  
  36.     }  
  37.   
  38.     createAndAddWindows();  
  39.   
  40.     disable(switches[0]);  
  41.     setSystemUiVisibility(switches[1], 0xffffffff);  
  42.     topAppWindowChanged(switches[2] != 0);  
  43.     // StatusBarManagerService has a back up of IME token and it's restored here.  
  44.     setImeWindowStatus(binders.get(0), switches[3], switches[4]);  
  45.     setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);  
  46.   
  47.     // Set up the initial icon state  
  48.     int N = iconList.size();  
  49.     int viewIndex = 0;  
  50.     for (int i=0; i<N; i++) {  
  51.         StatusBarIcon icon = iconList.getIcon(i);  
  52.         if (icon != null) {  
  53.             addIcon(iconList.getSlot(i), i, viewIndex, icon);  
  54.             viewIndex++;  
  55.         }  
  56.     }  
  57.   
  58.     // Set up the initial notification state  
  59.     N = notificationKeys.size();  
  60.     if (N == notifications.size()) {  
  61.         for (int i=0; i<N; i++) {  
  62.             addNotification(notificationKeys.get(i), notifications.get(i));  
  63.         }  
  64.     } else {  
  65.         Log.wtf(TAG, "Notification list length mismatch: keys=" + N  
  66.                 + " notifications=" + notifications.size());  
  67.     }  
  68.   
  69.     if (DEBUG) {  
  70.         Log.d(TAG, String.format(  
  71.                 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",  
  72.                iconList.size(),  
  73.                switches[0],  
  74.                switches[1],  
  75.                switches[2],  
  76.                switches[3]  
  77.                ));  
  78.     }  
  79.   
  80.     mCurrentUserId = ActivityManager.getCurrentUser();  
  81.   
  82.     IntentFilter filter = new IntentFilter();  
  83.     filter.addAction(Intent.ACTION_USER_SWITCHED);  
  84.     mContext.registerReceiver(mBroadcastReceiver, filter);  
  85. }  

在这个方法中调用了registerStatusBar到StatusManagerService,用来获取status Icon,status Bar的部分功能关闭(clock,Expand panel, notification icon,还有Navigation bar上的返回键,Home键等等),systemUI 显示配置,topAppWindowChange,这些都是StatusManager对其它程序提供的操作接口,在以下章节我们再来分析status bar icon的加载与显示。

现在看一下PhoneStatusBar下的makeStatusBarView。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. protected PhoneStatusBarView makeStatusBarView() {  
  2.     final Context context = mContext;  
  3.   
  4.     Resources res = context.getResources();  
  5.   
  6.     updateDisplaySize(); // populates mDisplayMetrics  
  7.     loadDimens();  
  8.   
  9.     mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);  
  10.   
  11.     if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {  
  12.         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,  
  13.                 R.layout.msim_super_status_bar, null);  
  14.     } else {  
  15.         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,  
  16.                 R.layout.super_status_bar, null);  
  17.     }  
  18.     mStatusBarWindow.mService = this;  
  19.     mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {  
  20.         @Override  
  21.         public boolean onTouch(View v, MotionEvent event) {  
  22.             checkUserAutohide(v, event);  
  23.             if (event.getAction() == MotionEvent.ACTION_DOWN) {  
  24.                 if (mExpandedVisible) {  
  25.                     animateCollapsePanels();  
  26.                 }  
  27.             }  
  28.             return mStatusBarWindow.onTouchEvent(event);  
  29.         }});  
  30.   
  31.     if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {  
  32.         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(  
  33.                 R.id.msim_status_bar);  
  34.     } else {  
  35.         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);  
  36.     }  
  37.     mStatusBarView.setBar(this);  
  38.   
  39.     PanelHolder holder;  
  40.     if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {  
  41.         holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.msim_panel_holder);  
  42.     } else {  
  43.         holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);  
  44.     }  
  45.     mStatusBarView.setPanelHolder(holder);  
  46.   
  47.     mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);  
  48.     mNotificationPanel.setStatusBar(this);  
  49.     mNotificationPanelIsFullScreenWidth =  
  50.         (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);  
  51.   
  52.     // make the header non-responsive to clicks  
  53.     mNotificationPanel.findViewById(R.id.header).setOnTouchListener(  
  54.             new View.OnTouchListener() {  
  55.                 @Override  
  56.                 public boolean onTouch(View v, MotionEvent event) {  
  57.                     return true// e eats everything  
  58.                 }  
  59.             });  
  60.   
  61.     if (!ActivityManager.isHighEndGfx()) {  
  62.         mStatusBarWindow.setBackground(null);  
  63.         mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(  
  64.                 R.color.notification_panel_solid_background)));  
  65.     }  
  66.     if (ENABLE_HEADS_UP) {  
  67.         mHeadsUpNotificationView =  
  68.                 (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);  
  69.         mHeadsUpNotificationView.setVisibility(View.GONE);  
  70.         mHeadsUpNotificationView.setBar(this);  
  71.     }  
  72.     if (MULTIUSER_DEBUG) {  
  73.         mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);  
  74.         mNotificationPanelDebugText.setVisibility(View.VISIBLE);  
  75.     }  
  76.   
  77.     updateShowSearchHoldoff();  
  78.   
  79.     try {  
  80.         boolean showNav = mWindowManagerService.hasNavigationBar();  
  81.         if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);  
  82.         if (showNav) {  
  83.             mNavigationBarView =  
  84.                 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);  
  85.   
  86.             mNavigationBarView.setDisabledFlags(mDisabled);  
  87.             mNavigationBarView.setBar(this);  
  88.             mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {  
  89.                 @Override  
  90.                 public boolean onTouch(View v, MotionEvent event) {  
  91.                     checkUserAutohide(v, event);  
  92.                     return false;  
  93.                 }});  
  94.         }  
  95.     } catch (RemoteException ex) {  
  96.         // no window manager? good luck with that  
  97.     }  
  98.   
  99.     // figure out which pixel-format to use for the status bar.  
  100.     mPixelFormat = PixelFormat.OPAQUE;  
  101.   
  102.     mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);  
  103.     mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);  
  104.     mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);  
  105.     mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);  
  106.     mNotificationIcons.setOverflowIndicator(mMoreIcon);  
  107.     mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);  
  108.     mTickerView = mStatusBarView.findViewById(R.id.ticker);  
  109.   
  110.     mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);  
  111.     mPile.setLayoutTransitionsEnabled(false);  
  112.     mPile.setLongPressListener(getNotificationLongClicker());  
  113.     mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);  
  114.   
  115.     mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);  
  116.   
  117.     mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);  
  118.     mClearButton.setOnClickListener(mClearButtonListener);  
  119.     mClearButton.setAlpha(0f);  
  120.     mClearButton.setVisibility(View.INVISIBLE);  
  121.     mClearButton.setEnabled(false);  
  122.     mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);  
  123.   
  124.     mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel);  
  125.     mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel);  
  126.   
  127.     mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);  
  128.     if (mDateTimeView != null) {  
  129.         mDateTimeView.setOnClickListener(mClockClickListener);  
  130.         mDateTimeView.setEnabled(true);  
  131.     }  
  132.   
  133.     mSettingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);  
  134.     if (mSettingsButton != null) {  
  135.         mSettingsButton.setOnClickListener(mSettingsButtonListener);  
  136.         if (mHasSettingsPanel) {  
  137.             if (mStatusBarView.hasFullWidthNotifications()) {  
  138.                 // the settings panel is hiding behind this button  
  139.                 mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);  
  140.                 mSettingsButton.setVisibility(View.VISIBLE);  
  141.             } else {  
  142.                 // there is a settings panel, but it's on the other side of the (large) screen  
  143.                 final View buttonHolder = mStatusBarWindow.findViewById(  
  144.                         R.id.settings_button_holder);  
  145.                 if (buttonHolder != null) {  
  146.                     buttonHolder.setVisibility(View.GONE);  
  147.                 }  
  148.             }  
  149.         } else {  
  150.             // no settings panel, go straight to settings  
  151.             mSettingsButton.setVisibility(View.VISIBLE);  
  152.             mSettingsButton.setImageResource(R.drawable.ic_notify_settings);  
  153.         }  
  154.     }  
  155.     if (mHasFlipSettings) {  
  156.         mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button);  
  157.         if (mNotificationButton != null) {  
  158.             mNotificationButton.setOnClickListener(mNotificationButtonListener);  
  159.         }  
  160.     }  
  161.   
  162.     mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);  
  163.     mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns  
  164.     if (!mNotificationPanelIsFullScreenWidth) {  
  165.         mScrollView.setSystemUiVisibility(  
  166.                 View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER |  
  167.                 View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |  
  168.                 View.STATUS_BAR_DISABLE_CLOCK);  
  169.     }  
  170.   
  171.     mTicker = new MyTicker(context, mStatusBarView);  
  172.   
  173.     TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);  
  174.     tickerView.mTicker = mTicker;  
  175.   
  176.     mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);  
  177.   
  178.     // set the inital view visibility  
  179.     setAreThereNotifications();  
  180.   
  181.     // Other icons  
  182.     mLocationController = new LocationController(mContext); // will post a notification  
  183.     mBatteryController = new BatteryController(mContext);  
  184.     mBluetoothController = new BluetoothController(mContext);  
  185.     mRotationLockController = new RotationLockController(mContext);  
  186.   
  187.     if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {  
  188.         mMSimNetworkController = new MSimNetworkController(mContext);  
  189.         MSimSignalClusterView mSimSignalCluster = (MSimSignalClusterView)  
  190.           mStatusBarView.findViewById(R.id.msim_signal_cluster);  
  191.         for (int i=0; i < MSimTelephonyManager.getDefault().getPhoneCount(); i++) {  
  192.             mMSimNetworkController.addSignalCluster(mSimSignalCluster, i);  
  193.         }  
  194.         mSimSignalCluster.setNetworkController(mMSimNetworkController);  
  195.   
  196.         mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(  
  197.                                                       R.id.emergency_calls_only);  
  198.         if (mEmergencyCallLabel != null) {  
  199.             mMSimNetworkController.addEmergencyLabelView(mEmergencyCallLabel);  
  200.             mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {  
  201.                 public void onClick(View v) { }});  
  202.             mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {  
  203.                 @Override  
  204.                 public void onLayoutChange(View v, int left, int top, int right, int bottom,  
  205.                         int oldLeft, int oldTop, int oldRight, int oldBottom) {  
  206.                     updateCarrierLabelVisibility(false);  
  207.                 }});  
  208.         }  
  209.   
  210.         mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);  
  211.         mSubsLabel = (TextView)mStatusBarWindow.findViewById(R.id.subs_label);  
  212.         mShowCarrierInPanel = (mCarrierLabel != null);  
  213.   
  214.         if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" +  
  215.                                 mShowCarrierInPanel + "operator label=" + mSubsLabel);  
  216.         if (mShowCarrierInPanel) {  
  217.             mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);  
  218.   
  219.             // for mobile devices, we always show mobile connection info here (SPN/PLMN)  
  220.             // for other devices, we show whatever network is connected  
  221.             if (mMSimNetworkController.hasMobileDataFeature()) {  
  222.                 mMSimNetworkController.addMobileLabelView(mCarrierLabel);  
  223.             } else {  
  224.                 mMSimNetworkController.addCombinedLabelView(mCarrierLabel);  
  225.             }  
  226.             mSubsLabel.setVisibility(View.VISIBLE);  
  227.             mMSimNetworkController.addSubsLabelView(mSubsLabel);  
  228.             // set up the dynamic hide/show of the label  
  229.             mPile.setOnSizeChangedListener(new OnSizeChangedListener() {  
  230.                 @Override  
  231.                 public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {  
  232.                     updateCarrierLabelVisibility(false);  
  233.                 }  
  234.             });  
  235.         }  
  236.     } else {  
  237.         mNetworkController = new NetworkController(mContext);  
  238.         final SignalClusterView signalCluster =  
  239.             (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);  
  240.   
  241.         mNetworkController.addSignalCluster(signalCluster);  
  242.         signalCluster.setNetworkController(mNetworkController);  
  243.   
  244.         final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();  
  245.         if (isAPhone) {  
  246.             mEmergencyCallLabel = (TextView)mStatusBarWindow.findViewById(  
  247.                                                    R.id.emergency_calls_only);  
  248.             if (mEmergencyCallLabel != null) {  
  249.                 mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);  
  250.                 mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {  
  251.                     public void onClick(View v) { }});  
  252.                 mEmergencyCallLabel.addOnLayoutChangeListener(  
  253.                                                 new View.OnLayoutChangeListener() {  
  254.                     @Override  
  255.                     public void onLayoutChange(View v, int left, int top, int right, int bottom,  
  256.                         int oldLeft, int oldTop, int oldRight, int oldBottom) {  
  257.                         updateCarrierLabelVisibility(false);  
  258.                     }});  
  259.             }  
  260.         }  
  261.   
  262.         mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);  
  263.         mShowCarrierInPanel = (mCarrierLabel != null);  
  264.         if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" +  
  265.                                                               mShowCarrierInPanel);  
  266.         if (mShowCarrierInPanel) {  
  267.             mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);  
  268.   
  269.             // for mobile devices, we always show mobile connection info here (SPN/PLMN)  
  270.             // for other devices, we show whatever network is connected  
  271.             if (mNetworkController.hasMobileDataFeature()) {  
  272.                 mNetworkController.addMobileLabelView(mCarrierLabel);  
  273.             } else {  
  274.                 mNetworkController.addCombinedLabelView(mCarrierLabel);  
  275.             }  
  276.   
  277.             // set up the dynamic hide/show of the label  
  278.             mPile.setOnSizeChangedListener(new OnSizeChangedListener() {  
  279.                 @Override  
  280.                 public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {  
  281.                     updateCarrierLabelVisibility(false);  
  282.                 }  
  283.             });  
  284.         }  
  285.     }  
  286.   
  287.     // Quick Settings (where available, some restrictions apply)  
  288.     if (mHasSettingsPanel) {  
  289.         // first, figure out where quick settings should be inflated  
  290.         final View settings_stub;  
  291.         if (mHasFlipSettings) {  
  292.             // a version of quick settings that flips around behind the notifications  
  293.             settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub);  
  294.             if (settings_stub != null) {  
  295.                 mFlipSettingsView = ((ViewStub)settings_stub).inflate();  
  296.                 mFlipSettingsView.setVisibility(View.GONE);  
  297.                 mFlipSettingsView.setVerticalScrollBarEnabled(false);  
  298.             }  
  299.         } else {  
  300.             // full quick settings panel  
  301.             settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub);  
  302.             if (settings_stub != null) {  
  303.                 mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate();  
  304.             } else {  
  305.                 mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel);  
  306.             }  
  307.   
  308.             if (mSettingsPanel != null) {  
  309.                 if (!ActivityManager.isHighEndGfx()) {  
  310.                     mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor(  
  311.                             R.color.notification_panel_solid_background)));  
  312.                 }  
  313.             }  
  314.         }  
  315.   
  316.         // wherever you find it, Quick Settings needs a container to survive  
  317.         mSettingsContainer = (QuickSettingsContainerView)  
  318.                 mStatusBarWindow.findViewById(R.id.quick_settings_container);  
  319.         if (mSettingsContainer != null) {  
  320.             mQS = new QuickSettings(mContext, mSettingsContainer);  
  321.             if (!mNotificationPanelIsFullScreenWidth) {  
  322.                 mSettingsContainer.setSystemUiVisibility(  
  323.                         View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER  
  324.                         | View.STATUS_BAR_DISABLE_SYSTEM_INFO);  
  325.             }  
  326.             if (mSettingsPanel != null) {  
  327.                 mSettingsPanel.setQuickSettings(mQS);  
  328.             }  
  329.             mQS.setService(this);  
  330.             mQS.setBar(mStatusBarView);  
  331.             if (MSimTelephonyManager.getDefault().isMultiSimEnabled()) {  
  332.                 mQS.setup(mMSimNetworkController, mBluetoothController, mBatteryController,  
  333.                         mLocationController, mRotationLockController);  
  334.             } else {  
  335.                 mQS.setup(mNetworkController, mBluetoothController, mBatteryController,  
  336.                         mLocationController, mRotationLockController);  
  337.             }  
  338.         } else {  
  339.             mQS = null// fly away, be free  
  340.         }  
  341.     }  
  342.   
  343.     PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);  
  344.     mBroadcastReceiver.onReceive(mContext,  
  345.             new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));  
  346.   
  347.     // receive broadcasts  
  348.     IntentFilter filter = new IntentFilter();  
  349.     filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);  
  350.     filter.addAction(Intent.ACTION_SCREEN_OFF);  
  351.     filter.addAction(Intent.ACTION_SCREEN_ON);  
  352.     filter.addAction(ACTION_DEMO);  
  353.     context.registerReceiver(mBroadcastReceiver, filter);  
  354.   
  355.     // listen for USER_SETUP_COMPLETE setting (per-user)  
  356.     resetUserSetupObserver();  
  357.   
  358.     return mStatusBarView;  
  359. }  
其实这个类就是创建了一个View最后在添加WindowManager中,为了便于理解这个create的类的关系,特画了一张类图。


需要特别说明的是整个StatusBar为StatusBarView 和 Panel两部分组成,当触发显示Panel时statusBar隐藏,当收起Panel时StatusBar显示。

而Panel由Header和NotificationPanel/FlipSetting 组成,FlipSetting是一些快捷设置的入口,如:Wifi, Bluetooth,Backlight等等,而上图的SettingsPanelView很多项目中都没有使用。

整个status bar 的启动和创建的流程还是比较简单的。

二、Status Bar 构成

从第一节最后的类可以看出,整个Status Bar由Status bar和notificationPanelView两部分组成,这一节我们分析PhoneStatusBarView,它的布局如下所示:

但收到Notification时,它会显示对应的tick,以下的时序图上有显示流程。

2.1 Notification

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <LinearLayout  
  2.     android:id="@+id/notification_icon_area"  
  3.     android:layout_width="0dip"  
  4.     android:layout_height="match_parent"  
  5.     android:layout_weight="1"  
  6.     android:orientation="horizontal"  
  7.     >  
  8.     <com.android.systemui.statusbar.StatusBarIconView android:id="@+id/moreIcon"  
  9.         android:layout_width="@dimen/status_bar_icon_size"  
  10.         android:layout_height="match_parent"  
  11.         android:src="@drawable/stat_notify_more"  
  12.         android:visibility="gone"  
  13.         />  
  14.   
  15.     <com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="match_parent"  
  18.         android:layout_alignParentStart="true"  
  19.         android:gravity="center_vertical"  
  20.         android:orientation="horizontal"/>    
  21. </LinearLayout>  
所有的Notification Icon是由IconMerger连接显示的,那么PhoneStatusBar是如何接收Notification,如何创建Notification到Notification Panel呢,如果添加Icon到IconMerger呢?

下图说明了整个流程:


以上时序图流程是NotificationManagerService收到Notification时创健StatusBarNotification,并传给StatusManagerService,而StatusManagerService最后会调到PhoneStatusBar,它的父类BastStatusBar调用InflateViews把Notification添加到NotificationPanelView了,这个对象怎么来,这些可视化的对象都在第一节中的makeStatusBarView中创建的,它们的根View是mStatusBarWindow。完成这一部操作后,它会返回PhoneStatusBar发送添加tick,触发tickStarting(),接下来再调用updateNotificationIcons来添加NotificationIcon到IconMerger中,Notification的整个显示流程就是这样。

2.2 Status Icon

整个Status Icon的初始化,已及创建流程如下图所示:


StatusBarManagerService在调用StatusIconList的defines()传递了status icon 的配置文件config_statusBarIcons,内容如下:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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">location</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">data_connection</xliff:g></item>  
  15. <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>  
  16. <item><xliff:g id="id">phone_signal</xliff:g></item>  
  17. <item><xliff:g id="id">battery</xliff:g></item>  
  18. <item><xliff:g id="id">alarm_clock</xliff:g></item>  
  19. <item><xliff:g id="id">secure</xliff:g></item>  
  20. <item><xliff:g id="id">clock</xliff:g></item>  
  21. </string-array>  
StatusIconList根据这个数组大小创建了StatusBarIcon数组和mSlots用来保存以上配置文件内容,然后把List复制给了PhoneStatusBar,Icon List的初始化就结束了。创建显示的工作是从PhoneStatusBarPolicy开始的,它是怎么实现的呢,看代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public PhoneStatusBarPolicy(Context context) {  
  2.         mContext = context;  
  3.         mService = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);  
  4.   
  5.         // listen for broadcasts  
  6.         IntentFilter filter = new IntentFilter();  
  7.         filter.addAction(Intent.ACTION_ALARM_CHANGED);  
  8.         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);  
  9.         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);  
  10.         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
  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.         int numPhones = MSimTelephonyManager.getDefault().getPhoneCount();  
  17. //---省略部分代码  
  18.         // bluetooth status  
  19.         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();  
  20.         int bluetoothIcon = R.drawable.stat_sys_data_bluetooth;  
  21.         if (adapter != null) {  
  22.             mBluetoothEnabled = (adapter.getState() == BluetoothAdapter.STATE_ON);  
  23.             if (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED) {  
  24.                 bluetoothIcon = R.drawable.stat_sys_data_bluetooth_connected;  
  25.             }  
  26.         }  
  27.         mService.setIcon("bluetooth", bluetoothIcon, 0null);  
  28.         mService.setIconVisibility("bluetooth", mBluetoothEnabled);  
  29. //---省略部分代码  
  30. }  
这是PoneStatusBarPolicy的构造函数,我这儿只列出了Bluetooth的初始化,其它的代码省略了,所以Bluetooth Status icon的初始就是从这个地方开始的先是获取Bluetooth的状态,然后再调用StatusBarManagerService的setIcon方法,在这个位置它做了配置检查,如果上面配置列表的才会继续往下走,否则抛异常,接下来Service就调PhoneStatusBar注册的回调接口CommandQueue的setIcon方法,它里面经过判断是add 还是update,然后就调用到PhoneStatusBar的AddIcon方法:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {  
  2.     if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex  
  3.             + " icon=" + icon);  
  4.     StatusBarIconView view = new StatusBarIconView(mContext, slot, null);  
  5.     view.set(icon);  
  6.     mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));  
  7. }  
其实mStatusIcons就是一个LinearLayout,我们把Status Icon着为子View添加上去的。

初始化创建的流程是走完了,也很简单。

2.3 信号相关Icon

这部分的Icon包含了Wifi, 手机信号,数据流量,流量类型,和没有插入sim card,最后还有一个飞行模式图标,那这几个Icon是怎么添加到StatusBar上的呢,它们又是怎么保持和系统同步的呢?这儿有几个新类需要认识了,NetworkController它用来接收系统状态的变化,如wifi的连接断开,手机信号的变化,sim状态变化,飞行模式变化,而SignalClusterView用来组织这些Icon,并提供接口给NetworkController来更新状态。

NetworkController是在PhoneStatusBar的makeStatusBarView方法中创建的,并且在自己的构造函数中注册了这些广播,我们可以看下,它监听哪些广播:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. IntentFilter filter = new IntentFilter();  
  2. filter.addAction(WifiManager.RSSI_CHANGED_ACTION);  
  3. filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);  
  4. filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);  
  5. filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);  
  6. filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);  
  7. filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);  
  8. filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);  
  9. filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);  
  10. filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);  
  11. mWimaxSupported = mContext.getResources().getBoolean(  
  12. com.android.internal.R.bool.config_wimaxEnabled);  
  13. if(mWimaxSupported) {  
  14. filter.addAction(WimaxManagerConstants.WIMAX_NETWORK_STATE_CHANGED_ACTION);  
  15. filter.addAction(WimaxManagerConstants.SIGNAL_LEVEL_CHANGED_ACTION);  
  16. filter.addAction(WimaxManagerConstants.NET_4G_STATE_CHANGED_ACTION);  
  17. }  
  18. context.registerReceiver(this, filter)  
2.4电池图标
在status_bar.xml里配置了以下的xml:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <com.android.systemui.BatteryMeterView  
  2.     android:id="@+id/battery"  
  3.     android:layout_height="16dp"  
  4.     android:layout_width="10.5dp"  
  5.     android:layout_marginBottom="0.33dp"  
  6.     android:layout_marginStart="4dip"  
  7.     />  
它是怎么根据电量的变化来设置图片的呢?

这儿你如果还把电池图标理解成是图片,你就错了,不是图片难道是画出来的吗,答案就在BatteryMetterView中,它自己监听ACTION_BATTERY_CHANGED事件,并重写了draw方法电池图标就是这儿画出来的,节省篇副代码就不贴了。

2.5 Clock

这个就比较简单了,它就是继承了TextView,然后根据系统消息设置文字:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. filter.addAction(Intent.ACTION_TIME_TICK);  
  2. filter.addAction(Intent.ACTION_TIME_CHANGED);  
  3. filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);  
  4. filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);  
  5. filter.addAction(Intent.ACTION_USER_SWITCHED);  

总结:对于Status Bar的整个创建构成以及状态同步都做了一个详细的说明,如果要在这部分做一些定制开发我相信是没有问题的了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值