閱讀431 返回首頁    go 阿裏雲 go 技術社區[雲棲]


android 狀態欄(StatusBar)

一、SystemUI 概述

         android2.2 開始 , 原本存在與 framework-res.apk 中的狀態欄和下拉通知欄界麵控製被分割出一個單獨的 apk文件 , 命名為 SystemUI.apk, 保存在 System/app 文件夾中。在 SystemUI.apk  , 是存在著狀態欄的圖標 ,XML 和控製文件等 , 這樣的分割 , 使我們可以更方便地去修改。

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

1.1 Statusbar 的功能作用

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

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

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

Ⅱ、狀態欄的日期顯示

      狀態欄也會顯示係統時間,當前日期也會在狀態欄顯示,隻是在默認情況下日期是隱藏的,隻有在點擊狀態欄時才會顯示。

 

1.2 Statusbar 的使用方法

1.2.1 notification 的使用

  Notification 主要作用和使用步驟:

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

下麵主要介紹使用方法步驟:

獲取 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.1 Statusbar 布局

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

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

LinearLayout android: 我們看到的狀態欄,係統默認是左邊放通知圖標 notificationIcons ,右邊放狀態圖標 statusIcons

    --1. 通知圖標區域:  IconMerger android:

    --2. 狀態圖標區域: LinearLayout android:


LinearLayout android: 顯示。在正常情況下 ticker 是不顯示的,隻有在 StatusBarService 收到通知時它才顯示


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

 

三、模塊內部框架

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


總之 StatusbarService  Statusbar 的靈魂所在,是 Statusbar 的核心,所有關於 Statusbar 的操作處理都是建立在 StatusbarService 這個基礎之上的。

 

四、模塊流程

在整個 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 遠程接口簡介

StatusBarM

最後更新:2017-04-03 12:56:00

  上一篇:go 清理 zabbix 曆史數據, 縮減 mysql 空間
  下一篇:go 麵向對象