詳解Android中IntentService的使用方法_Android

來源:互聯網
上載者:User

為什麼我們需要IntentService ?

Android中的IntentService是繼承自Service類的,在我們討論IntentService之前,我們先想一下Service的特點: Service的回調方法(onCreate、onStartCommand、onBind、onDestroy)都是運行在主線程中的。當我們通過startService啟動Service之後,我們就需要在Service的onStartCommand方法中寫程式碼完成工作,但是onStartCommand是運行在主線程中的,如果我們需要在此處完成一些網路請求或IO等耗時操作,這樣就會阻塞主線程UI無響應,從而出現ANR現象。為瞭解決這種問題,最好的辦法就是在onStartCommand中建立一個新的線程,並把耗時代碼放到這個新線程中執行。可以參考之前的文章《Android通過startService實現檔案批量下載》,這篇文章在onStartCommand中開啟了新的線程作為背景工作執行緒去執行網路請求,所以這樣不會阻塞主線程。由此看來,建立一個帶有背景工作執行緒的Service是一種很常見的需求(因為背景工作執行緒不會阻塞主線程),所以Android為了簡化開發帶有背景工作執行緒的Service,Android額外開發了一個類——–IntentService。

IntentService的特點

IntentService具有以下特點:

  • 1. IntentService內建一個背景工作執行緒,當我們的Service需要做一些可能會阻塞主線程的工作的時候可以考慮使用IntentService。
  • 2. 我們需要將要做的實際工作放入到IntentService的onHandleIntent回到方法中,當我們通過startService(intent)啟動了IntentService之後,最終Android Framework會回調其onHandleIntent方法,並將intent傳入該方法,這樣我們就可以根據intent去做實際工作,並且onHandleIntent運行在IntentService所持有的背景工作執行緒中,而非主線程。
  • 3. 當我們通過startService多次啟動了IntentService,這會產生多個job,由於IntentService只持有一個背景工作執行緒,所以每次onHandleIntent只能處理一個job。面多多個job,IntentService會如何處理?處理方式是one-by-one,也就是一個一個按照先後順序處理,先將intent1傳入onHandleIntent,讓其完成job1,然後將intent2傳入onHandleIntent,讓其完成job2…這樣直至所有job完成,所以我們IntentService不能並行的執行多個job,只能一個一個的按照先後順序完成,當所有job完成的時候IntentService就銷毀了,會執行onDestroy回調方法。

如何使用IntentService ?

《Android通過startService實現檔案批量下載》一文中,我們示範了如何通過Service批量下載文章,現在在本文中我們還是要示範如何批量下載文章,只不過是用IntentService完成這項工作。

系統介面如下:

介面很簡單,就一個按鈕“批量下載文章”,通過該Activity上的按鈕啟動DownloadService。

代碼如下:

package com.ispring.startservicedemo;import android.app.IntentService;import android.content.Intent;import android.util.Log;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;public class DownloadIntentService extends IntentService { public DownloadIntentService(){  super("Download");  Log.i("DemoLog", "DownloadIntentService建構函式, Thread: " + Thread.currentThread().getName()); } @Override public void onCreate() {  super.onCreate();  Log.i("DemoLog", "DownloadIntentService -> onCreate, Thread: " + Thread.currentThread().getName()); } @Override public int onStartCommand(Intent intent, int flags, int startId) {  Log.i("DemoLog", "DownloadIntentService -> onStartCommand, Thread: " + Thread.currentThread().getName() + " , startId: " + startId);  return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(Intent intent) {  HttpURLConnection conn = null;  InputStream is = null;  String blogUrl = intent.getStringExtra("url");  String blogName = intent.getStringExtra("name");  try{   //下載指定的檔案   URL url = new URL(blogUrl);   conn = (HttpURLConnection)url.openConnection();   if(conn != null){    //我們在此處得到所下載文章的輸入資料流,可以將其以檔案的形式寫入到儲存卡上面或    //將其讀取出文本顯示在App中    is = conn.getInputStream();   }  }catch (MalformedURLException e){   e.printStackTrace();  }catch (IOException e){   e.printStackTrace();  }finally {   if(conn != null){    conn.disconnect();   }  }  Log.i("DemoLog", "DownloadIntentService -> onHandleIntent, Thread: " + Thread.currentThread().getName() + ", 《" + blogName + "》下載完成"); } @Override public void onDestroy() {  super.onDestroy();  Log.i("DemoLog", "DownloadIntentService -> onDestroy, Thread: " + Thread.currentThread().getName()); }}

DownloadActivity的代碼如下:

package com.ispring.startservicedemo;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;public class DownloadActivity extends Activity implements Button.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_download); } @Override public void onClick(View v) {  List<String> list = new ArrayList<>();  list.add("Android通過startService播放背景音樂;http://www.jb51.net/article/76479.htm");  Iterator iterator = list.iterator();  while (iterator.hasNext()){   String str = (String)iterator.next();   String[] splits = str.split(";");   String name = splits[0];   String url = splits[1];   Intent intent = new Intent(this, DownloadIntentService.class);   intent.putExtra("name", name);   intent.putExtra("url", url);   //啟動IntentService   startService(intent);  } }}

當我們單擊了按鈕“批量下載文章”時,我們會多次調用Activity的startService方法,其中我們在其參數intent中儲存了文章名name以及文章的地址url,由於我們多次調用了startService方法,所以會批量下載文章。

點擊按鈕後,控制台運行結果如下所示:

通過以上的輸出結果我們可以發現,DownloadIntentService的onCreate、onStartCommand、onDestroy回調方法都是運行在主線程中的,而onHandleIntent是運行在背景工作執行緒IntentService[Download]中的,這驗證了我們上面所說的IntentService的第一個和第二個特點。

通過上面的輸出結果我們還會發現,在我們連續調用了五次startService(intent)之後,onStartCommand依次被調用了五次,然後依次執行了onHandleIntent五次,這樣就依次完成了job,當最後一個job完成,也就是在最後一次onHandleIntent調用完成之後,整個IntentService的工作都完成,執行onDestroy回調方法,IntentService銷毀。

IntentService工作原理及源碼解析

在上面我們已經介紹了IntentService的特點以及如何使用,那麼你可能會疑問Android是如何將調度這些intent將其傳入onHandleIntent完成工作的,其實IntentService的工作原理很簡單,將intent轉換為Message並放到訊息佇列中,然後讓Handler依次從中取出Message對其進行處理。

IntentService的源碼如下:

package android.app;import android.content.Intent;import android.os.Handler;import android.os.HandlerThread;import android.os.IBinder;import android.os.Looper;import android.os.Message;public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private final class ServiceHandler extends Handler {  public ServiceHandler(Looper looper) {   super(looper);  }  @Override  public void handleMessage(Message msg) {   //在背景工作執行緒中調用onHandleIntent,確保onHandleIntent不會阻塞主線程   onHandleIntent((Intent)msg.obj);   //在執行完了onHandleIntent之後,我們需要調用stopSelf(startId)聲明某個job完成了   //當所有job完成的時候,Android就會回調onDestroy方法,銷毀IntentService   stopSelf(msg.arg1);  } } public IntentService(String name) {  //此處的name將用作線程名稱  super();  mName = name; } public void setIntentRedelivery(boolean enabled) {  mRedelivery = enabled; } @Override public void onCreate() {  super.onCreate();  //建立HandlerThread,利用mName作為線程名稱,HandlerThread是IntentService的背景工作執行緒  HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");  thread.start();  mServiceLooper = thread.getLooper();  //將建立的HandlerThread所綁定的looper對象傳遞給ServiceHandler,  //這樣我們建立的Handler就和HandlerThread通過訊息佇列綁定在了一起  mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public void onStart(Intent intent, int startId) {  //在此方法中建立Message對象,並將intent作為Message的obj參數,  //這樣Message與Intent就關聯起來了  Message msg = mServiceHandler.obtainMessage();  msg.arg1 = startId;  msg.obj = intent;  //將關聯了Intent資訊的Message發送給Handler  mServiceHandler.sendMessage(msg); } @Override public int onStartCommand(Intent intent, int flags, int startId) {  //IntentService重寫了onStartCommand回調方法:在內部調用onStart回調方法  //所以我們在繼承IntentService時,不應該再覆寫該方法,即便覆蓋該方法,我們也應該調用super.onStartCommand()  onStart(intent, startId);  return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onDestroy() {  //在onDestroy方法中調用了Handler的quit方法,該方法會終止訊息迴圈  mServiceLooper.quit(); } @Override public IBinder onBind(Intent intent) {  return null; } protected abstract void onHandleIntent(Intent intent);}

我對上面的代碼已經加了很多注釋,相信大家直接看代碼就能理解IntentService是如何運作的了。

IntentService繼承自Service類,並且IntentService重寫了onCreate、onStartCommand、onStart、onDestroy回調方法,並且IntentService還添加了一個onHandleIntent回調方法。下面我們依次解釋這幾個方法在IntentService的作用。

onCreate: 在onCreate回調方法中,利用mName作為線程名稱,建立HandlerThread,HandlerThread是IntentService的背景工作執行緒。HandlerThread在執行了start方法之後,其本身就關聯了訊息佇列和Looper,並且訊息佇列開始迴圈起來。

onStartCommand: IntentService重寫了onStartCommand回調方法:在內部調用onStart回調方法。

onStart: 在onStart方法中建立Message對象,並將intent作為Message的obj參數,這樣Message與Intent就關聯起來了,然後通過Handler的sendMessage方法將關聯了Intent資訊的Message發送給Handler。

onHandleIntent: 當在onStart方法中,通過sendMessage方法將Message放入到Handler所關聯的訊息佇列中後,Handler所關聯的Looper對象會從訊息佇列中取出一個Message,然後將其傳入Handler的handleMessage方法中,在handleMessage方法中首先通過Message的obj擷取到了原始的Intent對象,然後將其作為參數傳給了onHandleIntent方法讓其執行。handleMessage方法是運行在HandlerThread的,所以onHandleIntent也是運行在背景工作執行緒中的。在執行完了onHandleIntent之後,我們需要調用stopSelf(startId)聲明某個job完成了。當所有job完成的時候,Android就會回調onDestroy方法,銷毀IntentService。

onDestroy: 當所有job完成的時候,Service會銷毀並執行其onDestroy回調方法。在該方法中,調用了Handler的quit方法,該方法會終止訊息迴圈。

總結

IntentService可以在背景工作執行緒中完成工作而不阻塞主線程,但是IntentService不能平行處理多個job,只能依次處理,一個接一個,當所有的job完成後,會自動執行onDestroy方法而無需我們自己調用stopSelf()或stopSelf(startId)方法。IntentService並不神秘,只是Android對一種常見開發方式的封裝,便於開發人員減少開發工作量。 IntentService是個助手類,如果Android沒有提供該類也沒什麼,我們自己也可以寫一個類似的。IntentService之餘Service,類似於HandlerThread之於Handler。

希望本文對大家理解IntentService有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.