android 狀態欄(StatusBar)



 

1、SystemUI 概述

         android2.2 開始 , 本來存在與 framework-res.apk 中的狀態欄和下拉通知欄界面控制被分割出一個單獨的 apk文件 , 命名爲 SystemUI.apk, 保存在 System/app 文件夾中。在 SystemUI.apk  , 是存在着狀態欄的圖標 ,XML 和控制文件等 , 這樣的分割 , 使咱們能夠更方便地去修改。 php

SystemUI 模塊中主要包含了 USB  Statusbar 兩個子模塊,本文將以 Statusbar 爲主導來向你們闡述 SystemUI Statusbar 的功能做用,使用方法,模塊框架,以及模塊內部的重要流程。 html

1.1 Statusbar 的功能做用

狀態欄主要用來顯示一些系統圖標,應用的通知圖標和系統時間。 Statusbar 模塊就是控制和管理着這些圖標,以及通知信息的顯示和一些系統開關的。 java

Ⅰ、狀態欄的通知功能(包括時間,通知,系統狀態等) android

狀態欄與 Toast 均可以起到通知、提醒的做用。但它們的實現原理和表現形式卻徹底不同。 Toast 其實至關於一個 Widget 組件,有些相似於沒有按鈕的對話框。而 Statusbar 可與系統其它應用進行交互來顯示在屏幕上方狀態欄中的信息,而且 Statusbar 還可經過圖標的顯示變化來反應一些系統狀態的變換,如電池電量, wifi ,系統音量,鬧鐘等。狀態欄 是一種讓你的應用程序或系統信息變化在不使用 Activity 的狀況下給用戶的提醒和通知。 windows

Ⅱ、狀態欄的日期顯示 併發

      狀態欄也會顯示系統時間,當前日期也會在狀態欄顯示,只是在默認狀況下日期是隱藏的,只有在點擊狀態欄時纔會顯示。 app

 

1.2 Statusbar 的使用方法

1.2.1 notification 的使用

  Notification 主要做用和使用步驟: 框架

Notification 是看不見的程序組件( Broadcast Receiver , Service 和不活躍的 Activity )警示用戶有須要注意的事件發生的最好途徑 ide

下面主要介紹使用方法步驟: 函數

獲取 NotificationManager 實例

獲取 Notification 示例,設置屬性,併發送通知

Java代碼   收藏代碼
  1. public class Main extends Activity {  
  2.     private Button sendBtn , cancelBtn;  
  3.     private Notification n;  
  4.     private NotificationManager nm;  
  5.     //Notification的標示ID  
  6.     private static final int ID = 1;  
  7.      
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.main);  
  12.          
  13.         //實例化按鈕  
  14.         sendBtn = (Button)this.findViewById(R.id.sendBtn);  
  15.         cancelBtn = (Button)this.findViewById(R.id.cancelBtn);  
  16.          
  17.         //獲取NotificationManager實例  
  18.         String service = NOTIFICATION_SERVICE;  
  19.         nm = (NotificationManager)this.getSystemService(service);  
  20.          
  21.         //實例化Notification  
  22.         n = new Notification();  
  23.         //設置顯示圖標,該圖標會在狀態欄顯示  
  24.         int icon = R.drawable.icon;  
  25.         //設置顯示提示信息,該信息也會在狀態欄顯示  
  26.         String tickerText = "Test Notifaction";  
  27.         //顯示時間  
  28.         long when = System.currentTimeMillis();  
  29.          
  30.         n.icon = icon;  
  31.         n.tickerText = tickerText;  
  32.         n.when = when;  
  33.         n.flags = Notification.FLAG_NO_CLEAR;  
  34.         n.flags = Notification.FLAG_ONGOING_EVENT;  
  35.          
  36.         //爲按鈕添加監聽器  
  37.         sendBtn.setOnClickListener(sendClickListener);  
  38.         cancelBtn.setOnClickListener(cancelClickListener);  
  39.     }  
  40.      
  41.     private OnClickListener sendClickListener = new OnClickListener() {  
  42.    
  43.   @Override  
  44.   public void onClick(View v) {  
  45.    //實例化Intent  
  46.    Intent intent = new Intent(Main.this, Main.class);  
  47.    //獲取PendingIntent  
  48.    PendingIntent pi = PendingIntent.getActivity(Main.this0, intent, 0);  
  49.    //設置事件信息  
  50.    n.setLatestEventInfo(Main.this"My Title""My Content", pi);  
  51.    //發出通知  
  52.    nm.notify(ID, n);  
  53.     
  54.        }  
  55. };  
  56. private OnClickListener cancelClickListener = new OnClickListener(){  
  57.    
  58.   @Override  
  59.   public void onClick(View v) {  
  60.    nm.cancel(ID);  
  61.   }  
  62. };  
  63. }     

