IntentService锚扎,可以看做是Service和HandlerThread的結合體吞瞪,在完成了使命之后會自動停止,適合需要在工作線程處理UI無關任務的場景驾孔。
- IntentService 是繼承自 Service 并處理異步請求的一個類芍秆,在 IntentService 內有一個工作線程來處理耗時操作。
- 當任務執(zhí)行完后助币,IntentService 會自動停止浪听,不需要我們去手動結束。
- 如果啟動 IntentService 多次眉菱,那么每一個耗時操作會以工作隊列的方式在 IntentService 的 onHandleIntent 回調方法中執(zhí)行迹栓,依次去執(zhí)行,使用串行的方式俭缓,執(zhí)行完自動結束克伊。
例子
????下面是一個例子酥郭,點擊開始啟動一個IntentService去更新進度條,更新完畢IntentService會自動結束愿吹。如果多次點擊開始不从,就會執(zhí)行多遍,多遍執(zhí)行完之后IntentService才會執(zhí)行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();
}
}
/**
* 發(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í)行死宣。