Notification之 - Android5.0实现原理(二)
概述
前文講解了Notification的構造,現在來講講notification的發送,以及公布前文留下的疑問(自定義view不論高度是多高,最后只能顯示為64dp,why?)
NotificationManager
在Notification構造完成后,會調用NotificationManager的notify方法來發送通知,我們就來看看該方法
frameworks/base/core/java/android/app/NotificationManager.java
可以看出NotificationManager只是一個空殼,沒有做什么實際上的事情,只是把notify的動作交給了service來做。
為了主干的清晰,直接給出enqueueNotificationWithTag的實現在NotificationManagerService中
NotificationManagerService
frameworks/base/services/java/com/android/server/NotificationManagerService.java
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,Notification notification, int[] idOut, int userId) throws RemoteException {enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),Binder.getCallingPid(), tag, id, notification, idOut, userId); }復制代碼所以重要的是enqueueNotificationInternal方法
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int[] idOut, int incomingUserId) {...if (!isSystemNotification && !isNotificationFromListener) {...//MAX_PACKAGE_NOTIFICATIONS = 50;if (count >= MAX_PACKAGE_NOTIFICATIONS) {return;}}...mHandler.post(new Runnable() {@Overridepublic void run() {synchronized (mNotificationList) {...// blocked apps//如果用戶設置了該引用不顯示通知,并且不是系統通知的話,直接將該通知打分為-1000if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {if (!isSystemNotification) {//JUNK_SCORE = -1000;r.score = JUNK_SCORE;}}//SCORE_DISPLAY_THRESHOLD = -20;//打分小于閾值的通知不顯示if (r.score < SCORE_DISPLAY_THRESHOLD) {// Notification will be blocked because the score is too low.return;}//垃圾通知,也不會顯示if (isNotificationSpam(notification, pkg)) {mArchive.record(r.sbn);return;}...//只顯示有圖標的通知if (notification.icon != 0) {StatusBarNotification oldSbn = (old != null) ? old.sbn : null;mListeners.notifyPostedLocked(n, oldSbn);}...//聲音,震動,閃光燈的控制buzzBeepBlinkLocked(r);}}}); }復制代碼可以看到要想發出通知必須得滿足以下幾個條件
檢查通過后再使用notifyPostedLocked方法做真正的發送動作。buzzBeepBlinkLocked很簡單,不浪費篇幅敘述了。
INotificationListener
notifyPostedLocked方法最后調用notifyPosted方法,我們直接來看看該方法
private void notifyPosted(final ManagedServiceInfo info,final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {final INotificationListener listener = (INotificationListener)info.service;...listener.onNotificationPosted(sbnHolder, rankingUpdate);... }復制代碼這里有一個INotificationListener對象,一看到以I開頭的就可以知道,這里肯定又是一個IPC通信。
查看源碼可以知道,onNotificationPosted的實現是在SystemUI進程中,也就是我們的狀態欄進程。
BaseStatusBar
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@Override public void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {mHandler.post(new Runnable() {@Overridepublic void run() {...boolean isUpdate = mNotificationData.get(sbn.getKey()) != null|| isHeadsUp(sbn.getKey());...if (isUpdate) {updateNotification(sbn, rankingMap);} else {addNotification(sbn, rankingMap);}}}); }復制代碼狀態欄會根據通知的唯一key值來判斷該通知是否是更新還是新增的。
我們以新增的為例來講.addNotification是一個抽象方法,實現是在BaseStatusBar的子類PhoneStatusBar中
PhoneStatusBar
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
public void addNotification(StatusBarNotification notification, RankingMap ranking) {...Entry shadeEntry = createNotificationViews(notification);if (shadeEntry == null) {return;}...addNotificationViews(shadeEntry, ranking);... }復制代碼該方法做了2個重要的事情,一個就是創建Entry實例,另外一個就是將Entry添加到狀態欄上,然后就顯示完成了。
因為createNotificationViews的實現是在父類中,并且該方法十分重要,所以我們先跳過該方法。
先把Entry理解成一條通知,來講addNotificationViews的實現。
先直接將得到的Entry添加到mNotificationData里面
最終updateNotifications會調用PhoneStatusBar中的updateNotificationShade方法
這個mStackScroller是NotificationStackScrollLayout的對象,而這個NotificationStackScrollLayout是一個繼承自ViewGroup的,也就是我們下拉狀態欄看到的整片view的根view.
那么ExpandableNotificationRow也就是對應著每一個通知了. ExpandableNotificationRow是繼承自FrameLayout的
我們前面說到把Entry先理解為一條通知,看到這里,其實添加的是Entry對象里面的row屬性到界面上,也就是ExpandableNotificationRow
createNotificationViews
這個是解答開頭疑問的關鍵。 該方法是BaseStatusBar類的方法。
protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {...// Construct the expanded view.NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);if (!inflateViews(entry, mStackScroller)) {handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);return null;}return entry; }復制代碼這里首先實例化了NotificationData的內部類Entry。
NotificationData是一個十分重要的類,里面有幾個比較重要的數據結構
ArrayMap mEntries = new ArrayMap<>(); //所有Entry的集合
ArrayList mSortedAndFiltered = new ArrayList<>(); //排序后的Entry集合
那這個Entry到底是個什么東西呢?先來看看這個類的定義 public static final class Entry {...public ExpandableNotificationRow row; // the outer expanded viewpublic View expanded; // the inflated RemoteViewspublic View expandedPublic; // for insecure lockscreenspublic View expandedBig;...}復制代碼
從定義里面可以看出,一個Entry對應了一條通知欄的所有Data信息,其中比較重要的是row屬性,前面已經碰到過了。最后添加界面上的也就是這個row。
在inflateViews方法里面,這個row會被賦值,我們來看看row是怎么被賦值的
看完上面的代碼,先來坐個小節,整理下思路。在Entry.row添加到屏幕上前,做了如下的屬性賦值
到這里,一個通知欄從初始化到顯示的流程就講完了,但是最開頭的疑問不是還沒有解答嗎?來看答案
答案
contentView固定高度
在expanded.setContractedChild方法前,傳遞進來的ContentView都還是自義定的view,沒有做高度限制或者系統默認的view. 最后顯示的時候卻被限制了,說明在setContractedChild方法里做了手腳
public void setContractedChild(View child) {...sanitizeContractedLayoutParams(child);addView(child);... }復制代碼private void sanitizeContractedLayoutParams(View contractedChild) {LayoutParams lp = (LayoutParams) contractedChild.getLayoutParams();lp.height = mSmallHeight;contractedChild.setLayoutParams(lp); }復制代碼可以看到在sanitizeContractedLayoutParams方法里面,不論傳遞進來的contentView有多高最后的會被改成mSmallHeight的高度。這個mSmallHeight的值就是在SystemUI里面配置的,64dp
bigview最大高度
在expanded.setExpandedChild的方法里面卻沒有做最大高度的限制,那么最大高度是在哪限制的呢?
這個時候就要看看ExpandableNotificationRow這個根view了
ExpandableNotificationRow繼承自ExpandableView,來看看onMeasure方法
如果bigviewlayoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT則高度就是newHeightSpec。這個newHeightSpec要么是ownMaxHeight 要么是maxChildHeight,而這2個值的最大值就是256dp
如果bigviewlayoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT,最大值也是maxChildHeight 也就是256dp
注意: 這里并沒有顯示bigview的最小高度,所以bigview的高度范圍是可以在(0,256dp ] 區間的
最后
a pic worth thousands of words, tow pics worth double, lol
類圖
Notification_class_diagram.jpg流程圖
Notification_seq_diagram.jpg相關閱讀
Notification之----Android5.0實現原理(一)
Notification之----自定義樣式
Notification之----默認樣式
Notification之----任務棧
總結
以上是生活随笔為你收集整理的Notification之 - Android5.0实现原理(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Binlog参考资料
- 下一篇: Maven 中的pom.xml文件