IntentService,用完即走

小鴻洋發表於2018-07-10

IntentService

    IntentService,可以看做是Service和HandlerThread的結合體,在完成了使命之後會自動停止,適合需要在工作執行緒處理UI無關任務的場景。

  • IntentService 是繼承自 Service 並處理非同步請求的一個類,在 IntentService 內有一個工作執行緒來處理耗時操作。
  • 當任務執行完後,IntentService 會自動停止,不需要我們去手動結束。
  • 如果啟動 IntentService 多次,那麼每一個耗時操作會以工作佇列的方式在 IntentService 的 onHandleIntent 回撥方法中執行,依次去執行,使用序列的方式,執行完自動結束。

例子

    下面是一個例子,點選開始啟動一個IntentService去更新進度條,更新完畢IntentService會自動結束。如果多次點選開始,就會執行多遍,多遍執行完之後IntentService才會執行onDestroy方法。

這裡寫圖片描述


IntentService:

package com.bourne.android_common.ServiceDemo;

import android.app.IntentService;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;

import com.bourne.common_library.utils.Logout;

public class MyIntentService extends IntentService {

    /**
     * 是否正在執行
     */
    private boolean isRunning;

    /**
     *進度
     */
    private int count;

    /**
     * 廣播
     */
    private LocalBroadcastManager mLocalBroadcastManager;

    public MyIntentService() {
        super("MyIntentService");
        Logout.e("MyIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Logout.e("onCreate");
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Logout.e("onHandleIntent");
        try {
            Thread.sleep(1000);
            isRunning = true;
            count = 0;
            while (isRunning) {
                count++;
                if (count >= 100) {
                    isRunning = false;
                }
                Thread.sleep(50);
                sendThreadStatus("執行緒執行中...", count);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 傳送進度訊息
     */
    private void sendThreadStatus(String status, int progress) {
        Intent intent = new Intent(IntentServiceActivity.ACTION_TYPE_THREAD);
        intent.putExtra("status", status);
        intent.putExtra("progress", progress);
        mLocalBroadcastManager.sendBroadcast(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Logout.e("執行緒結束執行..." + count);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

    啟動之後會先執行構造方法,然後執行onCreate方法,再到onHandleIntent方法。在onHandleIntent讓進度自增,每次自增睡眠50ms並向Activity傳送廣播並傳遞進度的資料。 

IntentServiceActivity:

package com.bourne.android_common.ServiceDemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.bourne.android_common.R;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class IntentServiceActivity extends AppCompatActivity {
    /**
     * 狀態文字
     */
    @BindView(R.id.tv_status)
    TextView tv_status;

    /**
     * 進度文字
     */
    @BindView(R.id.tv_progress)
    TextView tv_progress;

    /**
     * 進度條
     */
    @BindView(R.id.progressbar)
    ProgressBar progressbar;

    private LocalBroadcastManager mLocalBroadcastManager;
    private MyBroadcastReceiver mBroadcastReceiver;
    public final static String ACTION_TYPE_THREAD = "action.type.thread";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service);
        ButterKnife.bind(this);

        //註冊廣播
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        mBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_TYPE_THREAD);
        mLocalBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);

        initView();
    }

    public void initView() {
        tv_status.setText("執行緒狀態:未執行");
        progressbar.setMax(100);
        progressbar.setProgress(0);
        tv_progress.setText("0%");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //登出廣播
        mLocalBroadcastManager.unregisterReceiver(mBroadcastReceiver);
    }

    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {

                case ACTION_TYPE_THREAD:
                    //更改UI
                    int progress = intent.getIntExtra("progress", 0);
                    tv_status.setText("執行緒狀態:" + intent.getStringExtra("status"));
                    progressbar.setProgress(progress);
                    tv_progress.setText(progress + "%");
                    if (progress >= 100) {
                        tv_status.setText("執行緒結束");
                    }
                    break;
            }
        }
    }

    @OnClick({R.id.btn_start})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_start:
                Intent intent = new Intent(IntentServiceActivity.this, MyIntentService.class);
                startService(intent);
                break;
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

    點選開始按鈕,會啟動MyIntentService。mLocalBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter)註冊廣播,接收廣播訊息和資料,並時刻更改進度條進度。 

註冊MyIntentService

        <service
            android:name=".ServiceDemo.MyIntentService">
        </service>
  • 1
  • 2
  • 3

IntentService原始碼分析

原始碼

package android.app;

import android.annotation.WorkerThread;
import android.annotation.Nullable;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;

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

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

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

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

    /**
     * You should not override this method for your IntentService. Instead,
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

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

    /**
     * Unless you provide binding for your service, you don't need to implement this
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

    IntentService繼承自Service,內部有一個HandlerThread物件。

    在onCreate的時候會建立一個HandlerThread物件,並啟動執行緒。緊接著建立ServiceHandler物件,ServiceHandler繼承自Handler,用來處理訊息。ServiceHandler將獲取HandlerThread的Looper就可以開始正常工作了。

    每啟動一次onStart方法,就會把數訊息和資料發給mServiceHandler,相當於傳送了一次Message訊息給HandlerThread的訊息佇列。mServiceHandler會把資料傳個onHandleIntent方法,onHandleIntent是個抽象方法,需要在IntentService實現,所以每次onStart方法之後都會呼叫我們自己寫的onHandleIntent方法去處理。處理完畢使用stopSelf通知HandlerThread已經處理完畢,HandlerThread繼續觀察訊息佇列,如果還有未執行玩的message則繼續執行,否則結束。


啟動 IntentService 為什麼不需要新建執行緒?

IntentService內部的HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裡新建執行緒並啟動,所以啟動 IntentService 不需要新建執行緒。


為什麼不建議通過 bindService() 啟動 IntentService?

@Override
public IBinder onBind(Intent intent) {
    return null;
}
  • 1
  • 2
  • 3
  • 4

    IntentService 原始碼中的 onBind() 預設返回 null;不適合 bindService() 啟動服務,如果你執意要 bindService() 來啟動 IntentService,可能因為你想通過 Binder 或 Messenger 使得 IntentService 和 Activity 可以通訊,這樣那麼 onHandleIntent() 不會被回撥,相當於在你使用 Service 而不是 IntentService。


為什麼多次啟動 IntentService 會順序執行事件,停止服務後,後續的事件得不到執行?

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

@Override
public void onDestroy() {
    mServiceLooper.quit();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    IntentService 中使用的 Handler、Looper、MessageQueue 機制把訊息傳送到執行緒中去執行的,所以多次啟動 IntentService 不會重新建立新的服務和新的執行緒,只是把訊息加入訊息佇列中等待執行,而如果服務停止,會清除訊息佇列中的訊息,後續的事件得不到執行。


參考文章

相關文章