深入理解IntentService(HandlerThread的經典應用)

IntentService 簡介

public abstract class IntentService extends Service {
        .......
}

IntentService是一個抽象類,繼承Service。

IntentService因爲是一個Service,在後臺不會輕易被系統殺死。他能夠在onHandleIntent中接收intent請求。然後在子線程中按順序處理。

特點:

優先級比較高的,用於串行執行異步任務,並且是一個會自盡的Service(stopSelf())。當任務執行完成以後,IntentService會自動停止。

啓動IntentService與啓動普通的Service一樣。可以啓動IntentService多次。每一個耗時的操作都會以工作隊列的方式在IntentService的OnHandleIntent回調中執行,並且只有一個工作線程。(由HandlerThread開啓一個新的線程)

請求都在一個單線程中,不會阻塞應用程序的主線程(UI Thread),同一時間只處理一個請求。 那麼,用 IntentService 有什麼好處呢?首先,我們省去了在 Service 中手動開線程的麻煩,第二,當操作完成時,我們不用手動停止 Service。

而Service 是長期運行在後臺的應用程序組件。

Service 不是一個單獨的進程,它和應用程序在同一個進程中,Service 也不是一個線程,它和線程沒有任何關係,所以它不能直接處理耗時操作。如果直接把耗時操作放在 Service 的 onStartCommand() 中,很容易引起 ANR .如果有耗時操作就必須開啓一個單獨的線程來處理。

源碼解析:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    //內部創建的 Handler
    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;
    }

    //設置當前服務被意外關閉後是否重新
    //如果設置爲 true,onStartCommand() 方法將返回 Service.START_REDELIVER_INTENT,這樣當
    //當前進程在 onHandleIntent() 方法返回前銷燬時,會重啓進程,重新使用之前的 Intent 啓動這個服務
    //(如果有多個 Intent,只會使用最後的一個)
    //如果設置爲 false,onStartCommand() 方法返回 Service.START_NOT_STICKY,當進程銷燬後也不重啓服務
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //創建時啓動一個 HandlerThread
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        //拿到 HandlerThread 中的 Looper,然後創建一個子線程中的 Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        //將 intent 和 startId 以消息的形式發送到 Handler
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * 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(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();    //值得學習的,在銷燬時退出 Looper
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

工作流程:

  • 創建了一個 HandlerThread 默認的工作線程
  • 使用 HandlerThread 的 Looper 創建了一個 Handler,這個 Handler 執行在子線程
  • 在 onStartCommand() 中調用 onStart(),然後在 onStart() 中將 intent 和 startId 以消息的形式發送到 Handler
  • 在 Handler 中將消息隊列中的 Intent 按順序傳遞給 onHandleIntent() 方法
  • 在處理完所有啓動請求後自動停止服務,不需要我們調用 stopSelf()

這個 stopSelf() 方法傳遞了一個 id,這個 id 是啓動服務時 IActivityManager 分配的 id,當我們調用 stopSelf(id) 方法結束服務時,IActivityManager 會對比當前 id 是否爲最新啓動該服務的 id,如果是就關閉服務。只有當最後一次啓動 IntentService 的任務執行完畢纔會關閉這個服務。IntentService 中除了 onHandleIntent 方法其他都是運行在主線程的

案例:

public class DownLoadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String DOWNLOADURL = "download_url";
    public static final int DOWN_START = 0;
    public static final int DOWN_FINISH = 1;

    private static  Handler mUIHandler;

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public DownLoadService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        String url = intent.getStringExtra(DOWNLOADURL);
        if (mUIHandler != null) {
            //下載開始,通知主線程
            Message startMsg = mUIHandler.obtainMessage(DOWN_START, "\n 開始下載 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(startMsg);
        }

        SystemClock.sleep(4000);    //模擬下載

        if (mUIHandler != null) {
            //下載完成,通知主線程
            Message finishMsg = mUIHandler.obtainMessage(DOWN_FINISH, "\n 下載完成 @" + System.currentTimeMillis() + "\n" + url);
            mUIHandler.sendMessage(finishMsg);
        }

    }

    public static void setUiHandler(Handler handler) {
        mUIHandler = handler;
    }

}

 

 

Activity:

public class MainActivity extends RxAppCompatActivity implements View.OnClickListener, Handler.Callback {
    TextView mTvStartMsg;
    TextView mTvFinishMsg;
    Button mBtnStartDownload;

    private Handler mUIHandler;

    private List<String> urlList = Arrays.asList("https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg",
            "https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg",
            "https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg");

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTvStartMsg = (TextView) findViewById(R.id.tv_start_msg);
        mTvFinishMsg = (TextView) findViewById(R.id.tv_finish_msg);
        mBtnStartDownload = (Button) findViewById(R.id.btn_start_download);

        mBtnStartDownload.setOnClickListener(this);

        mUIHandler = new Handler(this);
        DownLoadService.setUiHandler(mUIHandler);

    }

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

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_start_msg:

                break;
            case R.id.tv_finish_msg:
                break;
            case R.id.btn_start_download:
                Intent intent = new Intent(this,DownLoadService.class);
                for (String url : urlList) {
                    intent.putExtra(DownLoadService.DOWNLOADURL, url);
                    startService(intent);
                    SystemClock.sleep(4000);
                    Log.i("onion","startService:"+url);
                }

                mBtnStartDownload.setEnabled(false);

                break;
        }

    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case DownLoadService.DOWN_FINISH:
                Log.i("onion","開始下載: "+ msg.obj);

                break;
            case DownLoadService.DOWN_START:
                Log.i("onion","下載完成: "+ msg.obj);

                break;
        }

        return true;
    }


}

結果:

07-18 08:29:19.942 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:23.944 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:27.945 4418-4418/lib.com.myapplication I/onion: startService:https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:27.948 4418-4418/lib.com.myapplication I/onion: 下載完成: 
     開始下載 @1531916967948
    https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
07-18 08:29:31.949 4418-4418/lib.com.myapplication I/onion: 開始下載: 
     下載完成 @1531916971948
    https://ws1.sinaimg.cn/large/610dc034ly1fgepc1lpvfj20u011i0wv.jpg
    下載完成: 
     開始下載 @1531916971949
    https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.949 4418-4418/lib.com.myapplication I/onion: 開始下載: 
     下載完成 @1531916975949
    https://ws1.sinaimg.cn/large/d23c7564ly1fg6qckyqxkj20u00zmaf1.jpg
07-18 08:29:35.950 4418-4418/lib.com.myapplication I/onion: 下載完成: 
     開始下載 @1531916975950
    https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg
07-18 08:29:39.951 4418-4418/lib.com.myapplication I/onion: 開始下載: 
     下載完成 @1531916979951
    https://ws1.sinaimg.cn/large/610dc034ly1fgchgnfn7dj20u00uvgnj.jpg

 

IntentService內部的HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裏新建線程並啓動,所以啓動 IntentService 不需要新建線程。IntentService 中使用的 Handler、Looper、MessageQueue 機制把消息發送到子線程中去執行的,所以多次啓動 IntentService 不會重新創建新的服務和新的線程,只是把消息加入消息隊列中等待執行,而如果服務停止,會清除消息隊列中的消息,後續的事件得不到執行。

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