Android 源碼系列之<七>從源碼的角度深入理解IntentService及HandlerThread

來源:互聯網
上載者:User

標籤:

        轉載請註明出處:http://blog.csdn.net/llew2011/article/details/51373243

        提起Service大家都很熟悉,它乃Android四(si)大(da)組(jing)件(gang)之一。但是說起IntentService有童靴或許有點陌生,看名字感覺和Service有關連。不錯,不僅有關聯而且關係還不一般,IntentService是Service的子類,所以它也是正宗的Service,由於IntentService藉助了HandlerThread,我們今天就從源碼的角度巴拉一下IntentService及HandlerThread,看看它們是何方神聖,如果你對它們非常熟悉,請跳過本文(*^__^*) ……

        開始巴拉IntentService源碼之前我們先看看它的基本用法,既然IntentService是正宗的Service,那它的用法就和Service一樣。IntentService也是一個抽象類別,需要實現其抽象方法onHandleIntent()。我們先定義BackgroundService,使之繼承IntentService並實現其抽象方法onHandleIntent(),然後重寫IntentService的生命週期方法並列印日誌,代碼如下:

public class BackgroundService extends IntentService {private static final String TAG = BackgroundService.class.getSimpleName();public BackgroundService() {super("TT");Log.e(TAG, "BackgroundService()    " + Thread.currentThread());}@Overridepublic void onCreate() {Log.e(TAG, "onCreate()             " + Thread.currentThread());super.onCreate();}@Overridepublic void onStart(Intent intent, int startId) {Log.e(TAG, "onStart()              " + Thread.currentThread());super.onStart(intent, startId);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.e(TAG, "onStartCommand()       " + Thread.currentThread());return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {Log.e(TAG, "onDestroy()            " + Thread.currentThread());super.onDestroy();}@Overridepublic IBinder onBind(Intent intent) {Log.e(TAG, "onBind()               " + Thread.currentThread());return super.onBind(intent);}@Overrideprotected void onHandleIntent(Intent intent) {Log.e(TAG, "onHandleIntent()       " + Thread.currentThread());}}
        我們在重寫的部分方法中添加了日誌,主要列印當前方法名和方法執行時所在的線程名稱。然後在設定檔manifest.xml中配置BackgroundService,代碼如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.llew.wb.source.intentservice"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk android:minSdkVersion="8" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name="com.llew.wb.source.MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <service android:name="com.llew.wb.source.BackgroundService" >        </service>    </application></manifest>
        最後在MainActivity的布局檔案activity_layout.xml中添加一個button按鈕,該按鈕用來啟動BackgroundService。布局檔案代碼如下:
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <Button        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:onClick="startService"        android:text="測試IntentService" /></FrameLayout>

        定義完布局檔案後,在MainActivity中添加startIntentService()方法,代碼如下:

public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void startService(View view) {Intent action = new Intent(this, BackgroundService.class);startService(action);}}
        OK,一切就緒,我們運行一下看看列印結果。運行程式,輸出結果如所示:

        根據列印結果可以確定IntentService的生命週期函數執行順序是onCreate()→onStartCommon()→onStart()→onHandleIntent()→onDestroy()。再觀察列印的線程資訊發現只有onHandleIntent()的線程資訊和其他函數的線程資訊是不同的,也就是說onHandleIntent()所在的線程和其他函數所在的不是同一個線程,我們知道其他函數都是在主線程中執行的。所以onHandleIntent()的執行是在子線程中進行的。還有一點onDestroy()方法是在onHandleIntent()方法執行結束後才執行,因為我們並沒有主動的調用停止Service的相關方法,所以可以猜測使IntentService停止的操作一定是在和onHandleIntent()方法所在的線程中操作的。

        基於猜測,我們繼續做實驗,既然onHandleIntent()方法是在子線程中執行的,那我們就可以利用線程休眠來類比後台比較耗時的操作,修改onHandleIntent()方法,代碼如下:

@Overrideprotected void onHandleIntent(Intent intent) {Log.e(TAG, "onHandleIntent()       " + Thread.currentThread());try {Thread.sleep(3000);Log.e(TAG, "sleep finish           " + Thread.currentThread());} catch (Exception e) {e.printStackTrace();}}
        在onHandleIntent()方法中讓其所在的線程休眠了3秒鐘,然後運行程式,輸出結果如下:

        根據輸出結果看到,在onHandleIntent()方法中先是列印了第一句log,等待3秒鐘後又把第二句log內容列印出來了,log列印完之後是執行Service的onDestroy()方法。我們繼續做實現,剛剛只是在onHandleIntent()的方法中類比做了一個耗時任務,現在我們啟動多個IntentService,每一次啟動時都傳遞進來一個參數來表示每一個任務,繼續修改startService()方法,代碼如下:

