自 android2.2 開始 , 本來存在與 framework-res.apk 中的狀態欄和下拉通知欄界面控制被分割出一個單獨的 apk文件 , 命名爲 SystemUI.apk, 保存在 System/app 文件夾中。在 SystemUI.apk 中 , 是存在着狀態欄的圖標 ,XML 和控制文件等 , 這樣的分割 , 使咱們能夠更方便地去修改。 php
SystemUI 模塊中主要包含了 USB 和 Statusbar 兩個子模塊,本文將以 Statusbar 爲主導來向你們闡述 SystemUI 中Statusbar 的功能做用,使用方法,模塊框架,以及模塊內部的重要流程。 html
狀態欄主要用來顯示一些系統圖標,應用的通知圖標和系統時間。 Statusbar 模塊就是控制和管理着這些圖標,以及通知信息的顯示和一些系統開關的。 java
Ⅰ、狀態欄的通知功能(包括時間,通知,系統狀態等) android
狀態欄與 Toast 均可以起到通知、提醒的做用。但它們的實現原理和表現形式卻徹底不同。 Toast 其實至關於一個 Widget 組件,有些相似於沒有按鈕的對話框。而 Statusbar 可與系統其它應用進行交互來顯示在屏幕上方狀態欄中的信息,而且 Statusbar 還可經過圖標的顯示變化來反應一些系統狀態的變換,如電池電量, wifi ,系統音量,鬧鐘等。狀態欄 是一種讓你的應用程序或系統信息變化在不使用 Activity 的狀況下給用戶的提醒和通知。 windows
Ⅱ、狀態欄的日期顯示 併發
狀態欄也會顯示系統時間,當前日期也會在狀態欄顯示,只是在默認狀況下日期是隱藏的,只有在點擊狀態欄時纔會顯示。 app
Ⅰ 、 Notification 主要做用和使用步驟: 框架
Notification 是看不見的程序組件( Broadcast Receiver , Service 和不活躍的 Activity )警示用戶有須要注意的事件發生的最好途徑 ide
下面主要介紹使用方法步驟: 函數
獲取 NotificationManager 實例
獲取 Notification 示例,設置屬性,併發送通知
Ⅱ、步驟詳解
獲取 NotificationManager 實例
這個類主要負責將 Notification 在狀態欄中顯示出來和取消。主要包括 5 個函數:
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 應該保留給如鬧鐘的情形,它須要及時的採起響應.
這裏主要向你們介紹如何添加一個在狀態欄顯示的系統圖標,相似於系統默認的鬧鐘圖標,聲音圖標等。
文件中加資源:
. 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 中的控制開關會作詳細的描述,這裏就不在贅述。
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 時才顯示的,默認是隱藏的
Statusbar 內部各類交互以及模塊與其餘應用的交互都是創建在 StatusbarService 之上的,其中包括 Statusbar視圖的建立(包括 Statusbar 、 TrackingView 和 StatusbarExpandedView ),視圖動畫,系統圖標(鬧鐘、 wifi 、SIM 卡等)的加載和管理,其餘應用通知信息的加載顯示、更新、刪除等,其餘應用的遠程接口控制(如當打電話時statusbar 處於禁用狀態的)對 Android 系統其餘應用的通知信息(包括圖標、 tracker 、 notification 的佈局等)的處理。 SIM 卡信息的控制等。
總之 StatusbarService 是 Statusbar 的靈魂所在,是 Statusbar 的核心,全部關於 Statusbar 的操做處理都是創建在 StatusbarService 這個基礎之上的。
在整個 Statusbar 模塊中包括了多個操做流程(例如 StatusbarService 的啓動流程), Statusbar 與系統其餘應用交互的處理流程(例如 Statusbar 對天氣預報的通知的處理),還有系統圖標的更新流程, statusbar 拖動時動畫的繪製流程,以及遠程接口的控制流程等。
首先,當系統進程 system_press 啓動以後,調用系統 SystemServer.java ,在 SystemServer 中運行ServerThread.run() 方法時會註冊 StatusBarManagerService 。
注 :在 SystemUI 模塊的 SystemUiApp.java 的 onCreate 方法中也會 startService ,這是當 Statusbar 意外退出而致使 StatusbarService 中止服務時會從新啓動 StatusbarService
在啓動 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 位置。
這樣一個電池相關圖標就在狀態欄上添加或者更新了。刪除操做相似。
在應用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) { } } |
該函數中主要作了 2 件事:獲取一個服務,用該服務將通知事件「入隊」插入通知隊列,因此應該在某個地方有人在不停的讀取通知隊列。 下面是 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 中添加了該消息:
如上就是當應用發送完 notification 後 StatusbarService 是如何將發送的信息顯示到 Statusbar 上的。
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 的初始化
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));
}
從點擊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 走到 StatusBarService:interceptTouchEvent() ;
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 。
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.3.2 所講,經過遠程代理方式更新狀態欄圖標,由於 StatusBarManager 方法在 SDK 中並未公開以下就講述對StatusBarManager 的使用方法。
在 StatusbarService.java 中的的 disable 方法,就實現並擴展了了 StatusbarManager 的 disable 所實現的功能(如 statusbar 的禁止拖動,不顯示通知圖標,不顯示 ticker 等)。
權限:
<uses-permission android:name="android.permission.STATUS_BAR"/>
<uses-permission android:name="android.permission.DISABLE_STATUS_BAR"/>
這個方法也是禁用statusbar 的一種方法。
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 只是一個策略管理,實際的功能是StatusBarService 來實現的。StatusBarService 初始化時初始化了一個用於顯示statusbar 的StatusBarView。StatusBarView 裏面定義了icon 名字,的顯示順序,對應的png 圖等,在StatusBarService 調用makeStatusBarView 方法時實現statusbar 的初始化。 |
表 5.3
CommandQueue.java |
CommandQueue 是 StatusBarservice 和 StatusBarManagerService 交互的樞紐 |
IStatusBar.java 裏面對應的方法是用 CommandQueue 的接口 callback 的形式調用的,callback 的實如今對應的服務提供類也就是 StatusBarService.java 中提供的。 最終執行狀態欄更新通知等事件都是在實現的 CommandQueue.Callbacks 裏面執行。 |
表 5.4
本文檔主要講述了 SystemUI 模塊中 Statusbar 模塊的主要功能和實先步驟,文檔中介紹了 Statusbar 的功能,使用方法,模塊框架,以及模塊一些實現的主要流程等。
但願你們在閱讀文檔的過程當中,若是發現文檔的缺點和錯誤,請及時反饋,我將以最快的速度加以改進。