- 1. IntentService 概述
- 2.IntentService如何使用
- 3. IntentService源码解析
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的源码分析完了。