、步驟詳解

獲取 NotificationManager 實例

這個類主要負責將 Notification 在狀態欄中顯示出來和取消。主要包括 個函數:

void cancel(int id) , void cancel(String tag, int id) , void cancelAll() , void notify(int id, Notification notification) , notify(String tag, int id, Notification notification)

看看這五個函數就知道這個類的做用了。可是在初始化對象的時候要注意:

NotificationManager nm;

String service = NOTIFICATION_SERVICE;

nm = (NotificationManager)this.getSystemService(service);

獲取 Notification 示例,設置屬性,併發送通知

這個類主要是設置 Notification 的相關屬性,初始化。

Notification n = new Notification();

Notification 裏面有不少屬性下面選擇幾個經常使用的介紹一下(表 1.1 

icon

這個是設置通知的圖標。像天氣預報圖標。

sound

這個是設置來通知時的提示音。

tickerText

設置提示的文字。

vibrate

來通知時振動。

when

設置來通知時的時間。

contentIntent

Notification  Intent ,即點擊後轉向的 Activity

flag

FLAG_NO_CLEAR

設置爲這個屬性那麼通知欄的那個清楚按鈕就不會出現

FLAG_ONGOING_EVENT

設置爲這個屬性那麼通知就會像 QQ 圖標同樣一直在狀態欄顯示

DEFAULT_ALL

將全部屬性設置爲默認

DEFAULT_SOUND 

將提示聲音設置爲默認

DEFAULT_VIBRATE

將震動設置爲默認

 1.1

填充 Notification 的各個屬性:

//Notification 的 Intent ,即點擊後轉向的 Activity

Intent notificationIntent1 = new Intent(this, this.getClass());

notificationIntent1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

PendingIntent contentIntent1 = PendingIntent.getActivity(this, 0, notificationIntent1, 0);


n.contentIntent=contentIntent1;

n.icon = R.drawable.notification_icon;

n.tickerText = "hello";

notification.sound = Uri.parse("file:///sdcard/notification/ringer.mp3");

notification.vibrate = vibrate;

發送通知:

private static final int ID_NOTIFICATION = 1;

mNotificationManager.notify(ID_NOTIFICATION, notification);

通知的更新

   若是須要更新一個通知,只須要在設置好 notification 以後,再調用 setLatestEventInfo ,而後從新發送一次通知便可。

自定義通知視圖

   這部分能夠參考官方文檔,講的很詳細了。

AndroidSDK: docs/guide/topics/ui/notifiers/notifications.html

Notification.Builder

這個類通常用於管理 Notification ,動態的設置 Notification 的一些屬性。即用 set 來設置。

 

問題:如何區分「正在進行的」和「通知」,誰決定一個事件是「正在進行的」仍是持續的「通知」 ?

經過設置 Notification  flag 屬性能夠設定 notification 是正在進行的仍是持續的 notification 

FLAG_INSISTENT  FLAG_ONGOING_EVENT 標誌位可讓 Notification 成爲持續的或正在進行的Notification 

. Notification 標記爲 ONGOING, 以下面的代碼所示,它就能用於表示當前正在進行的事件(如來電)。正在進行的事件與「通知」 Notification 區別在擴展的狀態條窗口中。


notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;


. 持續的 Notification 一直重複,直到用戶取消。下面的代碼給出瞭如何設置 Notification 爲持續的:


notification.flags = notification.flags | Notification.FLAG_INSISTENT;

持續 Notification 反覆重複開頭的 Notification 效果,直到用戶取消。持續的 Notification 應該保留給如鬧鐘的情形,它須要及時的採起響應.


1.2.2 系統圖標的增長刪除

這裏主要向你們介紹如何添加一個在狀態欄顯示的系統圖標,相似於系統默認的鬧鐘圖標,聲音圖標等。

文件中加資源:

. frameworks\base\core\res\res\drawalbe 中添加系統圖標的圖片資源

. frameworks\base\core\res\res\values\config.xml 中添加圖片引用,這些 icon 在這個 string array 的位置就決定了其在 status bar 上顯示的位置了。咱們能夠從 code 裏面得出該結論。因此當你要調換 icon 的順序時,改動這個 config.xml 就能夠了。在 StatusBarManagerService 初始化的時候就會讀取 config.xml  icons  String array 

這個文件中加代碼: StatusBarPolicy.java 以鬧鐘爲例。

.  StatusbarPolicy.java 中初始化所增長的系統圖標

. 在構造函數中 SetIcon

. StatusBarPolicy 調用 registerReceiver 註冊了感興趣的 intent, 當感興趣的 intent 發生時,對圖標進行更新。例如,設置一個鬧鐘後,鬧鐘模塊會發出一個叫作 Intent.ACTION_ALARM_CHANGED 的廣播,而後 StatusBarPolicy接收到此廣播,繼而更新狀態欄上的鬧鐘圖標。

………

// Alarm clock StatusBarPolicy 構造方法中初始化鬧鐘圖標

mService.setIcon("alarm_clock",R.drawable.stat_notify_alarm, 0);

mService.setIconVisibility("alarm_clock", false);

……..

// StatusBarPolicy 構造方法中註冊鬧鐘改變廣播

filter.addAction(Intent.ACTION_ALARM_CHANGED);

…....

. 添加 圖標更新函數

private final void updateAlarm(Intent intent) {

boolean alarmSet = intent.getBooleanExtra(「alarmSet」, false);

mService.setIconVisibility(「alarm_clock」, alarmSet);

}

以上是在狀態欄添加顯示的系統圖標的步驟。


代碼執行步驟:

StatusBarManagerService.java 

StatusBarIconList mIcons = new StatusBarIconList();

………

          mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));

          StatusBarPolicy.java -- > setIcon(…)

          StatusBarManager.java -- > setIcon(…)

          StatusBarManagerService.java -- > setIcon(…)

