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 不會重新創建新的服務和新的線程,只是把消息加入消息隊列中等待執行,而如果服務停止,會清除消息隊列中的消息,後續的事件得不到執行。