Notification使用詳解之三:經過服務更新進度通知&在Activity中監聽服務進度

上次咱們講到如何實現一個可更新的進度通知,實現的方式是啓動一個線程模擬一個下載任務,而後根據任務進度向UI線程消息隊列發送進度消息,UI線 程根據進度消息更新通知的UI界面。但是在實際應用中,咱們通常會將上傳、下載等比較耗時的後臺任務以服務的形式運行,更新進度通知也是交由後臺服務來完 成的。 不過有的時候,除了在通知裏面顯示進度信息,咱們也要在Activity中顯示當前進度,不少下載系統都有這樣的功能,例如Android自帶瀏覽器的下 載系統、QQ瀏覽器的下載系統等等。那麼如何實現這一功能呢?實現方式有不少,咱們今天先來介紹其中的一種:在Activity中主動監聽服務的進度。 html

具體的思路是:讓Activity與後臺服務綁定,經過中間對象Binder的實例操做後臺服務,獲取進度信息和服務的狀態以及在必要的時候中止服務。 java

關於服務的生命週期,若是有些朋友們不太熟悉的話,能夠去查閱相關資料;若是之後有時間,我可能也會總結一些與服務相關的知識。 android

爲了讓你們對這個過程更清晰一些,在上代碼以前,咱們先來看看幾個截圖: 瀏覽器

 

整個過程如上圖所示:在咱們點擊開始按鈕後,下載任務開始運行,同事更新通知上的進度,當前Activity也從後臺服務獲取進度信息,顯示到按鈕下方;當咱們點擊通知後,跳轉到下載管理界面,在這裏咱們也從後臺服務獲取進度,還能夠作取消任務等操做。 app

瞭解了整個過程的狀況後,咱們就來分析一下具體的代碼實現。 ide

首先是/res/main.xml佈局文件: 佈局

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent">  
  6.     <Button  
  7.         android:layout_width="fill_parent"  
  8.         android:layout_height="wrap_content"  
  9.         android:text="start"  
  10.         android:onClick="start"/>  
  11.     <TextView  
  12.         android:id="@+id/text"  
  13.         android:layout_width="fill_parent"  
  14.         android:layout_height="wrap_content"  
  15.         android:gravity="center"/>  
  16. </LinearLayout>  

其中Button是用來啓動服務的,TextView是用來顯示進度信息的。 this

而後再在看一下MainActivity.java的代碼: spa

[java] view plain copy
  1. package com.scott.notification;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11. import android.os.Message;  
  12. import android.view.View;  
  13. import android.widget.TextView;  
  14.   
  15. public class MainActivity extends Activity {  
  16.   
  17.     private DownloadService.DownloadBinder binder;  
  18.     private TextView text;  
  19.       
  20.     private boolean binded;  
  21.   
  22.     private Handler handler = new Handler() {  
  23.         public void handleMessage(android.os.Message msg) {  
  24.             int progress = msg.arg1;  
  25.             text.setText("downloading..." + progress + "%");  
  26.         };  
  27.     };  
  28.   
  29.     private ServiceConnection conn = new ServiceConnection() {  
  30.   
  31.         @Override  
  32.         public void onServiceConnected(ComponentName name, IBinder service) {  
  33.             binder = (DownloadService.DownloadBinder) service;  
  34.             binded = true;  
  35.             // 開始下載  
  36.             binder.start();  
  37.             // 監聽進度信息  
  38.             listenProgress();  
  39.         }  
  40.   
  41.         @Override  
  42.         public void onServiceDisconnected(ComponentName name) {  
  43.         }  
  44.     };  
  45.   
  46.     @Override  
  47.     public void onCreate(Bundle savedInstanceState) {  
  48.         super.onCreate(savedInstanceState);  
  49.         setContentView(R.layout.main);  
  50.         text = (TextView) findViewById(R.id.text);  
  51.     }  
  52.   
  53.     @Override  
  54.     protected void onDestroy() {  
  55.         super.onDestroy();  
  56.         if (binded) {  
  57.             unbindService(conn);              
  58.         }  
  59.     }  
  60.   
  61.     public void start(View view) {  
  62.         if (binded) {  
  63.             binder.start();  
  64.             listenProgress();  
  65.             return;  
  66.         }  
  67.         Intent intent = new Intent(this, DownloadService.class);  
  68.         startService(intent);   //若是先調用startService,則在多個服務綁定對象調用unbindService後服務仍不會被銷燬  
  69.         bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  70.     }  
  71.   
  72.     /** 
  73.      * 監聽進度 
  74.      */  
  75.     private void listenProgress() {  
  76.         new Thread() {  
  77.             public void run() {  
  78.                 while (!binder.isCancelled() && binder.getProgress() <= 100) {  
  79.                     int progress = binder.getProgress();  
  80.                     Message msg = handler.obtainMessage();  
  81.                     msg.arg1 = progress;  
  82.                     handler.sendMessage(msg);  
  83.                     if (progress == 100) {  
  84.                         break;  
  85.                     }  
  86.                     try {  
  87.                         Thread.sleep(200);  
  88.                     } catch (InterruptedException e) {  
  89.                         e.printStackTrace();  
  90.                     }  
  91.                 }  
  92.             };  
  93.         }.start();  
  94.     }  
  95. }  
