Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 源碼系列之(七)從源碼的角度深入理解IntentService及HandlerThread

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

編輯:關於Android編程

提起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());
	}

	@Override
	public void onCreate() {
		Log.e(TAG, "onCreate()             " + Thread.currentThread());
		super.onCreate();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		Log.e(TAG, "onStart()              " + Thread.currentThread());
		super.onStart(intent, startId);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.e(TAG, "onStartCommand()       " + Thread.currentThread());
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		Log.e(TAG, "onDestroy()            " + Thread.currentThread());
		super.onDestroy();
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.e(TAG, "onBind()               " + Thread.currentThread());
		return super.onBind(intent);
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		Log.e(TAG, "onHandleIntent()       " + Thread.currentThread());
	}
}
我們在重寫的部分方法中添加了日志,主要打印當前方法名和方法執行時所在的線程名稱。然後在配置文件manifest.xml中配置BackgroundService,代碼如下:


    

    
        
            
                

                
            
        

        
        
    

最後在MainActivity的布局文件activity_layout.xml中添加一個button按鈕,該按鈕用來啟動BackgroundService。布局文件代碼如下:

<framelayout android:layout_height="match_parent" android:layout_width="match_parent" xmlns:android="http://schemas.android.com/apk/res/android"></framelayout>

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

public class MainActivity extends Activity {

	@Override
	protected 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()方法,代碼如下:

@Override
protected 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()方法,代碼如下:
@Override
protected 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;
// 是否重新發送Intent
private 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()方法,源碼如下:
@Override
public 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
 */
@Override
public 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()方法,源碼如下:

@Override
public 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的講解就告一段落,感謝觀看。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved