Android 服務之IntentService 詳解

1. IntentService 概述

IntentService繼承自Service,所以,我們可以用來執行後臺耗時任務。那爲什麼又要封裝一個這樣的IntentService呢?下面我們來看看IntentService有什麼特點。

  1. 它本質是一種特殊的Service,繼承自Service並且本身就是一個抽象類
  2. 它可以用於在後臺執行耗時的異步任務,當任務完成後會自動停止
  3. 它擁有較高的優先級,不易被系統殺死(繼承自Service的緣故),因此比較適合執行一些高優先級的異步任務
  4. 它內部通過HandlerThread和Handler實現異步操作
  5. 創建IntentService時,只需實現onHandleIntent和構造方法,onHandleIntent爲異步方法,可以執行耗時操作

2.IntentService如何使用

我們來模擬一個後臺加載進度條的例子。爲了模擬多任務串行處理,我們進度條會加載兩次。
來看看效果:

展示效果

前面我們提到了IntentService是一個抽象類,所以我們第一步就是繼承IntentService。
1.我們需要繼承IntentService,然後重寫onHandleIntent方法。模擬耗時任務就是在onHandleIntent方法中進行處理。下面我們來具體看看這個類,我們取名爲:
MyIntentService.java

/**
 * Created by zhangcong on 2018/3/21.
 */

public class MyIntentService extends IntentService {

    private LocalBroadcastManager localBroadcastManager;
    private boolean isRunning;
    private int count;

    /**
     * Creates an IntentService.
     *
     */
    public MyIntentService() {
        super("MyIntentService");
        Log.i("SIMON","MyIntentService");
    }

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

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

    @Override
    public void onCreate() {
        Log.i("SIMON","onCreate");
        super.onCreate();
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.i("SIMON","onHandleIntent");
        //test mutiTask
        String firstTask=intent.getStringExtra(MainActivity.INTENT_TAG);
        Log.i("SIMON",firstTask);
        try {
            Thread.sleep(1000);
            isRunning = true;
            count = 0;
            while (isRunning) {
                count++;
                if (count >= 100) {
                    isRunning = false;
                }
                Thread.sleep(50);
                sendMessage("線程運行中",count);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * send message
     */
    private void sendMessage(String status,int count) {
        Intent intent=new Intent(MainActivity.ACTION_THREAD);
        intent.putExtra("status",status);
        intent.putExtra("count",count);
        localBroadcastManager.sendBroadcast(intent);
    }

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

必須創建一個構造方法,並且調用父類帶String的構造方法來創建一個IntentService。
這裏我們重寫了許多方法,是爲了看整個Service的生命週期。後面會貼出log。

2.在activity進行模擬

public class MainActivity extends AppCompatActivity {
    public final static String ACTION_THREAD = "action.thread";
    public final static String INTENT_TAG = "com.wind.intent";
    private TextView textView;
    private Button button;
    private ProgressBar progressBar;
    private TextView percent;
    private LocalBroadcastManager localBroadcastManager;
    private MyBroadCastReceiver myBroadCastReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_text);
        progressBar = findViewById(R.id.pb_progress);
        percent=findViewById(R.id.tv_percent);
        button=findViewById(R.id.bt_button);
        //registerReceiver
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        myBroadCastReceiver=new MyBroadCastReceiver();
        IntentFilter intentFilter=new IntentFilter();
        intentFilter.addAction(ACTION_THREAD);
        localBroadcastManager.registerReceiver(myBroadCastReceiver,intentFilter);
        //initView
        textView.setText("線程狀態:未運行");
        progressBar.setMax(100);
        progressBar.setProgress(0);
        percent.setText("0%");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("SIMON","SIMON");
                Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.setAction("sss");
                intent.putExtra(INTENT_TAG,"firstintent");
                startService(intent);
                intent.putExtra(INTENT_TAG,"secondintent");
                startService(intent);//模擬多任務
            }
        });
    }

    //define broadcastreceiver
    public class MyBroadCastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case ACTION_THREAD:
                    int progress = intent.getIntExtra("count", 0);
                    String text = intent.getStringExtra("status");
                    textView.setText(text);
                    percent.setText(progress + "%");
                    progressBar.setProgress(progress);
                    if (progress >= 100) {
                        textView.setText("線程結束");
                        break;
                    }
                default:
                    break;
            }

        }
    }
}

調用
這裏用了一個廣播,是用來傳遞數據的,然後在主線程進行UI的更新。

