Android(java)学习笔记210:Android线程形态之 IntentService
1. IntentService原理
IntentService是一种特殊的Service,既然是Service,使用的时候记得在AndroidManifest清单文件中注册。
并且它是一个抽象类,因此必须创建它的子类才能使用IntentService,IntentService可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高级很多,所以IntentService比较适合执行高优先级的后台任务,因为它的优先级高不容易被系统杀死。
在实现上,IntentService封装了HandlerThread 和 Handler,这一点我们可以在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方法会被调用,onCreate方法会创建一个HandlerThread,然后利用它的Looper构造一个Handler对象mServiceHandler(ServiceHandler继承自Handler,ServiceHandler是IntentService内部类)
我们来到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); } }
这样通过mServiceHandler发送的消息都会最终在HandlerThread中执行,从这个角度来看,IntentService也可以用于执行后台任务。
每次启动IntentService,它的onStartCommand方法就会调用一次,IntentService在onStartCommand中处理每个后台任务的Intent。
接下来我们要看一下IntentService在OnStartCommand方法是如何处理外界的Intent的,如下:
@Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; }
在上面源码中可以知道onStartCommand调用了onStart,我们再看看IntentService中onStart方法的实现如下所示:
@Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }
上面的源码可以看出,在onStart方法之中,IntentService仅仅是通过mServiceHandler发送了一个消息,而mServiceHandler录属于HandlerThread的,所以这个消息会在HandlerThread这个线程之中被处理。
mServiceHandler收到消息之后,会将Intent对象传递给onHandleIntent方法去处理。
需要特别注意:
这里的Intent对象的内容 和 外界的StartService(intent)中的intent的内容是完全一致的,通过这个Intent对象即可解析出外界启动IntentService时候所传递的参数,通过这些参数就可以区分具体的后台任务,这样在onHandleIntent方法中就可以对不同的后台任务做处理了。
当onHandleIntent方法执行结束之后,IntentService会通过stopSelf(int startId)方法尝试停止服务。这里之所以采用stopSelf(int startId)而不是stopSelf()来停止服务,那是因为stopSelf()会立刻停止服务,而这个时候可能还有其他消息未处理,stopSelf(int startId)则会等待所有的消息都处理完毕之后才终止服务。一般来说,stopSelf(int startId)在尝试停止服务之前会判断最近启动服务的次数是否和startId相等,如果相等就立刻停止服务,不相等则不停止服务。
IntentService的onHandleIntent方法是一个抽象方法,它需要在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。如果目前只存在一个后台任务,那么onHandleIntent方法执行完整个任务之后,stopSelf(int startId)就会直接停止服务;如果目前存在多个后台任务,那么当onHandleIntent方法执行完最后一个任务时候,stopSelf(int startId)才会直接停止服务。
另外,由于每执行一个后台任务就必须启动一次IntentService,而IntentService内部则可以通过消息的方式向HandlerThread请求执行任务,Handler中的Looper是顺序处理消息的,这就意味着IntentService也是顺序执行后台任务的,当有多个后台任务同时存在,这些后台任务会按照外界发起的顺序排队执行。
总而言之,流程如下:
2. IntentService使用示例:
(1)新建Android工程如下:
(2)首先我们来到主Activity,如下:
package com.himi.intentservicedemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; public class MainActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * 可以启动多次,每启动一次,就会新建一个work thread * 但IintentService的实例始终只有一个 */ // Operation 1 Intent startServiceIntent = new Intent(this,MyIntentService.class); Bundle bundle = new Bundle(); bundle.putString("param", "oper1"); startServiceIntent.putExtras(bundle); startService(startServiceIntent); // Operation 2 Intent startServiceIntent2 = new Intent(this,MyIntentService.class); Bundle bundle2 = new Bundle(); bundle2.putString("param", "oper2"); startServiceIntent2.putExtras(bundle2); startService(startServiceIntent2); } }
(3)其次这里在MainActivity中开启IntentService服务,但是它是个抽象类,需要一个IntentService具体实现子类,这里为MyIntentService,如下:
package com.himi.intentservicedemo; import android.app.IntentService; import android.content.Intent; import android.os.IBinder; public class MyIntentService extends IntentService { public MyIntentService() { // 必须实现父类的构造方法 super("IntentServiceDemo"); } @Override public IBinder onBind(Intent intent) { System.out.println("onBind"); return super.onBind(intent); } @Override public void onCreate() { System.out.println("onCreate"); super.onCreate(); } @Override public void onStart(Intent intent, int startId) { System.out.println("onStart"); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void setIntentRedelivery(boolean enabled) { super.setIntentRedelivery(enabled); System.out.println("setIntentRedelivery"); } @Override protected void onHandleIntent(Intent intent) { //这个方法在子线程中执行,因此需要用到handler跟主线程进行通信 // Iintent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务 String action = intent.getExtras().getString("param"); if (action.equals("oper1")) { System.out.println("Operation1"); } else if (action.equals("oper2")) { System.out.println("Operation2"); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void onDestroy() { System.out.println("onDestroy"); super.onDestroy(); } }
(4)最后很重要一步,别忘了配置IntentService,因为它继承于Service,所以,它还是一个Service,一定要配置,否则是不起作用的,在清单文件中配置一下IntentService,如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.himi.intentservicedemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="MyIntentService"> </service> </application> </manifest>
(5)部署程序到手机上,观察Logcat如下:
从结果可以看到,onCreate方法只执行了一次,而onStartCommand和onStart方法执行了两次,开启了两个Work Thread,这就证实了之前所说的,启动多次,但IntentService的实例只有一个,这跟传统的Service是一样的。Operation1也是先于Operation2打印,并且我让两个操作间停顿了2s,最后是onDestroy销毁了IntentService。
这就是IntentService,一个方便我们处理业务流程的类,它是一个Service,但是比Service更智能。