Android-通知Notification全解析

在Android系統中,Notification是一種具有全局效果的通知,在App開發中也很常用,今天就記錄一下學習Notification的過程。

老規矩,先來一個搞笑段子:

經常熬夜有三大害處:第一,記憶力越來越差;第二,數學水平下降;第四,記憶力越來越差。


這篇文章Copy自xxbs2003的博文。因爲他寫的太好了,並且我不想寫。大笑


Notification

  Notification,俗稱通知,是一種具有全局效果的通知,它展示在屏幕的頂端,首先會表現爲一個圖標的形式,當用戶向下滑動的時候,展示出通知具體的內容。

  因爲Android的快速發展,而Android的版本也快速的升級導致了一些兼容性的問題。對於Notification而言,Android3.0是一個分水嶺,在其之前構建Notification推薦使用NotificationCompate.Builder,它位於android.support.v4.app.NotificationCompat.Builder,是一個Android向下版本的兼容包,而在Android3.0之後,一般推薦使用Notification.Builder構建。本博客主要介紹的是Android4.x的開發,所以在這裏使用Notification.Builder進行講解演示。

  通知一般通過NotificationManager服務發送一個Notification對象來完成通知,NotificationManager是一個重要的系統級服務,該對象位於應用程序的框架層中,應用程序可以通過它向系統發送全局的通知。使用通知的時候,需要創建一個Notification對象用來承載通知的內容,但是一般不會直接通過Notification的構造方法來得到對象,而是使用它的內部類Notification.Builder來實例化一個Builder對象,並設置通知的各項屬性,最後通過Notification.Builder.builder()方法得到一個Notification對象,當獲得這個Notification對象之後,就可以使用NotificationManager.notify()方法發送通知。

  NotificationManager類是一個通知管理器類,這個對象是由系統維護的服務,是以單例模式的方式獲得,所以一般並不直接實例化這個對象。在Activity中,可以使用Activity.getSystemService(String)方法獲取NotificationManager對象,Activity.getSystemService(String)方法可以通過Android系統級服務的句柄,返回對應的對象。在這裏需要返回NotificationManager,所以直接傳遞Context.NOTIFICATION_SERVICE即可。

  雖然通知中提供了各種屬性的設置,但是一個通知對象,有幾個屬性是必須要設置的,其他的屬性均是可選的,必須設置的屬性如下:

  • 小圖標,使用setSamllIcon()方法設置。
  • 標題,使用setContentTitle()方法設置。
  • 文本內容,使用setContentText()方法設置。 

  

更新與移除通知

  在使用NotificationManager.notify()發送通知的時候,需要傳遞一個標識符,用於唯一標識這個通知。對於有些場景,並不是無限的添加新的通知,有時候需要更新原有通知的信息,這個時候可以重寫構建Notification,而使用與之前通知相同標識符來發送通知,這個時候舊的通知就被被新的通知所取代,起到更新通知的效果。

  對於一個通知,當展示在狀態欄之後,但是使用過後,如何取消呢?Android爲我們提供兩種方式移除通知,一種是Notification自己維護,使用setAutoCancel()方法設置是否維護,傳遞一個boolean類型的數據。另外一種方式使用NotificationManager通知管理器對象來維護,它通過notify()發送通知的時候,指定的通知標識Id來操作通知,可以使用cancel(int)來移除一個指定的通知,也可以使用cancelAll()移除所有的通知。

  使用NotificationManager移除指定通知示例:

1         NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
2         mNotificationManager.cancel(0);

 

 PendingIntent

  對於一個通知而言,它顯示的消息是有限的,一般僅用於提示一些概要信息。但是一般簡短的消息,並不能表達需要告訴用戶的全部內容,所以需要綁定一個意圖,當用戶點擊通知的時候,調用一個意圖展示出一個Activity用來顯示詳細的內容。而Notification中,並不使用常規的Intent去傳遞一個意圖,而是使用PendingIntent。

  先來說說Intent和PendingIntent的區別,PendingIntent可以看做是對Intent的包裝,通過名稱可以看出PendingIntent用於處理即將發生的意圖,而Intent用來用來處理馬上發生的意圖。而對於通知來說,它是一系統級的全局通知,並不確定這個意圖被執行的時間。當在應用外部執行PendingIntent時,因爲它保存了觸發應用的Context,使得外部應用可以如在當前應用中一樣,執行PendingIntent裏的Intent,就算執行的時候響應通知的應用已經被銷燬了,也可以通過存在PendingIntent裏的Context照常執行它,並且還可以處理Intent說帶來的額外信息。

  PendingIntent提供了多個靜態的getXxx()方法,用於獲得適用於不同場景的PendingIntent對象。一般需要傳遞的幾個參數都很常規,只介紹一個flag參數,用於標識PendingIntent的構造選擇:

  • FLAG_CANCEL_CURRENT:如果構建的PendingIntent已經存在,則取消前一個,重新構建一個。
  • FLAG_NO_CREATE:如果前一個PendingIntent已經不存在了,將不再構建它。
  • FLAG_ONE_SHOT:表明這裏構建的PendingIntent只能使用一次。
  • FLAG_UPDATE_CURRENT:如果構建的PendingIntent已經存在,則替換它,常用。

 

Notification視覺風格

  Notification有兩種視覺風格,一種是標準視圖(Normal view)、一種是大視圖(Big view)。標準視圖在Android中各版本是通用的,但是對於大視圖而言,僅支持Android4.1+的版本。

  從官方文檔瞭解到,一個標準視圖顯示的大小要保持在64dp高,寬度爲屏幕標準。標準視圖的通知主體內容有一下幾個:


  1. 通知標題。
  2. 大圖標。
  3. 通知內容。
  4. 通知消息。
  5. 小圖標。
  6. 通知的時間,一般爲系統時間,也可以使用setWhen()設置。

  下面通過一個示例,模仿上面效果的通知。

複製代碼
 1 btnNotification.setOnClickListener(new View.OnClickListener() {
 2 
 3             @Override
 4             public void onClick(View v) {
 5                 Bitmap btm = BitmapFactory.decodeResource(getResources(),
 6                         R.drawable.msg);
 7                 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
 8                         MainActivity.this).setSmallIcon(R.drawable.msg)
 9                         .setContentTitle("5 new message")
10                         .setContentText("[email protected]");
11                 mBuilder.setTicker("New message");//第一次提示消息的時候顯示在通知欄上
12                 mBuilder.setNumber(12);
13                 mBuilder.setLargeIcon(btm);
14                 mBuilder.setAutoCancel(true);//自己維護通知的消失
15                 
16                 //構建一個Intent
17                 Intent resultIntent = new Intent(MainActivity.this,
18                         ResultActivity.class);
19                 //封裝一個Intent
20                 PendingIntent resultPendingIntent = PendingIntent.getActivity(
21                         MainActivity.this, 0, resultIntent,
22                         PendingIntent.FLAG_UPDATE_CURRENT);
23                 // 設置通知主題的意圖
24                 mBuilder.setContentIntent(resultPendingIntent);
25                 //獲取通知管理器對象
26                 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
27                 mNotificationManager.notify(0, mBuilder.build());
28             }
29         });
複製代碼

  顯示效果:



  而對於大視圖(Big View)而言,它的細節區域只能顯示256dp高度的內容,並且只對Android4.1+之後的設備才支持,它比標準視圖不一樣的地方,均需要使用setStyle()方法設定,它大致的效果如下:


  setStyle()傳遞一個NotificationCompat.Style對象,它是一個抽象類,Android爲我們提供了三個實現類,用於顯示不同的場景。分別是:

  • NotificationCompat.BigPictureStyle, 在細節部分顯示一個256dp高度的位圖。
  • NotificationCompat.BigTextStyle,在細節部分顯示一個大的文本塊。
  • NotificationCompat.InboxStyle,在細節部分顯示一段行文本。

  如果僅僅顯示一個圖片,使用BigPictureStyle是最方便的;如果需要顯示一個富文本信息,則可以使用BigTextStyle;如果僅僅用於顯示一個文本的信息,那麼使用InboxStyle即可。後面會以一個示例來展示InboxStyle的使用,模仿上面圖片的顯示。

   實現代碼:

