Android_Notificaiton源碼研究

一辰企,研究背景:

起點Android客戶端670版本有個需求蓬戚,就是游戲下載在通知欄顯示。為了把其功能實現(xiàn)的優(yōu)雅滤祖,特此對此知識進(jìn)行一番深入的研究,希望達(dá)到的目的是實現(xiàn)多樣化的通知瓶籽,應(yīng)付各種先關(guān)需求變化。

二埂材,結(jié)構(gòu)圖:

Notification大家都不陌生塑顺,其實用起來也很簡單,有關(guān)工作原理如下:

通過以上簡單的結(jié)構(gòu)圖不難看出,這個Notification的生命周期其實是一個sevice的生命周期严拒,全權(quán)有NotificationManagerSerice來進(jìn)行管理扬绪。

下面會詳細(xì)分析到源碼。

三裤唠,運用介紹:

關(guān)于如何運用挤牛,我簡單的概括如下幾個步驟:

Step1:獲取NotificationManager對象

/** 獲取Notification管理器。*/

mNotificationManager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

Step2: 創(chuàng)建Notification對象

(有兩種方式种蘸,一種直接new一個notificaiton對象墓赴,一種是創(chuàng)建一個builder中間對象,最后動過Bulder.build()方法達(dá)到目的航瞭,這個我覺得是看個人用法習(xí)慣)

builder=newNotification.Builder(this);

Step3:關(guān)聯(lián)intent對象

/*** 給Notification設(shè)置Intent诫硕,單擊Notification會發(fā)出這個Intent。*/

builder.setContentIntent(mPendingIntent);

這個intent我們一般會設(shè)置成PendingIntent刊侯,指的是當(dāng)觸發(fā)單個通知的時候發(fā)生的操作章办。提到PendingIntent就簡單說一下他和intent的區(qū)別吧,pendingintent顧名思義不會立即被當(dāng)前的activity所執(zhí)行滨彻,他算是intent的一個包裝藕届,保留了當(dāng)前app的上下文,也就是說不依賴于當(dāng)前的activity亭饵,即便是當(dāng)前的activty消失了休偶,外界的app也會通過保留的上下文來執(zhí)行pendingintent,通常使用他的場景就是鬧鐘和通知了冬骚。

Step4:執(zhí)行notify

/** 發(fā)送Notification提醒椅贱。*/

mNotificationManager.notify(0,builder.build());

以上四個步驟四最簡單不過的操作了,如果想更復(fù)雜點只冻,可以自定義自己的RemoteView等庇麦。

接下來我主要講notificaiton的新特性和notificaiton的源碼機(jī)制:

A.新特性:

5.0提出了MediaStyle這一設(shè)置,就是你經(jīng)诚驳拢看到鎖屏狀態(tài)也能收到某些app的通知界面山橄,這些通知會直接展現(xiàn)在手機(jī)界面上。MediaStyle實現(xiàn)了這一功能舍悯,可以將你通過Notification.Builder.addAction()添加的動作按鈕航棱,緊湊的呈現(xiàn)出來。?通過設(shè)置setSession可以配 置notification控制指定的MediaSession萌衬。通 過.setVisibility(Notification.VISIBILITY_PUBLIC)以使其出現(xiàn)在鎖屏界面上饮醇。

如何實現(xiàn),其實很簡單:

private voidshow() {

MediaSession mediaSession =newMediaSession(this,"My Special Notification!");

builder.setVisibility(Notification.VISIBILITY_PUBLIC);// 設(shè)置能看的權(quán)限秕豫,這個代表鎖屏狀態(tài)下也能看

builder.setSmallIcon(R.drawable.about_icon);

builder.setContentTitle("My Notification朴艰!");

builder.setContentText("Test-test-test");

mIntent=newIntent(this,QDTestActivity.class);

PendingIntent p1 = PendingIntent.getActivity(this,0,mIntent,0);

builder.addAction(R.drawable.about_icon,"Move",p1);// 可以設(shè)置不同的pendingintent

builder.addAction(R.drawable.about_icon,"Pause",p1);

builder.addAction(R.drawable.about_icon,"Stop",p1);

builder.setStyle(newNotification.MediaStyle().setMediaSession(mediaSession.getSessionToken()));

mNotificationManager.notify(0,builder.build());

}