public void startService(View view) {Intent action1 = new Intent(this, BackgroundService.class);action1.putExtra("params", "task 1");startService(action1);Intent action2 = new Intent(this, BackgroundService.class);action2.putExtra("params", "task 2");startService(action2);Intent action3 = new Intent(this, BackgroundService.class);action3.putExtra("params", "task 3");startService(action3);}
        我們在startService()方法中啟動了3次BackgroundService,並在啟動時傳遞了參數。然後修改onHandleIntent()方法,代碼如下:
@Overrideprotected void onHandleIntent(Intent intent) {String params = intent.getStringExtra("params");Log.e(TAG, params + " in onHandleIntent()   " + Thread.currentThread());try {Thread.sleep(3000);Log.e(TAG, params + " is finished           " + Thread.currentThread());} catch (Exception e) {e.printStackTrace();}}
        運行程式,日誌列印結果如所示:

        觀察輸出結果發現onHandleIntent()的執行是有序的,當所有的類比耗時任務都結束後該Service才銷毀。也就是說我們可以方便的使用IntentService來執行一些有序的並且非常耗時的非同步作業,當所有的任務都執行完畢後該IntentService會主動銷毀自己,我們無需關心IntentService的銷毀。

        好了,現在我們清楚了IntentService的執行流程,那接下來我們就從源碼的角度來巴拉一下IntentService,看看其內部流程,首先看一下官網對其的說明:

        IntentService是一個繼承Service的用來處理非同步請求的類,用戶端通過調用startService(Intent)發送請求,Service服務就會在必要的時候啟動然後在背景工作執行緒中依次處理每一個Intent,當背景工作執行緒執行完畢後Service服務就關閉自己。

        這個"工作隊列處理器"是將任務從一個應用的主線程中做分離的最常用的模式,IntentService類就是該模式的經典代表。為了使用IntentService需要先繼承IntentService然後實現其抽象方法onHandleIntent(),它在背景工作執行緒中接收發送來的所有Intent並在適當的時候結束自己。

        所有的請求都會在一個單一的背景工作執行緒中被接收,背景工作執行緒可以隨意耗時而不會阻塞主線程,但是在同一時刻只能處理一個請求。

        知曉了IntentService的說明後我們繼續往下看代碼,首先看一下IntentService的定義的成員變數有哪些,代碼如下:

// 提供訊息佇列和private volatile Looper mServiceLooper;// 處理訊息private volatile ServiceHandler mServiceHandler;// 表示背景工作執行緒的名字private String mName;// 是否重新發送Intentprivate boolean mRedelivery;private final class ServiceHandler extends Handler {    public ServiceHandler(Looper looper) {        super(looper);    }    @Override    public void handleMessage(Message msg) {        onHandleIntent((Intent)msg.obj);        stopSelf(msg.arg1);    }}
        IntentService中僅僅定義了4個成員變數,其中Looper類型的mServiceLooper童靴們應該很熟悉了(不熟悉也無妨我會在後續文章中從源碼的角度出發講解Android訊息機制之Handler, Looper, Message和MessageQueue等),還定義了ServiceHandler類型的mServiceHandler,ServiceHandler繼承Handler,並在handleMessage()方法中調用了抽象方法onHandleIntent()方法和結束Service的stopSelf()方法。

        瞭解完IntentService的成員變數後我們緊接著看一下構造方法,源碼如下:

/** * Creates an IntentService.  Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */public IntentService(String name) {    super();    mName = name;}
        IntentService的構造方法要求必須傳遞進來一個String類型的值(該值表示的是背景工作執行緒的名稱),把name賦值給了其成員變數mName。看完了構造方法後接著看IntentService的onCreate()方法,源碼如下:
@Overridepublic void onCreate() {    // TODO: It would be nice to have an option to hold a partial wakelock    // during processing, and to have a static startService(Context, Intent)    // method that would launch the service & hand off a wakelock.    super.onCreate();    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");    thread.start();    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);}
        在onCreate()方法中首先調用了父類的onCreate()方法,接著建立了一個HandlerThread的執行個體thread,看到這裡或許有的童靴會有疑問了,HandlerThread又是何方神聖了?不必擔心,我們進入HandlerThread的源碼看看它到底是何方神聖,其源碼如下:
/** * Handy class for starting a new thread that has a looper. The looper can then be  * used to create handler classes. Note that start() must still be called. */public class HandlerThread extends Thread {    int mPriority;    int mTid = -1;    Looper mLooper;    public HandlerThread(String name) {        super(name);        mPriority = Process.THREAD_PRIORITY_DEFAULT;    }        /**     * Constructs a HandlerThread.     * @param name     * @param priority The priority to run the thread at. The value supplied must be from      * {@link android.os.Process} and not from java.lang.Thread.     */    public HandlerThread(String name, int priority) {        super(name);        mPriority = priority;    }        /**     * Call back method that can be explicitly over ridden if needed to execute some     * setup before Looper loops.     */    protected void onLooperPrepared() {    }    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }        /**     * This method returns the Looper associated with this thread. If this thread not been started     * or for any reason is isAlive() returns false, this method will return null. If this thread      * has been started, this method will block until the looper has been initialized.       * @return The looper.     */    public Looper getLooper() {        if (!isAlive()) {            return null;        }                // If the thread has been started, wait until the looper has been created.        synchronized (this) {            while (isAlive() && mLooper == null) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mLooper;    }        /**     * Ask the currently running looper to quit.  If the thread has not     * been started or has finished (that is if {@link #getLooper} returns     * null), then false is returned.  Otherwise the looper is asked to     * quit and true is returned.     */    public boolean quit() {        Looper looper = getLooper();        if (looper != null) {            looper.quit();            return true;        }        return false;    }        /**     * Returns the identifier of this thread. See Process.myTid().     */    public int getThreadId() {        return mTid;    }}
        哦,看完HandlerThread的源碼我們就可以放心了,原來HandlerThread繼承自Thread,那也就是說HandlerThread也是一個正宗的線程類。HandlerThread類有三個成員變數,mPriority表示當前線程的優先順序(預設值為0),mTid表示線程的標識符,mLooper的作用為當前線程添加訊息佇列並迴圈讀取訊息。

        HandlerThread的成員變數瞭解之後,看一下其重寫的run()方法,在run()方法中先調用Process.myTid()給mTid賦值,接著調用Looper.prepare()方法為當前線程建立一個訊息佇列,建立完訊息佇列後為成員變數mLooper賦值並喚醒可能處於等待狀態的鎖機制,緊接著又設定了當前線程的優先順序,最後進入Looper.loop()的方法中。

        總的來看HandlerThread核心就是對外提供一個帶有Looper功能的線程,當我們建立完HandlerThread執行個體之後要立即調用其start()方法,如果不調用start()方法,當我們需要使用HandlerThread中的Looper時該線程就會處於掛起狀態,因為在調用HandlerThread執行個體對象的getLooper()方法時,如果當前線程是isAlive()並且mLooper為null,那麼該線程就將一直wait()下去,所以在建立完HandlerThread後要立即調用其start()方法。

        看完HandlerThread源碼後我們接著看IntentService的onCreate()方法,在該方法中執行個體化了一個HandlerThread類型的thread,緊接著調用該thread的start()方法,然後再調用thread的getLooper()方法為mServiceLooper賦值,最後利用mServiceLooper完成mServiceHandler的初始化工作。

       看完IntentService的onCreate()方法,接著看onStartCommand()方法,源碼如下:

/** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    onStart(intent, startId);    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}
        onStartCommand()函數中調用了onStart()函數,然後通過對mRedelivery的判斷決定返回START_REDELIVER_INTENT或START_NOT_STICKY,需要注意在onStartCommand()函數中只能返回以下三種類型中的一種:

  • START_NOT_STICKY
            如果該服務在onStartCommand()方法返回後被系統殺死,那麼知道接收到新的Intent對象,該服務才會重新建立。這是安全的選項,用來避免在你不需要的時候運行裡的服務。
  • START_STICKY
            如果該服務在onStartCommand()方法返回後被系統殺死,那麼系統就會重新建立這個服務並且嘗試調用onStartCommand()方法,但是系統不會重新傳遞最後的Intent對象,系統會用一個null的Intent對象來調用onStartCommand()方法。在這個情況下除非有一些被發送的Intent對象在等待啟動服務。這適用於不執行命令的媒體播放器(或類似的服務),它只是無限期的運行著並等待工作的到來。
  • START_REDELIVER_INTENT
            如果該服務在onStartCommand()方法返回後被系統殺死,那麼系統會重新建立這個服務,並且用發送給這個服務的最後一個Intent對象來調用onStartCommand()方法。任意等待的Intent對象會依次被發送,這適合於那些應該立即恢複正在執行工作的服務,例如下載檔案。

        然後我們看一下onStart()方法,源碼如下:

@Overridepublic void onStart(Intent intent, int startId) {    Message msg = mServiceHandler.obtainMessage();    msg.arg1 = startId;    msg.obj = intent;    mServiceHandler.sendMessage(msg);}
        在onStart()方法中利用mServiceHandler發送訊息並把傳遞進來的Intent等參數也一同打包發送。該訊息最後在文章開頭看到的在handleMessage()方法中被處理:
private final class ServiceHandler extends Handler {    public ServiceHandler(Looper looper) {        super(looper);    }    @Override    public void handleMessage(Message msg) {        onHandleIntent((Intent)msg.obj);        stopSelf(msg.arg1);    }}
        handleMessage()方法中調用了onHandleIntent()方法,等其執行完畢後調用stopSelf()方法關閉服務。

        總的來說IntentService適合在後台執行比較耗時的,有序的非同步作業並且無需我們關心何時結束該服務。HandlerThread不僅是標準的Thread而且對外提供了Looper功能,需要注意的是當建立了HandlerThread後需要立即執行其start()方法,否則該線程可能一直處於掛起狀態。

        好了,到這裡有關IntentService和HandlerThread的講解就告一段落,感謝觀看。




Android 源碼系列之<七>從源碼的角度深入理解IntentService及HandlerThread

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.