Android IntentService使用全面介紹及原始碼解析

快樂的kuai小馬發表於2016-07-22

一 IntentService介紹

IntentService定義的三個基本點:是什麼?怎麼用?如何work?

官方解釋如下:

//IntentService定義的三個基本點:是什麼?怎麼用?如何work?*/

1、IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand.

2、Clients send requests through startService(Intent) calls;

3、the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

//解釋了IntentService的好處,以及How to use IntentService*/

This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

All requests are handled on a single worker thread — they may take as long as necessary (and will not block the application’s main loop), but only one request will be processed at a time.

總結IntentService的特點如下:

1、IntentService是Service類的子類,用來處理非同步請求。

2、客戶端可以通過startService(Intent)方法傳遞請求給IntentService

3、IntentService在onCreate()函式中通過HandlerThread單獨開啟一個執行緒來處理所有Intent請求物件(通過startService的方式傳送過來的)所對應的任務,這樣以免事務處理阻塞主執行緒。

4、執行完所一個Intent請求物件所對應的工作之後,如果沒有新的Intent請求達到,則自動停止Service;否則執行下一個Intent請求所對應的任務。

5、IntentService在處理事務時,還是採用的Handler方式,建立一個名叫ServiceHandler的內部Handler,並把它直接繫結到HandlerThread所對應的子執行緒。 ServiceHandler把處理一個intent所對應的事務都封裝到叫做onHandleIntent的虛擬函式;因此我們直接實現虛擬函式onHandleIntent,再在裡面根據Intent的不同進行不同的事務處理就可以了。

IntentService最重要的一個方法onHandleIntent:

This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf().

1、該函式用於針對Intent的不同進行不同的事務處理.執行完所一個Intent請求物件所對應的工作之後,如果沒有新的Intent請求達到,
則自動停止Service;否則ServiceHandler會取得下一個Intent請求傳人該函式來處理其所對應的任務。

2、所有請求都在一個單執行緒中,不會阻塞應用程式的主執行緒(UI Thread),但是同一時間只處理一個請求。

IntentService的特點、優點、缺點?

IntentService的特點:

  1. IntentService是藉助於訊息佇列實現的,所以任務的執行順序就是一個queue的形式;
  2. 由於是單執行緒(一個工作執行緒),所以所有的任務需要排隊執行;/
  3. 避免了我們再去建立執行緒和管理service的結束工作;

基於以上,IntentService與Service比較的好處有:

  • 第一,使用方便,程式碼簡潔,不再需要我們自己像Service裡面還要去手動建立執行緒;
  • 第二,當操作完成時,我們不用手動停止Service。

當然,IntentService的缺點也是顯而易見:

由於是單個的worker thread,所以任務需要排隊,不適合大多數的多工情況;

二 DEMO實踐

DEMO場景描述:

主要由MainActivity和MyIntentService構成,在MainActivity中啟動服務,並傳遞兩個引數a和b,在MyIntentService中獲取引數,求和,並通過傳送廣播的形式向UI返回結果,然後MainActivity在接收到廣播之後更新UI。

MainActivity.java主要做了三件事:

  1. 註冊/登出廣播接收器;
  2. 建立UI和更新UI;
  3. 傳遞引數,啟動MyIntentService;

程式碼如下:

public class MainActivity extends AppCompatActivity {

    private LinearLayout ll_container;
    private BroadcastReceiver forSumReceiver=new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i("TAG","onReceive ()");
            if(intent.getAction()==Constans.ACTION_RESULT){
                int a=intent.getIntExtra(Constans.A,0);
                int result=intent.getIntExtra(Constans.RESULT,0);
                Log.i("TAG","onReceive --result:"+result);
                handleResult(a,result);
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ll_container= (LinearLayout)findViewById(R.id.ll_container);
        Log.i("TEST","MainActivity:"+android.os.Process.myTid());
        registerBroadcast();
    }

    private void handleResult(int a,int result){
        TextView textView=(TextView)ll_container.findViewWithTag(a);
        String old=textView.getText().toString();
        String newText=old.replaceAll("  正在計算中...",String.valueOf(result)+"  計算Success");
        textView.setText(newText);
    }

    private void registerBroadcast(){
        IntentFilter intentFilter=new IntentFilter();
        intentFilter.addAction(Constans.ACTION_RESULT);
        registerReceiver(forSumReceiver,intentFilter);
    }

    private int a=1;
    public void addTask(View view){
        int b=new Random().nextInt(101)+1;
        MyIntentService.startMyIntentService(this,a,b);
        TextView textView=new TextView(this);
        textView.setText(a+"+"+b+"= "+ "  正在計算中...");
        textView.setTag(a);
        ll_container.addView(textView);
        a++;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(forSumReceiver);
    }
}

MyIntentService用於接收引數,並返回計算的結果:

public class MyIntentService extends IntentService {

