1. IntentService 概述
IntentService繼承自Service,所以,我們可以用來執行後臺耗時任務。那爲什麼又要封裝一個這樣的IntentService呢?下面我們來看看IntentService有什麼特點。
- 它本質是一種特殊的Service,繼承自Service並且本身就是一個抽象類
- 它可以用於在後臺執行耗時的異步任務,當任務完成後會自動停止
- 它擁有較高的優先級,不易被系統殺死(繼承自Service的緣故),因此比較適合執行一些高優先級的異步任務
- 它內部通過HandlerThread和Handler實現異步操作
- 創建IntentService時,只需實現onHandleIntent和構造方法,onHandleIntent爲異步方法,可以執行耗時操作
2.IntentService如何使用
我們來模擬一個後臺加載進度條的例子。爲了模擬多任務串行處理,我們進度條會加載兩次。
來看看效果:
前面我們提到了IntentService是一個抽象類,所以我們第一步就是繼承IntentService。
1.我們需要繼承IntentService,然後重寫onHandleIntent方法。模擬耗時任務就是在onHandleIntent方法中進行處理。下面我們來具體看看這個類,我們取名爲:
MyIntentService.java
/**
* Created by zhangcong on 2018/3/21.
*/
public class MyIntentService extends IntentService {
private LocalBroadcastManager localBroadcastManager;
private boolean isRunning;
private int count;
/**
* Creates an IntentService.
*
*/
public MyIntentService() {
super("MyIntentService");
Log.i("SIMON","MyIntentService");
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.i("SIMON","onStart");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.i("SIMON","onStartCommand");
Log.i("SIMON","intent"+intent.getAction());
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.i("SIMON","onCreate");
super.onCreate();
localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i("SIMON","onHandleIntent");
//test mutiTask
String firstTask=intent.getStringExtra(MainActivity.INTENT_TAG);
Log.i("SIMON",firstTask);
try {
Thread.sleep(1000);
isRunning = true;
count = 0;
while (isRunning) {
count++;
if (count >= 100) {
isRunning = false;
}
Thread.sleep(50);
sendMessage("線程運行中",count);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* send message
*/
private void sendMessage(String status,int count) {
Intent intent=new Intent(MainActivity.ACTION_THREAD);
intent.putExtra("status",status);
intent.putExtra("count",count);
localBroadcastManager.sendBroadcast(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("SIMON","onDestroy");
}
}
必須創建一個構造方法,並且調用父類帶String的構造方法來創建一個IntentService。
這裏我們重寫了許多方法,是爲了看整個Service的生命週期。後面會貼出log。
2.在activity進行模擬
public class MainActivity extends AppCompatActivity {
public final static String ACTION_THREAD = "action.thread";
public final static String INTENT_TAG = "com.wind.intent";
private TextView textView;
private Button button;
private ProgressBar progressBar;
private TextView percent;
private LocalBroadcastManager localBroadcastManager;
private MyBroadCastReceiver myBroadCastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv_text);
progressBar = findViewById(R.id.pb_progress);
percent=findViewById(R.id.tv_percent);
button=findViewById(R.id.bt_button);
//registerReceiver
localBroadcastManager = LocalBroadcastManager.getInstance(this);
myBroadCastReceiver=new MyBroadCastReceiver();
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction(ACTION_THREAD);
localBroadcastManager.registerReceiver(myBroadCastReceiver,intentFilter);
//initView
textView.setText("線程狀態:未運行");
progressBar.setMax(100);
progressBar.setProgress(0);
percent.setText("0%");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("SIMON","SIMON");
Intent intent = new Intent(MainActivity.this, MyIntentService.class);
intent.setAction("sss");
intent.putExtra(INTENT_TAG,"firstintent");
startService(intent);
intent.putExtra(INTENT_TAG,"secondintent");
startService(intent);//模擬多任務
}
});
}
//define broadcastreceiver
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_THREAD:
int progress = intent.getIntExtra("count", 0);
String text = intent.getStringExtra("status");
textView.setText(text);
percent.setText(progress + "%");
progressBar.setProgress(progress);
if (progress >= 100) {
textView.setText("線程結束");
break;
}
default:
break;
}
}
}
}
調用
這裏用了一個廣播,是用來傳遞數據的,然後在主線程進行UI的更新。
我們來看下log:
03-22 05:22:11.373 19700-19700/com.wind.intentservice I/SIMON: SIMON
03-22 05:22:11.401 19700-19700/com.wind.intentservice I/SIMON: MyIntentService
03-22 05:22:11.401 19700-19700/com.wind.intentservice I/SIMON: onCreate
03-22 05:22:11.403 19700-19700/com.wind.intentservice I/SIMON: onStartCommand
03-22 05:22:11.403 19700-19700/com.wind.intentservice I/SIMON: onStart
03-22 05:22:11.403 19700-19723/com.wind.intentservice I/SIMON: onHandleIntent
03-22 05:22:11.403 19700-19723/com.wind.intentservice I/SIMON: firstintent
03-22 05:22:11.406 19700-19700/com.wind.intentservice I/SIMON: onStartCommand
03-22 05:22:11.406 19700-19700/com.wind.intentservice I/SIMON: onStart
03-22 05:22:17.562 19700-19723/com.wind.intentservice I/SIMON: onHandleIntent
03-22 05:22:17.562 19700-19723/com.wind.intentservice I/SIMON: secondintent
03-22 05:22:23.746 19700-19700/com.wind.intentservice I/SIMON: onDestroy
這裏我們通過startService來啓動兩次服務,但是我們可以看到,只會實例化一次Service,而是通過onStart和onStartCommand方法來進行多任務的執行。是按照任務的順序來執行的,最後判斷沒有任務了,再把服務進行關閉。這裏可能會有點困惑,這個是怎麼實現的,後面我們會進行源碼分析。
3. IntentService源碼解析
我們從startService()開始分析,首先調用構造方法來創建IntentService。
然後調用IntentService的onCreate方法:
@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);
}
當第一啓動IntentService時,它的onCreate方法將會被調用,其內部會去創建一個HandlerThread並啓動它,接着創建一個ServiceHandler(繼承Handler),傳入HandlerThread的Looper對象,這樣ServiceHandler就變成可以處理異步線程的執行類了(因爲Looper對象與HandlerThread綁定,而HandlerThread又是一個異步線程,我們把HandlerThread持有的Looper對象傳遞給Handler後,ServiceHandler內部就持有異步線程的Looper,自然就可以執行異步任務了),那麼IntentService是怎麼啓動異步任務的呢?其實IntentService啓動後還會去調用onStartCommand方法,而onStartCommand方法又會去調用onStart方法,我們看看它們的源碼:
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
調用onStart方法:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
從源碼我們可以看出,在onStart方法中,IntentService通過mServiceHandler的sendMessage方法發送了一個消息,這個消息將會發送到HandlerThread中進行處理(因爲HandlerThread持有Looper對象,所以其實是Looper從消息隊列中取出消息進行處理,然後調用mServiceHandler的handleMessage方法),我們看看ServiceHandler的源碼:
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);
}
}
這裏其實也說明onHandleIntent確實是一個異步處理方法(ServiceHandler本身就是一個異步處理的handler類),在onHandleIntent方法執行結束後,IntentService會通過 stopSelf(int startId)方法來嘗試停止服務。這裏採用stopSelf(int startId)而不是stopSelf()來停止服務,是因爲stopSelf()會立即停止服務,而stopSelf(int startId)會等待所有消息都處理完後才終止服務。最後看看onHandleIntent方法的聲明:
protected abstract void onHandleIntent(@Nullable Intent intent);
到此我們就知道了IntentService的onHandleIntent方法是一個抽象方法,所以我們在創建IntentService時必須實現該方法,通過上面一系列的分析可知,onHandleIntent方法也是一個異步方法。這裏要注意的是如果後臺任務只有一個的話,onHandleIntent執行完,服務就會銷燬,但如果後臺任務有多個的話,onHandleIntent執行完最後一個任務時,服務才銷燬。最後我們要知道每次執行一個後臺任務就必須啓動一次IntentService,而IntentService內部則是通過消息的方式發送給HandlerThread的,然後由Handler中的Looper來處理消息,而Looper是按順序從消息隊列中取任務的,也就是說IntentService的後臺任務時順序執行的,當有多個後臺任務同時存在時,這些後臺任務會按外部調用的順序排隊執行。到這裏我們就徹底明白了IntentService是怎麼保證任務按照順序來執行的。
到這裏,我們就講IntentService的源碼分析完了。