IntentService,用完即走

IntentService锚扎,可以看做是Service和HandlerThread的結合體吞瞪,在完成了使命之后會自動停止,適合需要在工作線程處理UI無關任務的場景驾孔。

  • IntentService 是繼承自 Service 并處理異步請求的一個類芍秆,在 IntentService 內有一個工作線程來處理耗時操作。
  • 當任務執(zhí)行完后助币,IntentService 會自動停止浪听,不需要我們去手動結束。
  • 如果啟動 IntentService 多次眉菱,那么每一個耗時操作會以工作隊列的方式在 IntentService 的 onHandleIntent 回調方法中執(zhí)行迹栓,依次去執(zhí)行,使用串行的方式俭缓,執(zhí)行完自動結束克伊。

例子
????下面是一個例子酥郭,點擊開始啟動一個IntentService去更新進度條,更新完畢IntentService會自動結束愿吹。如果多次點擊開始不从,就會執(zhí)行多遍,多遍執(zhí)行完之后IntentService才會執(zhí)行onDestroy方法犁跪。

image.png

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

    /**
     * 發(fā)送進度消息
     */
    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);
    }
}

啟動之后會先執(zhí)行構造方法椿息,然后執(zhí)行onCreate方法,再到onHandleIntent方法坷衍。在onHandleIntent讓進度自增寝优,每次自增睡眠50ms并向Activity發(fā)送廣播并傳遞進度的數據。

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 {
    /**
     * 狀態(tài)文字
     */
    @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("線程狀態(tài):未運行");
        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("線程狀態(tài):" + 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;
        }
    }
}

點擊開始按鈕枫耳,會啟動MyIntentService乏矾。mLocalBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter)注冊廣播,接收廣播消息和數據迁杨,并時刻更改進度條進度钻心。

注冊MyIntentService

      <service
          android:name=".ServiceDemo.MyIntentService">
      </service>

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

IntentService繼承自Service,內部有一個HandlerThread對象铅协。

在onCreate的時候會創(chuàng)建一個HandlerThread對象捷沸,并啟動線程。緊接著創(chuàng)建ServiceHandler對象警医,ServiceHandler繼承自Handler亿胸,用來處理消息。ServiceHandler將獲取HandlerThread的Looper就可以開始正常工作了预皇。

每啟動一次onStart方法侈玄,就會把數消息和數據發(fā)給mServiceHandler,相當于發(fā)送了一次Message消息給HandlerThread的消息隊列吟温。mServiceHandler會把數據傳個onHandleIntent方法序仙,onHandleIntent是個抽象方法,需要在IntentService實現鲁豪,所以每次onStart方法之后都會調用我們自己寫的onHandleIntent方法去處理潘悼。處理完畢使用stopSelf通知HandlerThread已經處理完畢,HandlerThread繼續(xù)觀察消息隊列爬橡,如果還有未執(zhí)行玩的message則繼續(xù)執(zhí)行治唤,否則結束。

啟動 IntentService 為什么不需要新建線程糙申?
IntentService內部的HandlerThread 繼承自 Thread宾添,內部封裝了 Looper,在這里新建線程并啟動,所以啟動 IntentService 不需要新建線程缕陕。

為什么不建議通過 bindService() 啟動 IntentService粱锐?

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

IntentService 源碼中的 onBind() 默認返回 null;不適合 bindService() 啟動服務扛邑,如果你執(zhí)意要 bindService() 來啟動 IntentService怜浅,可能因為你想通過 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,這樣那么 onHandleIntent() 不會被回調蔬崩,相當于在你使用 Service 而不是 IntentService恶座。


為什么多次啟動 IntentService 會順序執(zhí)行事件,停止服務后舱殿,后續(xù)的事件得不到執(zhí)行奥裸?

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

IntentService 中使用的 Handler、Looper沪袭、MessageQueue 機制把消息發(fā)送到線程中去執(zhí)行的,所以多次啟動 IntentService 不會重新創(chuàng)建新的服務和新的線程樟氢,只是把消息加入消息隊列中等待執(zhí)行冈绊,而如果服務停止,會清除消息隊列中的消息埠啃,后續(xù)的事件得不到執(zhí)行死宣。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碴开,隨后出現的幾起案子毅该,更是在濱河造成了極大的恐慌,老刑警劉巖潦牛,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眶掌,死亡現場離奇詭異,居然都是意外死亡巴碗,警方通過查閱死者的電腦和手機朴爬,發(fā)現死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來橡淆,“玉大人召噩,你說我怎么就攤上這事∫菥簦” “怎么了具滴?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長师倔。 經常有香客問我构韵,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任贞绳,我火速辦了婚禮谷醉,結果婚禮上,老公的妹妹穿的比我還像新娘冈闭。我一直安慰自己俱尼,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布萎攒。 她就那樣靜靜地躺著遇八,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耍休。 梳的紋絲不亂的頭發(fā)上刃永,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音羊精,去河邊找鬼斯够。 笑死,一個胖子當著我的面吹牛喧锦,可吹牛的內容都是我干的读规。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼燃少,長吁一口氣:“原來是場噩夢啊……” “哼束亏!你這毒婦竟也來了?” 一聲冷哼從身側響起阵具,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤碍遍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阳液,有當地人在樹林里發(fā)現了一具尸體怕敬,經...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年趁舀,在試婚紗的時候發(fā)現自己被綠了赖捌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡矮烹,死狀恐怖越庇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情奉狈,我是刑警寧澤卤唉,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站仁期,受9級特大地震影響桑驱,放射性物質發(fā)生泄漏竭恬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一熬的、第九天 我趴在偏房一處隱蔽的房頂上張望痊硕。 院中可真熱鬧,春花似錦押框、人聲如沸岔绸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盒揉。三九已至,卻和暖如春兑徘,著一層夾襖步出監(jiān)牢的瞬間刚盈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工挂脑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藕漱,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓崭闲,卻偏偏與公主長得像谴分,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子镀脂,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內容