在 StatusBarService onCreate 的時候調用StatusBarManagerService 中的 registerStatusBar (…)

Statusbar 中的控制開關會作詳細的描述,這裏就不在贅述。

2、模塊基本佈局

2.1 Statusbar 佈局

Android 系統頂上的狀態欄是屬於 FrameWork 的內容,在此先對 statusbar 的的結構作必定描述。

StatusBar 的佈局文件 status_bar.xml ,文件位置:frameworks/base/packages/SystemUI/res/layout/status_bar.xml

LinearLayout android:id="@+id/icons" 咱們看到的狀態欄,系統默認是左邊放通知圖標 notificationIcons ,右邊放狀態圖標 statusIcons

    --1. 通知圖標區域:  IconMerger android:id="@+id/notificationIcons"

    --2. 狀態圖標區域: LinearLayout android:id="@+id/statusIcons"


LinearLayout android:id="@+id/ticker" 顯示。在正常狀況下 ticker 是不顯示的,只有在 StatusBarService 收到通知時它才顯示


最後一個是 DateView ,它是在點擊 statusbar 時才顯示的,默認是隱藏的

 

3、模塊內部框架

Statusbar 內部各類交互以及模塊與其餘應用的交互都是創建在 StatusbarService 之上的,其中包括 Statusbar視圖的建立(包括 Statusbar  TrackingView  StatusbarExpandedView ),視圖動畫,系統圖標(鬧鐘、 wifi SIM 卡等)的加載和管理,其餘應用通知信息的加載顯示、更新、刪除等,其餘應用的遠程接口控制(如當打電話時statusbar 處於禁用狀態的)對 Android 系統其餘應用的通知信息(包括圖標、 tracker  notification 的佈局等)的處理。 SIM 卡信息的控制等。


總之 StatusbarService  Statusbar 的靈魂所在,是 Statusbar 的核心,全部關於 Statusbar 的操做處理都是創建在 StatusbarService 這個基礎之上的。

 

4、模塊流程

在整個 Statusbar 模塊中包括了多個操做流程(例如 StatusbarService 的啓動流程), Statusbar 與系統其餘應用交互的處理流程(例如 Statusbar 對天氣預報的通知的處理),還有系統圖標的更新流程, statusbar 拖動時動畫的繪製流程,以及遠程接口的控制流程等。

4.1 啓動流程

4.1.1 StatusbarService 的啓動流程

 

首先,當系統進程 system_press 啓動以後,調用系統 SystemServer.java ,在 SystemServer 中運行ServerThread.run() 方法時會註冊 StatusBarManagerService 

 