咱們能夠看到,當點擊開始按鈕後,以bindService的方式綁定服務,用獲取到的DownloadService.DownloadBinder實例啓動服務,並在Activity中啓動一個線程監聽服務的進度信息,及時的顯示到按鈕下方。

服務類DownloadService.java代碼以下: .net

[java] view plain copy
  1. package com.scott.notification;  
  2.   
  3. import android.app.Notification;  
  4. import android.app.NotificationManager;  
  5. import android.app.PendingIntent;  
  6. import android.app.Service;  
  7. import android.content.Context;  
  8. import android.content.Intent;  
  9. import android.os.Binder;  
  10. import android.os.Handler;  
  11. import android.os.IBinder;  
  12. import android.os.Message;  
  13. import android.widget.RemoteViews;  
  14.   
  15. public class DownloadService extends Service {  
  16.   
  17.     private static final int NOTIFY_ID = 0;  
  18.     private boolean cancelled;  
  19.     private int progress;  
  20.   
  21.     private Context mContext = this;  
  22.   
  23.     private NotificationManager mNotificationManager;  
  24.     private Notification mNotification;  
  25.   
  26.     private DownloadBinder binder = new DownloadBinder();  
  27.   
  28.     private Handler handler = new Handler() {  
  29.         public void handleMessage(android.os.Message msg) {  
  30.             switch (msg.what) {  
  31.             case 1:  
  32.                 int rate = msg.arg1;  
  33.                 if (rate < 100) {  
  34.                     // 更新進度  
  35.                     RemoteViews contentView = mNotification.contentView;  
  36.                     contentView.setTextViewText(R.id.rate, rate + "%");  
  37.                     contentView.setProgressBar(R.id.progress, 100, rate, false);  
  38.                 } else {  
  39.                     // 下載完畢後變換通知形式  
  40.                     mNotification.flags = Notification.FLAG_AUTO_CANCEL;  
  41.                     mNotification.contentView = null;  
  42.                     Intent intent = new Intent(mContext, FileMgrActivity.class);  
  43.                     // 告知已完成  
  44.                     intent.putExtra("completed""yes");  
  45.                     //更新參數,注意flags要使用FLAG_UPDATE_CURRENT  
  46.                     PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);  
  47.                     mNotification.setLatestEventInfo(mContext, "下載完成""文件已下載完畢", contentIntent);  
  48.                     stopSelf();//停掉服務自身  
  49.                 }  
  50.   
  51.                 // 最後別忘了通知一下,不然不會更新  
  52.                 mNotificationManager.notify(NOTIFY_ID, mNotification);  
  53.                 break;  
  54.             case 0:  
  55.                 // 取消通知  
  56.                 mNotificationManager.cancel(NOTIFY_ID);  
  57.                 break;  
  58.             }  
  59.         };  
  60.     };  
  61.   
  62.     @Override  
  63.     public void onCreate() {  
  64.         super.onCreate();  
  65.         mNotificationManager = (NotificationManager) getSystemService(android.content.Context.NOTIFICATION_SERVICE);  
  66.     }  
  67.   
  68.     @Override  
  69.     public IBinder onBind(Intent intent) {  
  70.         // 返回自定義的DownloadBinder實例  
  71.         return binder;  
  72.     }  
  73.   
  74.     @Override  
  75.     public void onDestroy() {  
  76.         super.onDestroy();  
  77.         cancelled = true// 取消下載線程  
  78.     }  
  79.       
  80.     /** 
  81.      * 建立通知 
  82.      */  
  83.     private void setUpNotification() {  
  84.         int icon = R.drawable.down;  
  85.         CharSequence tickerText = "開始下載";  
  86.         long when = System.currentTimeMillis();  
  87.         mNotification = new Notification(icon, tickerText, when);  
  88.   
  89.         // 放置在"正在運行"欄目中  
  90.         mNotification.flags = Notification.FLAG_ONGOING_EVENT;  
  91.   
  92.         RemoteViews contentView = new RemoteViews(mContext.getPackageName(), R.layout.download_notification_layout);  
  93.         contentView.setTextViewText(R.id.fileName, "AngryBird.apk");  
  94.         // 指定個性化視圖  
  95.         mNotification.contentView = contentView;  
  96.   
  97.         Intent intent = new Intent(this, FileMgrActivity.class);  
  98.         PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);  
  99.         // 指定內容意圖  
  100.         mNotification.contentIntent = contentIntent;  
  101.         mNotificationManager.notify(NOTIFY_ID, mNotification);  
  102.     }  
  103.   
  104.     /** 
  105.      * 下載模塊 
  106.      */  
  107.     private void startDownload() {  
  108.         cancelled = false;  
  109.         int rate = 0;  
  110.         while (!cancelled && rate < 100) {  
  111.             try {  
  112.                 // 模擬下載進度  
  113.                 Thread.sleep(500);  
  114.                 rate = rate + 5;  
  115.             } catch (InterruptedException e) {  
  116.                 e.printStackTrace();  
  117.             }  
  118.             Message msg = handler.obtainMessage();  
  119.             msg.what = 1;  
  120.             msg.arg1 = rate;  
  121.             handler.sendMessage(msg);  
  122.   
  123.             this.progress = rate;  
  124.         }  
  125.         if (cancelled) {  
  126.             Message msg = handler.obtainMessage();  
  127.             msg.what = 0;  
  128.             handler.sendMessage(msg);  
  129.         }  
  130.     }  
  131.   
  132.     /** 
  133.      * DownloadBinder中定義了一些實用的方法 
  134.      *  
  135.      * @author user 
  136.      *  
  137.      */  
  138.     public class DownloadBinder extends Binder {  
  139.   
  140.         /** 
  141.          * 開始下載 
  142.          */  
  143.         public void start() {  
  144.             //將進度歸零  
  145.             progress = 0;  
  146.             //建立通知  
  147.             setUpNotification();  
  148.             new Thread() {  
  149.                 public void run() {  
  150.                     //下載  
  151.                     startDownload();  
  152.                 };  
  153.             }.start();  
  154.         }  
  155.   
  156.         /** 
  157.          * 獲取進度 
  158.          *  
  159.          * @return  
  160.          */  
  161.         public int getProgress() {  
  162.             return progress;  
  163.         }  
  164.   
  165.         /** 
  166.          * 取消下載 
  167.          */  
  168.         public void cancel() {  
  169.             cancelled = true;  
  170.         }  
  171.   
  172.         /** 
  173.          * 是否已被取消 
  174.          *  
  175.          * @return  
  176.          */  
  177.         public boolean isCancelled() {  
  178.             return cancelled;  
  179.         }  
  180.     }  
  181. }  