    public MyIntentService() {
        //必須實現父類的構造方法
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        Log.i("TEST","onCreate()");
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.i("TEST","onStart()");
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("TEST","onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i("TEST", "onBind()");
        return super.onBind(intent);
    }

    @Override
    public void onDestroy() {
        Log.i("TEST","onDestroy()");
        super.onDestroy();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i("TEST","onHandleIntent():"+android.os.Process.myTid());
        if (intent!=null){
            String action=intent.getAction();
            if(Constans.ACTION_FOR_SUM.equals(action)){
                int a=intent.getIntExtra(Constans.A,0);
                int b=intent.getIntExtra(Constans.B,0);
                int result=a+b;
                Log.i("TEST","result: "+result);
                handleResult(a,result);
            }
        }
    }

    private void handleResult(int a,int result){
        try{
            //模擬計算耗時
            Thread.sleep(3000);
            Intent intent=new Intent(Constans.ACTION_RESULT);
            intent.putExtra(Constans.RESULT,result);
            intent.putExtra(Constans.A,a);
            sendBroadcast(intent);
        }catch (InterruptedException e){
            e.printStackTrace();;
        }
    }

    public static void startMyIntentService(Context context,int a,int b){
        Intent intent=new Intent(context,MyIntentService.class);
        intent.setAction(Constans.ACTION_FOR_SUM);
        intent.putExtra(Constans.A,a);
        intent.putExtra(Constans.B,b);
        context.startService(intent);
    }

}

IntentService生命週期方法執行順序如下:

07-08 10:18:51.579 com.troy.intentservicedemo I/TEST: MainActivity:30060 
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onCreate()
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onStartCommand()
07-08 10:19:26.009 com.troy.intentservicedemo I/TEST: onStart()
07-08 10:19:26.039 com.troy.intentservicedemo I/TEST: onHandleIntent():30223
07-08 10:19:26.039 com.troy.intentservicedemo I/TEST: result: 23
07-08 10:19:29.100 com.troy.intentservicedemo I/TEST: onDestroy()
07-08 10:19:31.839 com.troy.intentservicedemo I/TEST: onCreate()
07-08 10:19:31.849 com.troy.intentservicedemo I/TEST: onStartCommand()
07-08 10:19:31.849 com.troy.intentservicedemo I/TEST: onStart()
07-08 10:19:31.869 com.troy.intentservicedemo I/TEST: onHandleIntent():30305
07-08 10:19:31.869 com.troy.intentservicedemo I/TEST: result: 52
07-08 10:19:34.899 com.troy.intentservicedemo I/TEST: onDestroy()

從上面Log中可以看出:

  1. UI執行緒的執行緒ID=30060,而onHandleIntent()的執行緒ID分別是30223,30305;由此知道onHandleIntent()方法是執行在工作執行緒中的;
  2. 並且IntentService的所有請求如果是在一個生命週期中完成的話,則所有請求是在一個工作執行緒中順序執行的。否則,是在不同的工作執行緒中完成。
  3. 同時驗證了:執行完所一個Intent請求物件所對應的工作之後,如果沒有新的Intent請求達到,則自動停止Service;

執行效果如圖:

這裡寫圖片描述

三 IntentService原始碼簡單解析

IntentService是Service的子類,擁有Service的所有生命週期方法;同時還有自己的ServiceHandler;

ServiceHandler相關原始碼如下:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;//volatile關鍵字保證同步,保證可見性
    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);
        }
    }

    public IntentService(String name) {//構造方法
        super();
        mName = name;
    }

IntentService 實際上是Looper,Handler,Service 的集合體,他不僅有服務的功能,還有處理和迴圈訊息的功能。

onCreate()的原始碼:建立了一個HandlerThread

@Override 
        public void onCreate() { 
                super.onCreate(); 

                HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); 
                thread.start(); 

                mServiceLooper = thread.getLooper(); 
                mServiceHandler = new ServiceHandler(mServiceLooper); 
        }

IntentService建立時就會建立Handler執行緒(HandlerThread)並且啟動,然後再得到當前執行緒的Looper物件來初始化IntentService的mServiceLooper,接著建立mServicehandler物件。

下面是onStart()的原始碼: 呼叫mServiceHandler

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

                mServiceHandler.sendMessage(msg); 
        }

當你啟動IntentService的時候,就會產生一條附帶startId和Intent的 Message併傳送到MessageQueue中,接下來Looper發現MessageQueue中有Message的時候,就會停止Handler 處理訊息。

handleMessage處理的程式碼如下:

@Override 
        public void handleMessage(Message msg) { 
                        onHandleIntent((Intent)msg.obj); 
                        stopSelf(msg.arg1); 
        }

接著呼叫 onHandleIntent((Intent)msg.obj),這是一個抽象的方法,其實就是我們要重寫實現的方法,我們可以在這個方法裡面處理我們的工作.當任務完成時就會呼叫stopSelf(msg.arg1)這個方法來結束指定的工作。

stopSelf(msg.arg1):

注意下:回撥完成後回撥用 stopSelf(msg.arg1),注意這個msg.arg1是個int值,相當於一個請求的唯一標識。每傳送一個請求,會生成一個唯一的標識,然後將請求放入佇列,當全部執行完成(最後一個請求也就相當於getLastStartId == startId),或者當前傳送的標識是最近發出的那一個(getLastStartId == startId),則會銷燬我們的Service.如果傳入的是-1則直接銷燬。

當所有的工作執行完後:就會執行onDestroy方法。

onDestroy原始碼如下:

@Override 
        public void onDestroy() { 
                mServiceLooper.quit(); 
        }

服務結束後呼叫這個方法 mServiceLooper.quit()使looper停下來。

最後總結一下上述的分析:

1、 IntentService是一個基於訊息的服務,每次啟動該服務並不是馬上處理你的工作,而是首先會建立對應的Looper,Handler並且在MessageQueue中添 加的附帶客戶Intent的Message物件, 當Looper發現有Message的時候接著得到Intent物件通過在 onHandleIntent((Intent)msg.obj)中呼叫你的處理程式. 處理完後即會停止自己的服務. 意思是Intent的生命週期跟你的 處理的任務是一致的. 所以這個類用下載任務中非常好,下載任務結束後服務自身就會結束退出。

2、IntentService是不適合用於bindService()這樣的啟動方式的。其次我們通過startService多次啟動Service時,相當於在MessageQueue中新增了多個任務,就可以實現多工按照順序執行。

3、只有在onHandleIntent()方法中執行的程式碼才是在工作執行緒中執行的。IntentService的停止不是因為在handleMessage() 中執行了stopSelf(msg.arg1);而是系統自己停止的。

相關文章