Java代碼   收藏代碼
  1. <span style="font-size: x-small;">try {  
  2.   
  3. Slog.i(TAG, "Status Bar");  
  4.   
  5. statusBar = new StatusBarManagerService(context);  
  6.   
  7. ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);  
  8.   
  9. catch (Throwable e) {  
  10.   
  11. Slog.e(TAG, "Failure starting StatusBarManagerService", e);  
  12.   
  13. }  
  14.   
  15. 讓後調用StatusBarManagerService 的systemReady2() 方法,會在systemReady2() 方法中啓動StatusbarService 。  
  16.   
  17. final StatusBarManagerService statusBarF = statusBar;  
  18.   
  19. if (statusBarF != null) statusBarF.systemReady2();  
  20.   
  21. public void systemReady2() {  
  22.   
  23. ComponentName cn = ComponentName.unflattenFromString(mContext.getString(com.android.internal.R.string.config_statusBarComponent));  
  24.   
  25. Intent intent = new Intent();  
  26.   
  27. intent.setComponent(cn);  
  28.   
  29. Slog.i(TAG, "Starting service: " + cn);  
  30.   
  31. mContext.startService(intent);  
  32.   
  33. } </span>  
 

 :在 SystemUI 模塊的 SystemUiApp.java  onCreate 方法中也會 startService ,這是當 Statusbar 意外退出而致使 StatusbarService 中止服務時會從新啓動 StatusbarService

4.1.2 系統圖標初始化流程

 

在啓動 StatusBarService   StatusbarService 會調用一個 makeStatusBarView 的方法  在裏面將建立StatusBarView 在建立 StatusbarView 的過程當中會加載系統圖標。

在啓動 StatusbarService 的過程當中會建立 StatusBarPolicy 的對象, StatusBarPolicy.java 主要負責狀態欄顯示策略的管理(如狀態欄的圖標何時顯示,在什麼位置顯示等)。 StatusBarPolicy 的構造函數中初始化了不少系統圖標(如電池信息圖標,鬧鐘圖標,聲音圖標,信號欄圖標等)。 。 默認時有不少圖標是不顯示的,須要顯示時再進行更新。

圖標初始化,以電池電量顯示爲例,大概關鍵步驟以下:

經過 BroadcastReceiver 機制, StatusBarPolicy 中註冊的 mIntentReceiver 收到 BatteryService 廣播的ACTION_BATTERY_CHANGED 事件觸發;

調用 updateBattery(intent) 開始更新電池狀態欄;

 intent 中解析須要的字段,調用 StatusBarManager  setIcon()  StatusBarManager 是客戶端使用的狀態欄管理類;

經過 IBinder 機制跨進程調用 StatusBarManagerService  setIcon()  StatusBarManagerService 派生於IStatusBarService.Stub ,是狀態欄管理的服務端,是具體實現;

StatusBarManagerService 有一個 mIcons 成員,這個 list 成員在 StatusBarManagerService 建立時加載。StatusBarManagerService  setIcon() 過程當中,會又 "battery" 字段得到在 mIcons 中的索引,再由包名、圖片id 和等級建立 StatusBarIcon 實例,並將這個實例更新 StatusBarIconList 中所得到索引對應項;

調用 CommandQueue  setIcon()  CommandQueue 派生於 IStatusBar.Stub ,有一個內部接口 Callbacks ,這個接口的實現就是 StatusBarService  CommandQueue  StatusBarService  StatusBarManager 屬於同一個進程,而 StatusBarManagerService 是一個系統級服務,它們之間必然須要經過 IBinder 進程間通訊;

CommandQueue 用於處理狀態欄、通知相關的請求,內部維護了一個事件隊列, setIcon() 會建立一個 OP_SET_ICON massege ,發送給 Handler 處理;

CommandQueue 內部也有一個 StatusBarIconList 實例,這個實例是由 StatusBarService 建立。在處理OP_SET_ICON  massege 前,會先經過 getViewIndex 得到圖標 View 的位置索引 viewIndex ,(由於有些圖標有可能爲空)再更新 StatusBarIconList ,最後調用 Callbacks ,也就是 StatusBarService  addIcon() 或者updateIcon() 

 addIcon() 爲例, StatusBarService  addIcon() 會建立一個新的 StatusBarIconView ,將第步中所建立的StatusBarIcon 實例設置進去,而後把這個 view 添加到 LinearLayout  viewIndex 位置。

這樣一個電池相關圖標就在狀態欄上添加或者更新了。刪除操做相似。

4.2 通知處理

 

在應用Activity 中實現通知欄圖標變化的程序中。是用NotificationManager 對象mNotificationManager 來發送通知。通知爲Notification mNotification   對象,填充mNotification   的圖標和消息內容以及一個when ,而後構造了一個Intent 對象intent ,包含了本Activity 對象的引用,以及本Activity 的類名,一個PendingIntent pi對象,包含上述Intent 對象以及本Activity 對象的引用,是用於消息列表中顯示本Activity 項。點擊時從新激活Activity 。而後調用nm.setLatestEventInfo 設置狀態欄下拉列表項內容。最後調用nm.notify(1,n) 方法來發送通知,接着改變狀態欄的工做就由NotificationManager StatusBarManagerService 交互了。

下面來看看NotificationManager 是如何和StatusBarManagerService 交互的。

nm.notify(1,n) 方法作了最重要的事,就是所謂的發送通知   該方法的代碼以下:

public void notify(int id, Notification notification) 

     {

         notify(null, id, notification);

}

其實是調用了下面這個函數:

pu blic void notify(String tag, int id, Notification notification)

     {

         int[] idOut = new int[1];

         INotificationManager service = getService();

         String pkg = mContext.getPackageName();

         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");

         try {

             service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);

             if (id != idOut[0]) {

                 Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);

             }

         } catch (RemoteException e) {

         }

     }

該函數中主要作了 件事:獲取一個服務,用該服務將通知事件「入隊」插入通知隊列,因此應該在某個地方有人在不停的讀取通知隊列。

下面是 getService 的代碼,這裏的 INotificationManager.Stub.asInterface(b) 這個形式在好多地方出現,必定要詳細理解該類代碼,在 Binder 機制中。

static public INotificationManager getService()

     {

         if (sService != null) {

             return sService;

         }

         IBinder b = ServiceManager.getService("notification");

         sService = INotificationManager.Stub.asInterface(b);

         return sService;

     }

StatusBarManagerService 中添加了該消息:

位於 NotificationManagerService  enqueueNotificationInternal 函數中:

r.statusBarKey =   mStatusBar.addNotification(n);    其中 是由 notification 對象構造的 statusBarNotification 對象   mStatusBar 是一個 StutusBarManagerService 的引用。

 addNotification() 中執行了:

synchronized (mNotifications) {

             IBinder key = new Binder();

             mNotifications.put(key, notification);

             if (mBar != null) {

                 try {

                     mBar.addNotification(key, notification);

                 } catch (RemoteException ex) {

                 }

             }

             return key;

這裏先執行了 mNotification.put 將該通知加入一個 hashMap 結構體中

而後執行了 mBar.addNotification(key,notification)   調用了 CommandQueue 中的addNotification 方法,該方法利用 Handler 調用了 mCallbacks.addNotification 方法,其實就是 StatusBarService 中的。

這個 mBar 是由方法:

public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,

             List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {

         enforceStatusBarService();


         Slog.i(TAG, "registerStatusBar bar=" + bar);

         mBar = bar;

         synchronized (mIcons) {

             iconList.copyFrom(mIcons);

         }

         synchronized (mNotifications) {

             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {

                 notificationKeys.add(e.getKey());

                 notifications.add(e.getValue());

             }

         }

     }

 StatusBarService 啓動的時候註冊的 mCommandQueue 對象的引用  

mCommandQueue = new CommandQueue(this, iconList);

由該對象的實例化以及其構造函數

public CommandQueue(Callbacks callbacks, StatusBarIconList list) {

         mCallbacks = callbacks;

         mList = list;

     }

能夠看出,註冊的 mCommandQueue 中的 callbacks 接口,是由 StatusBarService 實現的

public interface Callbacks {

         public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);

         public void updateIcon(String slot, int index, int viewIndex,

                 StatusBarIcon old, StatusBarIcon icon);

         public void removeIcon(String slot, int index, int viewIndex);

         public void addNotification(IBinder key, StatusBarNotification notification);

         public void updateNotification(IBinder key, StatusBarNotification notification);

         public void removeNotification(IBinder key);

         public void disable(int state);

         public void animateExpand();

         public void animateCollapse();

     }

接口聲明如上:

其中, StatusBarService  addNotification 的實現以下:

public void addNotification(IBinder key, StatusBarNotification notification) {

boolean shouldTick = true;

if (notification.notification.fullScreenIntent != null) {

shouldTick = false;

Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");

try {

notification.notification.fullScreenIntent.send();

} catch (PendingIntent.CanceledException e) {

}

}


StatusBarIconView iconView = addNotificationViews(key, notification);

if (iconView == null) return;


if (shouldTick) {

tick(notification);

}


// Recalculate the position of the sliding windows and the titles.

setAreThereNotifications();

updateExpandedViewPos(EXPANDED_LEAVE_ALONE);

}

因此到頭來 enqueueNotificationInternal 方法中 mBar.addNotification(key, notification); 實際上是調用了 StatusBarService 實現的 addNotification 方法,即上面的代碼。

上面的代碼中這句 StatusBarIconView iconView = addNotificationViews(key, notification);   以及 tick(notification)   多是將圖標以及信息顯示在 StatusBarView 上的主要語句。接着進入這兩個方法。

addNotificationViews():

StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {

         NotificationData list;

         ViewGroup parent;

         final boolean isOngoing = notification.isOngoing();

         if (isOngoing) {

             list = mOngoing;

             parent = mOngoingItems;

         } else {

             list = mLatest;

             parent = mLatestItems;

         }

         // Construct the expanded view.

         final View[] views = makeNotificationView(notification, parent);

         if (views == null) {

             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "

                     + notification);

             return null;

         }

         final View row = views[0];

         final View content = views[1];

         final View expanded = views[2];

         // Construct the icon.

         final StatusBarIconView iconView = new StatusBarIconView(this,

                 notification.pkg + "/0x" + Integer.toHexString(notification.id));

         final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,

                     notification.notification.iconLevel, notification.notification.number);

         if (!iconView.set(ic)) {

             handleNotificationError(key, notification, "Coulding create icon: " + ic);

             return null;

         }

         // Add the expanded view.

         final int viewIndex = list.add(key, notification, row, content, expanded, iconView);

         parent.addView(row, viewIndex);

         // Add the icon.

         final int iconIndex = chooseIconIndex(isOngoing, viewIndex);

         mNotificationIcons.addView(iconView, iconIndex);

         return iconView;

     }

final StatusBarIconView iconView = new StatusBarIconView(this,

                 notification.pkg + "/0x" + Integer.toHexString(notification.id));

其中這一句利用傳來的 notification 構造了圖標 view

mNotificationIcons.addView(iconView, iconIndex);   其中 mNotificationIcons 是一個IconMerger 對象, IconMerger 是繼承 LinearLayout 的類。

這一句將圖標顯示在 StatusBar 上。

如上就是當應用發送完 notification  StatusbarService 是如何將發送的信息顯示到 Statusbar 上的。

4.3 圖標更新

4.3.1 經過廣播接收器的方式

StatusBarPolicy 調用 registerReceiver 註冊了感興趣的 intent, 當感興趣的 intent 發生時,對圖標進行更新。例如,設置一個鬧鐘後,鬧鐘模塊會發出一個叫作 Intent.ACTION_ALARM_CHANGED 的廣播,而後 StatusBarPolicy接收到此廣播,繼而更新狀態欄上的鬧鐘圖標。


………

// Alarm clock StatusBarPolicy 構造方法中初始化鬧鐘圖標

mService.setIcon("alarm_clock",R.drawable.stat_notify_alarm, 0);

mService.setIconVisibility("alarm_clock", false);

……..

// StatusBarPolicy 構造方法中註冊鬧鐘改變廣播

filter.addAction(Intent.ACTION_ALARM_CHANGED);

…....

// 改變鬧鐘圖標

private final void updateAlarm(Intent intent) {

boolean alarmSet = intent.getBooleanExtra(「alarmSet」, false);

mService.setIconVisibility(「alarm_clock」, alarmSet);

}


StatusBarPolicy 只是一個策略管理,實際的功能是 StatusBarService 來實現的。 StatusBarService 初始化時初始化了一個用於顯示 statusbar  StatusBarView  StatusBarView 裏面定義了 icon 名字,的顯示順序,對應的png 圖等,在 StatusBarService 調用 makeStatusBarView 方法時實現 statusbar 的初始化

 4.3.2 經過遠程代理方式

StatusBarManager 有一個更新圖標的方法: public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) ,不過 StatusBarManager 並未把方法公開在 sdk 中,可是應該有方法能夠訪問的。 
    public void updateIcon(IBinder key, String slot, int iconId, int iconLevel) {
        try {
            mService.updateIcon(key, slot, mContext.getPackageName(), iconId, iconLevel);
        } catch (RemoteException ex) {
                       throw new RuntimeException(ex);
        }
    }
