总结下来就是:
- IntentService是Service类的子类,用来处理异步请求;
- 客户端可以通过startService(Intent)方法传递请求给IntentService;
- IntentService单独开启了一个线程来处理所有的Intent请求所对应的任务,以免事务处理阻塞主线程,而且任务是按先后顺序逐个进行处理的;
- 当IntentService处理完所有的任务后,它会在适当的时候自动结束服务。
下面是一个模拟图片上传的demo。
ImgUploadService.java类:
public class ImgUploadService extends IntentService { public static final String TAG = "ImgUploadService"; private static final String ACTION_UPLOAD_IMG = "com.demo.service.action.UPLOAD_IMAGE"; public static final String EXTRA_IMG_PATH = "com.demo.service.extra.IMG_PATH"; public ImgUploadService(String name) { super(name); Log.d(TAG, "ImgUploadService[" + " ThreadName: " + name + " ]"); } @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent"); if (intent != null) { final String action = intent.getAction(); if (ACTION_UPLOAD_IMG.equals(action)) { final String path = intent.getStringExtra(EXTRA_IMG_PATH); handleUploadImg(path); } } } private void handleUploadImg(String path) { try { Thread.sleep(3000); //模拟上传耗时 Intent intent = new Intent(IntentServiceActivity.UPLOAD_RESULT); intent.putExtra(EXTRA_IMG_PATH, path); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); } catch (InterruptedException e) { e.printStackTrace(); } } public static void startUploadImg(Context context, String path) { Intent intent = new Intent(context, ImgUploadService.class); intent.setAction(ACTION_UPLOAD_IMG); intent.putExtra(EXTRA_IMG_PATH, path); context.startService(intent); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); } }
IntentServiceActivity.java类:
public class IntentServiceActivity extends Activity { public static final String UPLOAD_RESULT = "com.demo.service.UPLOAD_RESULT"; private LinearLayout mLlContainer; private TextView mBtnUpload; int i = 0; @Override public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); setContentView(R.layout.activity_main_handlerthread); mLlContainer = (LinearLayout) findViewById(R.id.ll_container); mBtnUpload = (TextView) findViewById(R.id.btn_upload); registerReceiver(); mBtnUpload.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addTask(); //模拟上传 } }); } private void registerReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(UPLOAD_RESULT); LocalBroadcastManager.getInstance(this).registerReceiver(mUploadImgReceiver, filter); } public void addTask() { String path = "图片" + i++ + ".png"; ImgUploadService.startUploadImg(this, path); TextView tv = new TextView(this); mLlContainer.addView(tv); tv.setText(path + " ....正在上传中...."); tv.setTag(path); } private void handleResult(String path) { TextView tv = (TextView) mLlContainer.findViewWithTag(path); tv.setText(path + " ----上传成功---- "); } private BroadcastReceiver mUploadImgReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(UPLOAD_RESULT)) { String path = intent.getStringExtra(ImgUploadService.EXTRA_IMG_PATH); handleResult(path); } } }; @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mUploadImgReceiver); } }
activity_main.xml文件:
IntentService本身就是一个Service,它拥有Service的所有生命周期方法,但内部是通过HandlerThread实现异步执行任务的,HandlerThread是一个内部维护了一个消息队列的线程。
既然IntentService有生命周期,那么就从它的构造函数看起:
/** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public IntentService(String name) { super(); mName = name; }
构造方法很简单,用于创建一个IntentService对象,参数namne用于定义工作线程的名字,仅用于调试作用。接下来看下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对象,并传入工作线程的名字 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); // 开启后台工作线程 thread.start(); // 获取后台工作线程的Looper对象 mServiceLooper = thread.getLooper(); // 创建一个ServiceHandler对象,用来处理异步消息。 mServiceHandler = new ServiceHandler(mServiceLooper); }
在onCreate()方法里,首先利用HandlerThread类创建了一个循环的工作线程,接着获取工作线程中的Looper对象并将其作为参数创建了一个叫ServiceHandler的类,该类是IntentService的内部类,该类继承了Handler,我们看它的定义:
public abstract class IntentService extends Service { //volatile关键字保证变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”中读取 private volatile Looper mServiceLooper;// private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { //一收到消息就回调到onHandleIntent()中进行处理 onHandleIntent((Intent)msg.obj); //处理完消息就调用stopSelf()方法,并传入消息的索引值 stopSelf(msg.arg1); } } }
从源码中可以看到,mServiceLooper和mServiceHandler都加了volatile关键字修饰,这是为了保证变量在每次使用的时候都从主内存中读取,而不是从各个线程的“工作内存”中读取,也就是说保证内存的可见性。
接着看onHandleIntent()方法,该方法是在ServiceHandler收到消息后回调的,也即onHandleIntent()方法是在HandlerThread工作线程中执行的,它是一个抽象方法,留给调用者去实现耗时任务的。
/** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * When all requests have been handled, the IntentService stops itself, * so you should not call {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ protected abstract void onHandleIntent(Intent intent);
此外,因为任务是通过ServiceHandler发送给异步线程的消息队列来处理的,而消息队列里的消息是依次取出并执行的,所以从这里可以了解到任务必定是串行执行的。而执行完一个消息后,调用了一个带有startId参数的stopSelf()方法:
** * Old version of {@link #stopSelfResult} that doesn't return a result. * * @see #stopSelfResult */ public final void stopSelf(int startId) { if (mActivityManager == null) { return; } try { mActivityManager.stopServiceToken( new ComponentName(this, mClassName), mToken, startId); } catch (RemoteException ex) { } }
该方法实际上是通过调用ActivityManager的stopServiceToken()方法来停止当前服务的,不过服务不会马上停止,而是等待继续完成剩下的任务后才自动结束,因为方法参数里携带了一个startId,它可以看做是一个请求的唯一标识,只有当所有的请求都结束,我们的Service才自行销毁,而startId是从onStartCommand()里被调用的,我们看下它的源码:
/** * 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(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); }
对于Service,每次调用startServcie方法启动一个服务,都会调用一次onStartCommand()方法,每次也会带一个startId过去,如果执行多个任务,那么就会存在多个startId,然后在onStart()方法中通过mServiceHandler获得一个消息对象msg,然后将startId作为该消息的消息码,将异步任务请求intent作为消息内容封装成一个消息msg发送到mServiceHandler中进行异步处理,最后回调到onHandleIntent()中,子类通过实现onHandleIntent()即可进行异步耗时任务的执行。
这里注意,我们不需要自己去重写onStartCommand()和onStart()方法,但onHandleIntent()必须重写。
当所有的startId都处理完后,IntentService会调用onDestroy()自行销毁,消息队列也会自行退出。