咱們看到,在服務中有個DownloadBinder類,它繼承自Binder,定義了一系列方法,獲取服務狀態以及操做當前服務,剛纔咱們在 MainActivity中獲取的就是這個類的實例。最後,不要忘了在AndroidManifest.xml中配置該服務。關於進度通知的佈局文件/res/layout/download_notification_layout.xml,在這裏就不需貼出了,朋友們能夠參考一下Notification使用詳解之二中進度通知佈局的具體代碼。

下面咱們來介紹一下FileMgrActivity,它就是點擊通知以後跳轉到的界面,佈局文件/res/filemgr.xml以下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent">  
  6.     <ProgressBar   
  7.         android:id="@+id/progress"  
  8.         style="?android:attr/progressBarStyleHorizontal"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:max="100"  
  12.         android:progress="0"/>  
  13.     <Button  
  14.         android:id="@+id/cancel"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:text="cancel"  
  18.         android:onClick="cancel"/>  
  19. </LinearLayout>  

咱們來看一下FileMgrActivity.java具體的代碼:

[java] view plain copy
  1. package com.scott.notification;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ComponentName;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.ServiceConnection;  
  8. import android.os.Bundle;  
  9. import android.os.Handler;  
  10. import android.os.IBinder;  
  11. import android.os.Message;  
  12. import android.view.View;  
  13. import android.widget.Button;  
  14. import android.widget.ProgressBar;  
  15.   
  16. public class FileMgrActivity extends Activity {  
  17.     private DownloadService.DownloadBinder binder;  
  18.     private ProgressBar progressBar;  
  19.     private Button cancel;  
  20.     private boolean binded;  
  21.       
  22.     private Handler handler = new Handler() {  
  23.         public void handleMessage(android.os.Message msg) {  
  24.             int progress = msg.arg1;  
  25.             progressBar.setProgress(progress);  
  26.             if (progress == 100) {  
  27.                 cancel.setEnabled(false);  
  28.             }  
  29.         };  
  30.     };  
  31.       
  32.     private ServiceConnection conn = new ServiceConnection() {  
  33.           
  34.         @Override  
  35.         public void onServiceConnected(ComponentName name, IBinder service) {  
  36.             binder = (DownloadService.DownloadBinder) service;  
  37.             //監聽進度信息  
  38.             listenProgress();  
  39.         }  
  40.           
  41.         @Override  
  42.         public void onServiceDisconnected(ComponentName name) {  
  43.         }  
  44.     };  
  45.       
  46.     @Override  
  47.     public void onCreate(Bundle savedInstanceState) {  
  48.         super.onCreate(savedInstanceState);  
  49.         setContentView(R.layout.filemgr);  
  50.         progressBar = (ProgressBar) findViewById(R.id.progress);  
  51.         cancel = (Button) findViewById(R.id.cancel);  
  52.           
  53.         if ("yes".equals(getIntent().getStringExtra("completed"))) {  
  54.             //若是已完成,則不需再綁定service  
  55.             progressBar.setProgress(100);  
  56.               
  57.             cancel.setEnabled(false);  
  58.         } else {  
  59.             //綁定service  
  60.             Intent intent = new Intent(this, DownloadService.class);  
  61.             bindService(intent, conn, Context.BIND_AUTO_CREATE);  
  62.             binded = true;  
  63.         }  
  64.     }  
  65.       
  66.     @Override  
  67.     protected void onDestroy() {  
  68.         super.onDestroy();  
  69.         //若是是綁定狀態,則取消綁定  
  70.         if (binded) {  
  71.             unbindService(conn);  
  72.         }  
  73.     }  
  74.       
  75.     public void cancel(View view) {  
  76.         //取消下載  
  77.         binder.cancel();  
  78.     }  
  79.   
  80.     /** 
  81.      * 監聽進度信息 
  82.      */  
  83.     private void listenProgress() {  
  84.         new Thread() {  
  85.             public void run() {  
  86.                 while (!binder.isCancelled() && binder.getProgress() <= 100) {  
  87.                     int progress = binder.getProgress();  
  88.                     Message msg = handler.obtainMessage();  
  89.                     msg.arg1 = progress;  
  90.                     handler.sendMessage(msg);  
  91.                     try {  
  92.                         Thread.sleep(200);  
  93.                     } catch (InterruptedException e) {  
  94.                         e.printStackTrace();  
  95.                     }  
  96.                 }  
  97.             };  
  98.         }.start();  
  99.     }  
  100. }  

咱們發現,它和MainActivity實現方式很類似,恩,他們都是經過和服務綁定後獲取到的Binder對象來跟服務通訊的,都是主動和服務打招呼來獲取信息和控制服務的。

這兩個Activity和一個Service彷佛像是複雜的男女關係,兩個男人同時喜歡一個女人,都經過本身的手段試圖從那個女人獲取愛情,兩個男人都很主動,那個女人顯得很被動。

以上就是今天的所有內容,也許朋友們會有疑問,能不能讓Service主動告知Activity當前的進度信息呢?答案是能夠。下一次,我就會和你們分享一下,如何變Service爲主動方,讓一個女人腳踏兩隻船的方式。

相關文章
相關標籤/搜索