1. 程式人生 > >IntentService使用以及源碼分析

IntentService使用以及源碼分析

text volatil 簡單 平時 start 我們 roi lis 線程

一 概述

我們知道,在Android開發中,遇到耗時的任務操作時,都是放到子線程去做,或者放到Service中去做,在Service中開一個子線程來執行耗時操作。
那麽,在Service裏面我們需要自己管理Service的生命周期,何時開啟何時關閉,還是很麻煩的,還好Android給我們提供了一個這樣的類,叫做IntentService

那麽IntentService是做什麽用的呢?
IntentService: 是繼承於Service的一個類,用來處理異步請求。可以直接通過startService(Intent intent)來提交請求,Service的創建,關閉,開子線程等工作,IntentService內部都幫我們封裝好了,我們只需要發請求處理請求就行了。
如此簡單

我們先來看一下IntentService的用法

二 IntentService用法

假如我們現在有一個下載文件的需求,我們需要開啟一個Service並在裏面開啟一個子線程中去下載文件。

1 先繼承IntentService實現一個類,我們叫做 DownloadService如下

public class DownloadService extends IntentService {

     //重寫無參的構造方法
    public DownloadService(){
        super("DownloadService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String url = intent.getStringExtra("url");
        downloadFile(url);
    }

    //下載文件
    private void downloadFile(String url){
        try {
            Log.e("DownloadService","當前線程名:" + Thread.currentThread().getName());
            Log.e("DownloadService","文件開始下載... url="+url);
      
            //模擬下載文件操作
            Thread.sleep(3000);

            Log.e("DownloadService","文件下載完成...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

繼承IntentService類,實現onHandleIntent(Intent intent)方法
註意,不要忘了DownloadService需要在清單文件中註冊

2 我們在界面上的按鈕的點擊事件中,來下載一個文件,如下

  findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("DownloadService","當前線程名:"+Thread.currentThread().getName());
            
                //像平時啟動service方式一樣,沒有任何區別
                Intent intent = new Intent(MainActivity.this,DownloadService.class);
                intent.putExtra("url","http://xx/test.apk");
                startService(intent);
            }
        });

點擊按鈕,輸出
E/DownloadService: 當前線程名:main
E/DownloadService: 當前線程名:IntentService[DownloadService]
E/DownloadService: 文件開始下載... url=http://xx/test.apk
E/DownloadService: 文件下載完成...

通過輸出日誌,可以看到,開啟任務是在主線程中,而執行下載文件的耗時任務,是在子線程中,使用的時候,只需要startService(Intent intent),並通過intent把所需要的數據帶過去就行了。是不是很簡單。

下面來分析IntentService是如何做到的?

## 三 IntentService源碼分析

1 看下IntentService的類定義
```
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((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}


......

}
```
可以看到,IntentService有幾個需要註意的地

  1. IntentService是一個Service也是一個抽象類,子類只需要實現void onHandleIntent(@Nullable Intent intent)並在裏面添加自己的業務即可

  2. IntentService類中有一個 Looper mServiceLooper以及一個ServiceHandler mServiceHandler,

  3. ServiceHandler繼承Handler,並在handleMessage()中調用了外部類的onHandleIntent方法

  4. ServiceHandlerhandleMessage()方法中,執行完onHandleIntent((Intent)msg.obj),調用了stopSelf(msg.arg1)來關閉這個Service

所以IntentService不用去管怎麽創建的,怎麽關閉的,怎麽開啟線程的,我們只需要繼承IntentService,實現onHandleIntent()方法並在裏面執行我們的邏輯就行了,執行完任務之後,自動會把Service關閉。這一切都是IntentService幫我們封裝好了的。

2 startService之後,會走Service的生命周期方法,onCreate()源碼如下

   @Override
    public void onCreate() {
        super.onCreate();
        
        //還記得上一篇講的HandlerThread源碼分析嗎
        //創建一個HandlerThread對象,並調用start()方法
        //使之成為一個looper線程
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
            
        //拿到上面創建的looper線程對應的looper
        //並傳給ServiceHandler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

很明顯,在onCreate中創建了一個線程而且是一個looper線程,又創建了一個Handler對象,並把這個線程的looper傳給了這個Handler,那麽這個Handler發送的任務消息都會由這個ServiceHandler處理了

再看看 onStart()方法和onStartCommand()方法,如下

onStart()方法

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

onStartCommand()方法

  @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

startService方法啟動的時候,第一次會調用onCreate() -> onStart() -> onStartCommand()方法
而且之後再次調用startService方法啟動的時候,不會再調用onCreate()方法了,而是會調用onStandCommand()方法

而在IntentService中,onStartCommand()方法中又會調用onStart()方法,所以,我們只需要分析onStart()方法即可

onStart()方法源碼

   @Override
    public void onStart(@Nullable Intent intent, int startId){
        //使用mServiceHandler獲取一個消息
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        
        //發送這個消息
        mServiceHandler.sendMessage(msg);
    }

通過上面可知,我們調用startService方法啟動service的時候,會調用onStart()方法,在這個方法裏面,把intent和startId賦值給了 msg ,並發送消息。這時候會調用Handler的handleMessag()方法,ServiceHandler的源碼如下:

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((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
 
    ......
}

可以看到ServiceHandler是IntentService的內部類,而且在handleMessage中會調用IntentService的 onHandleIntent()方法,並把 intent 參數也傳過去。

我們來看下 onHandleIntent()方法
protected abstract void onHandleIntent(@Nullable Intent intent);
是一個抽象方法,這時候就需要子類去實現這個方法並在裏面做耗時的操作即可。

經過上面的分析可知,IntentService使用也非常方便,原理就是利用HandlerThread開啟了一個looper線程,並在onStart中把intent通過msg發送出去,並在handleMessage中又調用了onHandleIntent方法,子類實現即可

IntentService使用以及源碼分析