複製代碼
 1         btnBigViewNotification.setOnClickListener(new View.OnClickListener() {
 2 
 3             @Override
 4             public void onClick(View v) {
 5 
 6                 Bitmap btm = BitmapFactory.decodeResource(getResources(),
 7                         R.drawable.msg);
 8                 Intent intent = new Intent(MainActivity.this,
 9                         ResultActivity.class);
10 
11                 PendingIntent pendingIntent = PendingIntent.getActivity(
12                         MainActivity.this, 0, intent,
13                         PendingIntent.FLAG_CANCEL_CURRENT);
14 
15                 Notification noti = new NotificationCompat.Builder(
16                         MainActivity.this)
17                         .setSmallIcon(R.drawable.msg)
18                         .setLargeIcon(btm)
19                         .setNumber(13)
20                         .setContentIntent(pendingIntent)
21                         .setStyle(
22                                 new NotificationCompat.InboxStyle()
23                                         .addLine(
24                                                 "M.Twain (Google+) Haiku is more than a cert...")
25                                         .addLine("M.Twain Reminder")
26                                         .addLine("M.Twain Lunch?")
27                                         .addLine("M.Twain Revised Specs")
28                                         .addLine("M.Twain ")
29                                         .addLine(
30                                                 "Google Play Celebrate 25 billion apps with Goo..")
31                                         .addLine(
32                                                 "Stack Exchange StackOverflow weekly Newsl...")
33                                         .setBigContentTitle("6 new message")
34                                         .setSummaryText("[email protected]"))
35                         .build();
36 
37                 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
38                 mNotificationManager.notify(0, noti);
39             }
40         });


進度條樣式的通知

  對於一個標準通知,有時候顯示的消息並不一定是靜態的,還可以設定一個進度條用於顯示事務完成的進度。

  Notification.Builder類中提供一個setProgress(int max,int progress,boolean indeterminate)方法用於設置進度條,max用於設定進度的最大數,progress用於設定當前的進度,indeterminate用於設定是否是一個確定進度的進度條。通過indeterminate的設置,可以實現兩種不同樣式的進度條,一種是有進度刻度的(true),一種是循環流動的(false)。下面分別用兩個示例演示:

  有進度的進度條,實現代碼: 

複製代碼
 1         btnProgreNotification.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 5                 builder = new NotificationCompat.Builder(MainActivity.this)
 6                         .setSmallIcon(R.drawable.ic_launcher)
 7                         .setContentTitle("Picture Download")
 8                         .setContentText("Download in progress");
 9                 builder.setAutoCancel(true);
10                 //通過一個子線程,動態增加進度條刻度
11                 new Thread(new Runnable() {
12                     @Override
13                     public void run() {
14                         int incr;
15                         for (incr = 0; incr <= 100; incr += 5) {
16                             builder.setProgress(100, incr, false);
17                             manager.notify(0, builder.build());
18                             try {
19                                 Thread.sleep(300);
20                             } catch (InterruptedException e) {
21                                 Log.i(TAG, "sleep failure");
22                             }
23                         }
24                         builder.setContentText("Download complete")
25                                 .setProgress(0, 0, false);
26                         manager.notify(0, builder.build());
27                     }
28                 }).start();
29             }
30         });
複製代碼

  顯示效果:


   對於循環流動的進度條,下面是實現代碼:

複製代碼
 1         btnProNotification.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 5                 builder = new NotificationCompat.Builder(MainActivity.this)
 6                         .setSmallIcon(R.drawable.ic_launcher)
 7                         .setContentTitle("Picture Download")
 8                         .setContentText("Download in progress");
 9                 builder.setProgress(0, 0, true);//設置爲true,表示流動
10                 manager.notify(0, builder.build());
11 
12                 //5秒之後還停止流動
13                 new Thread(new Runnable() {
14                     @Override
15                     public void run() {
16                         try {
17                             Thread.sleep(5000);
18                         } catch (InterruptedException e) {
19                             e.printStackTrace();
20                         }
21                         builder.setProgress(100, 100, false);//設置爲true,表示刻度
22                         manager.notify(0, builder.build());
23                     }
24                 }).start();
25             }
26         });
複製代碼

  效果展示:



自定義通知

  和Toast一樣,通知也可以使用自定義的XML來自定義樣式,但是對於通知而言,因爲它的全局性,並不能簡單的通過inflate膨脹出一個View,因爲可能觸發通知的時候,響應的App已經關閉,無法獲取當指定的XML佈局文件。所以需要使用單獨的一個RemoteViews類來操作。

  RemoteViews,描述了一個視圖層次的結構,可以顯示在另一個進程。層次結構也是從佈局文件中“膨脹”出一個視圖,這個類,提供了一些基本的操作求改其膨脹的內容。

  RemoteViews提供了多個構造函數,一般使用RemoteViews(String packageName,int layoutId)。第一個參數爲包的名稱,第二個爲layout資源的Id。當獲取到RemoteViews對象之後,可以使用它的一系列setXxx()方法通過控件的Id設置控件的屬性。最後使用NotificationCompat.Builder.setContent(RemoteViews)方法設置它到一個Notification中。

  下面通過一個示例展示它:

  自定義的佈局XML代碼:

複製代碼
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:padding="10dp" >
 6 
 7     <ImageView
 8         android:id="@+id/imageNo"
 9         android:layout_width="wrap_content"
10         android:layout_height="match_parent"
11         android:layout_alignParentLeft="true"
12         android:layout_marginRight="10dp" />
13 
14     <TextView
15         android:id="@+id/titleNo"
16         android:layout_width="wrap_content"
17         android:layout_height="wrap_content"
18         android:layout_toRightOf="@id/imageNo" />
19 
20     <TextView
21         android:id="@+id/textNo"
22         android:layout_width="wrap_content"
23         android:layout_height="wrap_content"
24         android:layout_below="@id/titleNo"
25         android:layout_toRightOf="@id/imageNo" />
26 
27 </RelativeLayout>
複製代碼

  實現代碼:

複製代碼
 1         btnCustomNotification.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 RemoteViews contentViews = new RemoteViews(getPackageName(),
 5                         R.layout.custom_notification);
 6                 //通過控件的Id設置屬性
 7                 contentViews
 8                         .setImageViewResource(R.id.imageNo, R.drawable.btm1);
 9                 contentViews.setTextViewText(R.id.titleNo, "自定義通知標題");
10                 contentViews.setTextViewText(R.id.textNo, "自定義通知內容");
11 
12                 Intent intent = new Intent(MainActivity.this,
13                         ResultActivity.class);
14 
15                 PendingIntent pendingIntent = PendingIntent.getActivity(
16                         MainActivity.this, 0, intent,
17                         PendingIntent.FLAG_CANCEL_CURRENT);
18                 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
19                         MainActivity.this).setSmallIcon(R.drawable.ic_launcher)
20                         .setContentTitle("My notification")
21                         .setTicker("new message");
22                 mBuilder.setAutoCancel(true);
23 
24                 mBuilder.setContentIntent(pendingIntent);
25                 mBuilder.setContent(contentViews);
26                 mBuilder.setAutoCancel(true);
27                 NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
28                 mNotificationManager.notify(10, mBuilder.build());
29             }
30         });
複製代碼

  效果展示:



設定提示響應

  對於有些通知,需要調用一些設備的資源,使用戶能更快的發現有新通知,一般可設定的響應有:鈴聲、閃光燈、震動。對於這三個屬性,NotificationCompat.Builder提供了三個方法設定:

  • setSound(Uri sound):設定一個鈴聲,用於在通知的時候響應。傳遞一個Uri的參數,格式爲“file:///mnt/sdcard/Xxx.mp3”。
  • setLights(int argb, int onMs, int offMs):設定前置LED燈的閃爍速率,持續毫秒數,停頓毫秒數。
  • setVibrate(long[] pattern):設定震動的模式,以一個long數組保存毫秒級間隔的震動。

  大多數時候,我們並不需要設定一個特定的響應效果,只需要遵照用戶設備上系統通知的效果即可,那麼可以使用setDefaults(int)方法設定默認響應參數,在Notification中,對它的參數使用常量定義了,我們只需使用即可:

  • DEFAULT_ALL:鈴聲、閃光、震動均系統默認。
  • DEFAULT_SOUND:系統默認鈴聲。
  • DEFAULT_VIBRATE:系統默認震動。
  • DEFAULT_LIGHTS:系統默認閃光。

  而在Android中,如果需要訪問硬件設備的話,是需要對其進行授權的,所以需要在清單文件AndroidManifest.xml中增加兩個授權,分別授予訪問振動器與閃光燈的權限:

1     <!-- 閃光燈權限 -->
2     <uses-permission android:name="android.permission.FLASHLIGHT"/>
3     <!-- 振動器權限 -->
4     <uses-permission android:name="android.permission.VIBRATE"/>

 

  因爲只是一個屬性的設定,並且大部分時候,使用系統設定即可,這裏就不提供代碼示例了。 


總結

  通知算是Android中比較常用的一個功能,可以保持自己App的長存,在用戶沒有進入App的時候,也提供了與用戶交互的可能。


本期節目就到這裏,感謝大家收看,下期再見!

  展示效果:


發佈了40 篇原創文章 · 獲贊 88 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章