mService
  StatusBarManager 的一個成員變量, StatusBarManager 被構建的時候被賦值,他是 IStatusBar 的一個代理對象

    StatusBarManager(Context context) {
        mContext = context;
        mService = IStatusBar.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
    }

4.4 拖動刷新

4.4.1 StatusbarView 從被點擊到拖動

 

從點擊StatusBar 會出現新的View ,它的流程以下:

StatusBarView 就是StatusBar 所表明的View ,那麼查看它的代碼,看它處理點擊的方法。

它屬性變量保存了StatusBarService 的引用mService ,它的點擊處理函數onTouchEvent()onInterceptTouchEvent() 都會調用到StatusBarService 類中的interceptTouchEvent() 方法。

當咱們點擊StatusBar 時,會先走到onInterceptTouchEvent() 這個函數,並且這個函數只會在第一次走到,而後會走到onTouchEvent() 方法,這個方法每收到一個TouchEvent() 就會走到,所以會走到屢次。

函數onInterceptTouchEvent() 的處理:

1 、調用到StatusBarService 中的interceptTouchEvent() ,在這裏又會走到event.getAction() == MotionEvent.ACTION_DOWN 分支,在分支中,因爲mExpanded == false 且y < hitSize 會繼續調用prepareTracking(y) 。

2 、函數prepareTracking() 處理:這裏因爲mExpanded == false 因此會向H 中發送MSG_ANIMATE_REVEAL 消息,進入StatusBarService 本身的消息循環。執行doRevealAnimation() 函數。

3 、函數doRevealAnimation() 處理:這個實現的功能很簡單,就是在TrackingView( 就是點擊時StatusBar 下出現的View) 尚未徹底顯示出來的時候,經過動畫的方式,一點一點的將TrackingView 顯示出來。

當咱們手指離開時調用順序以下:

1 、StatusBarView :onTouchEvent() ,此時Action != MotionEvent.ACTION_DOWN 走到 StatusBarServiceinterceptTouchEvent() ;

2 、interceptTouchEvent() 中會走到分支 else if (mTracking) ;

3 、因爲ACTION_UP 因此會調用performFling() ,在這裏會向Handler 發送 MSG_ANIMATE 消息,而後進入函數doAnimation() 。

4 、在doAnimation() 因爲mAnimY < mStatusBarView.getHeight() 分支成立,會繼續調用updateExpandedViewPos(0) 和performCollapse();

5 、在performCollapse() 中,經過mTrackingView.setVisibility(View.GONE) 實現了 讓mTrackingView 的隱藏,其實這個函數還實現了其餘的View 的隱藏,好比咱們點擊後進行拖動所出現的其餘View 。

 

4.5 遠程接口

4.5.1 Statusbar 遠程接口簡介

StatusBarManagerService 經過使用 IStatusBar  aidl 調用 CommandQueue   CommandQueue 中定義Callbacks 

StatusBarService 實現了 CommandQueue  Callbacks 的回調

public interface Callbacks {

public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon);

public void updateIcon(String slot, int index, int viewIndex,

StatusBarIcon old, StatusBarIcon icon);

public void removeIcon(String slot, int index, int viewIndex);

public void addNotification(IBinder key, StatusBarNotification notification);

public void updateNotification(IBinder key, StatusBarNotification notification);

public void removeNotification(IBinder key);

public void disable(int state);

public void animateExpand();

public void animateExpandToggles(boolean needForceStatusBar);

public void animateCollapse();

public void showSIMIndicator(String businessType);

public void hideSIMIndicator();

}

由上述源碼咱們能夠得出在 StatusbarService.java 中都有增長 / 刪除狀態欄圖標、增長 / 更新 / 刪除notification 、禁用 Statusbar  SIM 指示信息的隱藏和顯示、還有 Statusbar 拖動動畫的實現。

4.5.2 StatusBarManager 的使用

 4.3.2 所講,經過遠程代理方式更新狀態欄圖標,由於 StatusBarManager 方法在 SDK 中並未公開以下就講述對StatusBarManager 的使用方法。

 StatusbarService.java 中的的 disable 方法,就實現並擴展了了 StatusbarManager  disable 所實現的功能(如 statusbar 的禁止拖動,不顯示通知圖標,不顯示 ticker 等)。


