众所周知,service可以用来执行后台任务。但是我们不能把类似下载等这样的耗时任务直接放在service里面执行,否则会造成ANR。一般遇到这样的情况我们有两种处理方式:
1.自己创建并管理线程来执行这些耗时操作
2.通过IntentService实现。
这里通过模拟耗时任务的方式来简单介绍下IntentService的实现方式:
第一步:写一个IntentService的子类
IntentService子类必须重写onHandleIntent(Intent intent),该方法是在子线程执行的,可以用来处理耗时任务,这里的Intent对象是从Activity中传递进来的。至于为什么后面再具体分析。
public class CustomIntentSerivce extends IntentService{
public String TAG = getClass().getSimpleName();
public static String EXTRA = "extra";
private int i=0;
public CustomIntentSerivce() {
super("customintentservice");
}
@Override
protected void onHandleIntent(Intent intent) {
//可以在这个方法中处理耗时任务
if(intent!=null){
i =intent.getIntExtra(CustomIntentSerivce.EXTRA,-1);
}
//这里打印当前的线程id
Log.i(TAG, "i: "+i+" onHandleIntent Thread name: "+Thread.currentThread().getName()
+" id:"+Thread.currentThread().getId());
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
//这里打印当前的线程id
Log.i(TAG, "CustomIntentSerivce Thread: "+Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
return super.onBind(intent);
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}
}
第二步:在Manifest.xml中注册Service
<service android:name=".CustomIntentSerivce"/>
第三步:在MainActivity中启动CustomIntentSerivce
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
testIntentService();
}
});
}
private void testIntentService() {
Intent intent1 = new Intent(MainActivity.this,CustomIntentSerivce.class);
intent1.putExtra(CustomIntentSerivce.EXTRA,1);
startService(intent1);
Intent intent2 = new Intent(MainActivity.this,CustomIntentSerivce.class);
intent2.putExtra(CustomIntentSerivce.EXTRA,2);
startService(intent2);
Intent intent3 = new Intent(MainActivity.this,CustomIntentSerivce.class);
intent3.putExtra(CustomIntentSerivce.EXTRA,3);
startService(intent3);
}
}
其中activity_main布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.android.lanjing.intentservice.MainActivity">
<Button
android:id="@+id/btn_start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动IntentService"
android:layout_centerInParent="true"/>
</RelativeLayout>
在MainActivity中通过startService(Intent intent)的方式启动service,虽然启动了3次但是Service的实例只会创建一个,即只执行一次Service的onCreate(),但是会执行3次onStartCommond()。
通过分析IntentService的源码可以发现在onCreate()中初始化了一个HandlerThread对象(线程):thread,同时调用thread.start()启动线程,在HandlerThread的run()内部有初始化Looper的操作。然后通过thread.getLooper()拿到这个线程的Looper,用这个Looper对象初始化ServiceHandler对象。源码如下:
@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的内部就可以通过ServiceHandler发送消息了,该消息的处理操作最终是在子线程HandlerThread中完成的。
继续分析IntentService类,可以看到在IntentService的onStartCommand(Intent intent, int flags, int startId)方法中调用了onStart(intent, startId)。在onStart(intent, startId)中封装了一个Message,同时通过ServiceHandler将消息发送到消息队列中等待处理。源码如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
这里可以看到从MainActivity中传递进来的Intent对象即是Message的obj属性的值,startId是Message的arg1属性值,该值在停止IntentService时会用到。
现在再来看下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);
}
}
可以看到在ServiceHandler类的handleMessage(Message msg)方法中将message的obj强转为Intent对象(就是从MainActivity中传递过来的)。通过调用抽象方法onHandleIntent((Intent)msg.obj)来将具体的耗时操作暴露给开发人员去完成。
同时在onHandleIntent((Intent)msg.obj)后面调用了stopSelf(msg.arg1)方法,即通过startId停止IntentService,因此IntentService启动之后不需要开发人员手动去调用stopService(intent1),任务完成之后它自己会销毁。
下面是运行程序点击按钮后打印的日志:
23474-23474 I/CustomIntentSerivce: onCreate:
23474-23474 I/CustomIntentSerivce: onStartCommand:
23474-23474 I/CustomIntentSerivce: CustomIntentSerivce Thread: 1
23474-23474 I/CustomIntentSerivce: onStartCommand:
23474-23474 I/CustomIntentSerivce: CustomIntentSerivce Thread: 1
23474-23474 I/CustomIntentSerivce: onStartCommand:
23474-23474 I/CustomIntentSerivce: CustomIntentSerivce Thread: 1
23474-24209 I/CustomIntentSerivce: i: 1 onHandleIntent Thread name: IntentService[customintentservice] id:12824
23474-24209 I/CustomIntentSerivce: i: 2 onHandleIntent Thread name: IntentService[customintentservice] id:12824
23474-24209 I/CustomIntentSerivce: i: 3 onHandleIntent Thread name: IntentService[customintentservice] id:12824
23474-23474 I/CustomIntentSerivce: onDestroy:
通过日志可以看出在onStartCommand(Intent intent, int flags, int startId)方法中打印的线程id是:1,在onHandleIntent(Intent intent)方法中打印的线程id是:12824,证实了onHandleIntent()方法是在子线程中执行的。
至此IntentService的介绍就差不多了,如有不对的地方请读者指出。