備注:5.0一下的機(jī)型是不支持的观蓄,否則會報以下的錯誤:

Process: com.qidian.liyongli.mynotificationtest, PID: 22064

java.lang.NoClassDefFoundError: android.media.session.MediaSession

B.Notificaiton機(jī)制:

我們知道應(yīng)用程序如果要在通知欄彈一個消息,看起來只有幾行代碼祠墅,實際上有兩個比較大的框架在里面侮穿。一個是通過 PendingIntent的靜態(tài)函數(shù)getActivity()獲取一個PendingIntent對象;一個是獲取 NotificationManagerService的服務(wù)代理對象調(diào)用notify()來post一個消息出去毁嗦。

通過PendingIntent的靜態(tài)函數(shù)getActivity()獲取一個PendingIntent對象亲茅,這個里面其實做了幾件隱蔽的事情:

其一是調(diào)用 了AMS的getIntentSender()函數(shù),在AMS中創(chuàng)建了一個PendingIntentRecord記錄塊并保存在 mIntentSenderRecords中狗准;

其二是:PendingIntentRecord繼承 IIntentSender.Stub克锣,getIntentSender()函數(shù)返回了PendingIntentRecord的binder引 用,binder引用保存在PendingIntent.mTarget變量中驶俊;

其三是上層應(yīng)用的創(chuàng)建的Intent保存在 PendingIntentRecord中供后續(xù)觸發(fā)娶耍。

Notification的發(fā)送也很簡單,首先將消息發(fā)送到系統(tǒng)層饼酿,NotificationManagerService會為之創(chuàng)建一個 NotificationRecord榕酒,保存在mNotificationList、mNotificationsByKey.put中故俐,然后把 ManagedServices.services中所有對象取出來(第一部分中為通知欄的INotificationListenerWrapper的 binder引用創(chuàng)建了一個ManagedServiceInfo)想鹰,然后調(diào)用onNotificationPosted()將通知發(fā)送給通知欄。

接下來就直接走進(jìn)源碼吧药版,撿關(guān)鍵的說辑舷,直接從notify方法入手:

public voidnotify(intid,Notification notification)

{

notify(null,id,notification);// 調(diào)用notificaiton的地方

}

public voidnotify(String tag, intid,Notification notification) {

int[] idOut =new int[1];

INotificationManager service = getService();// 拿到真正的service INotificationManager是一個通信接口,是一個aidl文件

String pkg = mContext.getPackageName();

if(notification.sound!=null) {

notification.sound= notification.sound.getCanonicalUri();

if(StrictMode.vmFileUriExposureEnabled()) {

notification.sound.checkFileUriExposed("Notification.sound");

}

}

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

Notification stripped = notification.clone();

Builder.stripForDelivery(stripped);

try{

service.enqueueNotificationWithTag(pkg,mContext.getOpPackageName(),tag,id,

stripped,idOut,UserHandle.myUserId());// 實際上是先對notificaiton進(jìn)行一個系統(tǒng)保存

if(id != idOut[0]) {

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

}

}catch(RemoteException e) {}

由于源碼太多槽片,下面我就根據(jù)邏輯流程撿重點的說何缓。

首先你消息如何保存。

答:NotificaitonRecord还栓,用它創(chuàng)建一個arraylist碌廓,所有的消息都暫存于此。但是創(chuàng)建一個這個對象時先判斷消息的有效性剩盒,特別提出的是數(shù)量不能超過50個谷婆。

消息如何發(fā)送。

答:通過int index = indexOfNotificationLocked(n.getKey());獲取是否已經(jīng)發(fā)送過此notification辽聊,如果是新發(fā)送的notification就走新增流程mNotificationList.add(r);

如果有發(fā)送過纪挎,就獲取old = mNotificationList.get(index);,后面走更新流程?mNotificationList.set(index, r); mUsageStats.registerPostedByApp(r)和mUsageStats.registerUpdatedByApp(r, old);都是更新stats.numPostedByApp++

如:

如果notification的icon不為空,調(diào)用mListeners.notifyPostedLocked(n, oldSbn);如果notification的icon為空跟匆,并且存在舊的NotificationRecord并且沒被canceled异袄,就調(diào)用mListeners.notifyRemovedLocked(n);取消這個notification

有一個新的notification,以異步方式通知所有l(wèi)isteners

消息是如何添加的玛臂。

答:addNotification(sbn, rankingMap);

inflateViewsForHeadsUp 構(gòu)建HeadsUp

mHeadsUpNotificationView.showNotification 顯示HeadsUp

3.createNotificationViews創(chuàng)建NotificationViews

4.tick(notification, true); 顯示tiker

5.該方法主要構(gòu)造Notification Icons以及expanded View

addNotificationViews(shadeEntry, ranking);

重新計算滑動窗口的位置和標(biāo)題隙轻。

setAreThereNotifications();

刷新一下ExpandedView的位置updateExpandedViewPos(EXPANDED_LEAVE_ALONE);

四:遇到的問題:

1.使用Bulder結(jié)合RemoteView的時候埠帕,不起作用。

原因:沒有使用setSamllIcon(5.0以下版本)

解決方案:添加如下代碼

builder.setSmallIcon(R.drawable.aa);

2. Notification.Bulder不可用玖绿。

原因:不兼容API11以下的版本。

解決方案:改變sdk的版本或者在AndroidManifest.xml寫入:

android:minSdkVersion="11"/>

if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.HONEYCOMB) {

builder=newNotification.Builder(this);

}