Java代碼   收藏代碼
  1. <span style="font-size: x-small;">/** 
  2.    * State is one or more of the DISABLE constants from StatusBarManager. 
  3.    */  
  4.    public void disable(int state) {  
  5.         final int old = mDisabled;  
  6.         final int diff = state ^ old;  
  7.         mDisabled = state;  
  8.         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {  
  9.             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {  
  10.                 if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");  
  11.                 animateCollapse();  
  12.             }  
  13.         }  
  14.         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {  
  15.             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {  
  16.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");  
  17.                 if (mTicking) {  
  18.                     mTicker.halt();  
  19.                 } else {  
  20.                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);  
  21.                 }  
  22.             } else {  
  23.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");  
  24.                 if (!mExpandedVisible) {  
  25.                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);  
  26.                 }  
  27.             }  
  28.         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {  
  29.             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {  
  30.                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");  
  31.                 mTicker.halt();  
  32.             }  
  33.         }  
  34.      }  
  35. 下面在將一種簡單的對StatusBarManager的引用方法:  
  36. Object service = getSystemService ("statusbar");  
  37.     try {  
  38. Class <?> statusBarManager = Class.forName  
  39. ("android.app.StatusBarManager");  
  40. Method expand = statusBarManager.getMethod ("disable",int.class);  
  41. expand.invoke (service,0x00000001);  
  42. catch (Exception e) {  
  43. e.printStackTrace();  
  44. }</span>  

 權限:

<uses-permission android:name="android.permission.STATUS_BAR"/>

<uses-permission android:name="android.permission.DISABLE_STATUS_BAR"/>

這個方法也是禁用statusbar 的一種方法。

5、重要文件的介紹

StatusBarManagerService.java

StatusBarManagerService 是服務端 StatusBarService 的管理者

顧名思義, StatusBarManagerService  StatusBarService 的管理者,是StatusBarService 與外界通訊的橋樑,如4.2 所講。

 StatusBarManagerService.java 中,有 addNotification removeNotification,updateNotification 等方法用於管理傳遞給他的通知對象。這個類是一些管理方法,實際執行相關動做的是在 IStatusBar.java 裏面,這個是framework/base/core/java/com /android/internal/statusbar/IStatusBar.aidl 自動生成的用於 IPC 的類。

 5.1

 

StatusBarService.java

StatusBarservice  Statusbar 的核心

StatusBarService 這個服務是Statusbar 模塊的中心點,全部關於圖標的加載、更新、刪除等處理,與應用的交互,對通知信息的處理,動畫的完成等都是創建在StatusBarService 這個基礎之上的。

 5.2

 

StatusBarPolicy.java

StatusBarPolicy 負責狀態欄顯示的策略管理

Android 中狀態欄上有不少圖標,這些圖標何時顯示何時不顯示 ,這些都是StatusBarPolicy 來管理的。
StatusBarPolicy
 
的構造函數裏初始化了好幾個圖標,如鬧鐘icon ,信號欄icon 等。默認時有不少圖標是不顯示的,須要顯示時再進行更新。StatusBarPolicy 調用 registerReceiver註冊了感興趣的intent, 當感興趣的intent 發生時,對圖標進行更新。

StatusBarPolicy 只是一個策略管理,實際的功能是StatusBarService 來實現的。StatusBarService 初始化時初始化了一個用於顯示statusbar StatusBarViewStatusBarView 裏面定義了icon 名字,的顯示順序,對應的png 圖等,在StatusBarService 調用makeStatusBarView 方法時實現statusbar 的初始化。

 5.3

 

CommandQueue.java

CommandQueue  StatusBarservice  StatusBarManagerService 交互的樞紐

IStatusBar.java 裏面對應的方法是用 CommandQueue 的接口 callback 的形式調用的,callback 的實如今對應的服務提供類也就是 StatusBarService.java 中提供的。

最終執行狀態欄更新通知等事件都是在實現的 CommandQueue.Callbacks 裏面執行。

 5.4

6、總結

本文檔主要講述了 SystemUI 模塊中 Statusbar 模塊的主要功能和實先步驟,文檔中介紹了 Statusbar 的功能,使用方法,模塊框架,以及模塊一些實現的主要流程等。

但願你們在閱讀文檔的過程當中,若是發現文檔的缺點和錯誤,請及時反饋,我將以最快的速度加以改進。

相關文章
相關標籤/搜索