我們來看下log:

03-22 05:22:11.373 19700-19700/com.wind.intentservice I/SIMON: SIMON
03-22 05:22:11.401 19700-19700/com.wind.intentservice I/SIMON: MyIntentService
03-22 05:22:11.401 19700-19700/com.wind.intentservice I/SIMON: onCreate
03-22 05:22:11.403 19700-19700/com.wind.intentservice I/SIMON: onStartCommand
03-22 05:22:11.403 19700-19700/com.wind.intentservice I/SIMON: onStart
03-22 05:22:11.403 19700-19723/com.wind.intentservice I/SIMON: onHandleIntent
03-22 05:22:11.403 19700-19723/com.wind.intentservice I/SIMON: firstintent
03-22 05:22:11.406 19700-19700/com.wind.intentservice I/SIMON: onStartCommand
03-22 05:22:11.406 19700-19700/com.wind.intentservice I/SIMON: onStart
03-22 05:22:17.562 19700-19723/com.wind.intentservice I/SIMON: onHandleIntent
03-22 05:22:17.562 19700-19723/com.wind.intentservice I/SIMON: secondintent
03-22 05:22:23.746 19700-19700/com.wind.intentservice I/SIMON: onDestroy

這裏我們通過startService來啓動兩次服務,但是我們可以看到,只會實例化一次Service,而是通過onStart和onStartCommand方法來進行多任務的執行。是按照任務的順序來執行的,最後判斷沒有任務了,再把服務進行關閉。這裏可能會有點困惑,這個是怎麼實現的,後面我們會進行源碼分析。

3. IntentService源碼解析

我們從startService()開始分析,首先調用構造方法來創建IntentService。
然後調用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);
    }

  當第一啓動IntentService時,它的onCreate方法將會被調用,其內部會去創建一個HandlerThread並啓動它,接着創建一個ServiceHandler(繼承Handler),傳入HandlerThread的Looper對象,這樣ServiceHandler就變成可以處理異步線程的執行類了(因爲Looper對象與HandlerThread綁定,而HandlerThread又是一個異步線程,我們把HandlerThread持有的Looper對象傳遞給Handler後,ServiceHandler內部就持有異步線程的Looper,自然就可以執行異步任務了),那麼IntentService是怎麼啓動異步任務的呢?其實IntentService啓動後還會去調用onStartCommand方法,而onStartCommand方法又會去調用onStart方法,我們看看它們的源碼:

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

調用onStart方法:

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

  從源碼我們可以看出,在onStart方法中,IntentService通過mServiceHandler的sendMessage方法發送了一個消息,這個消息將會發送到HandlerThread中進行處理(因爲HandlerThread持有Looper對象,所以其實是Looper從消息隊列中取出消息進行處理,然後調用mServiceHandler的handleMessage方法),我們看看ServiceHandler的源碼:

 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);
        }
    }

 這裏其實也說明onHandleIntent確實是一個異步處理方法(ServiceHandler本身就是一個異步處理的handler類),在onHandleIntent方法執行結束後,IntentService會通過 stopSelf(int startId)方法來嘗試停止服務。這裏採用stopSelf(int startId)而不是stopSelf()來停止服務,是因爲stopSelf()會立即停止服務,而stopSelf(int startId)會等待所有消息都處理完後才終止服務。最後看看onHandleIntent方法的聲明:

 protected abstract void onHandleIntent(@Nullable Intent intent);

到此我們就知道了IntentService的onHandleIntent方法是一個抽象方法,所以我們在創建IntentService時必須實現該方法,通過上面一系列的分析可知,onHandleIntent方法也是一個異步方法。這裏要注意的是如果後臺任務只有一個的話,onHandleIntent執行完,服務就會銷燬,但如果後臺任務有多個的話,onHandleIntent執行完最後一個任務時,服務才銷燬。最後我們要知道每次執行一個後臺任務就必須啓動一次IntentService,而IntentService內部則是通過消息的方式發送給HandlerThread的,然後由Handler中的Looper來處理消息,而Looper是按順序從消息隊列中取任務的,也就是說IntentService的後臺任務時順序執行的,當有多個後臺任務同時存在時,這些後臺任務會按外部調用的順序排隊執行。到這裏我們就徹底明白了IntentService是怎麼保證任務按照順序來執行的。

到這裏,我們就講IntentService的源碼分析完了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章