3. 如何使手機(jī)通知欄的高度適應(yīng)不來自定義的樣式

原因:4.0以上版本才能支持

解決方案:

notification.bigContentView = remoteView;

4. 有的時候發(fā)現(xiàn)添加的pendingintent不起作用叁巨。

原因:

manager.notify(COUNT++,notification);

當(dāng)有只有一個notificaiton實例時斑匪,Count只能唯一,這里不能加加锋勺。

解決方案:改成一個常量值蚀瘸,這是唯一標(biāo)識。

五庶橱,總結(jié):

總之關(guān)于notificaiton還有很多其他的小知識贮勃,比如如何實現(xiàn)信號燈,震動等苏章。這次的源碼分析也是走的主干邏輯寂嘉,有關(guān)更多的知識歡迎補(bǔ)充,改正和吐槽枫绅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泉孩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子并淋,更是在濱河造成了極大的恐慌寓搬,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件县耽,死亡現(xiàn)場離奇詭異句喷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)兔毙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門唾琼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞒御,你說我怎么就攤上這事父叙。” “怎么了肴裙?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵趾唱,是天一觀的道長。 經(jīng)常有香客問我蜻懦,道長甜癞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任宛乃,我火速辦了婚禮悠咱,結(jié)果婚禮上蒸辆,老公的妹妹穿的比我還像新娘。我一直安慰自己析既,他們只是感情好躬贡,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眼坏,像睡著了一般拂玻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宰译,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天檐蚜,我揣著相機(jī)與錄音,去河邊找鬼沿侈。 笑死闯第,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缀拭。 我是一名探鬼主播咳短,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼智厌!你這毒婦竟也來了诲泌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤铣鹏,失蹤者是張志新(化名)和其女友劉穎敷扫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诚卸,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡葵第,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了合溺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卒密。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棠赛,靈堂內(nèi)的尸體忽然破棺而出哮奇,到底是詐尸還是另有隱情,我是刑警寧澤睛约,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布鼎俘,位于F島的核電站,受9級特大地震影響辩涝,放射性物質(zhì)發(fā)生泄漏贸伐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一怔揩、第九天 我趴在偏房一處隱蔽的房頂上張望捉邢。 院中可真熱鬧脯丝,春花似錦、人聲如沸伏伐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藐翎。三九已至砰苍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阱高,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工茬缩, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留赤惊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓凰锡,卻偏偏與公主長得像未舟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子掂为,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容