IntentService源码分析
概述
在上一篇的文章中介绍过HandlerThread的源码分析【点这里 】,这篇文章介绍下HandlerThread一个典型应用IntentService。
我们都知道安卓官方是不建议我们在一个Service的主线程里做耗时操作,所以一般都需要去开启子线程去做一些事情,而自己去管理Service的生命周期以及子线程并非是个优雅的做法,所以安卓给我们提供了一个新的派生服务IntentService,接下来我们来看看IntentService的使用以及源码分析。
基本使用
一般使用IntentService步骤如下:
// 1. 创建继承IntentService的计类,IntentService为抽象类。
public class PhotoLoadService extends IntentService {
private static final String TAG = PhotoLoadService.class.getName();
private LoadPhotoEvent mLoadPhotoEvent;
private NetModel mNetModel;
public PhotoLoadService() {
super(TAG);
EventBus.getDefault().register(this);
mLoadPhotoEvent = new LoadPhotoEvent();
mNetModel = NetModel.getNetModel();
}
// 4.生命周期结束,释放资源
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
Log.e(TAG, "onDestroy");
}
// 2. 实现onHandleIntent()方法,该方法调度线程为子线程,可以执行耗时操作
@Override
public void onHandleIntent(Intent intent) {
String photoLoadUrl = null;
String photoId = null;
if (intent != null) {
photoLoadUrl = intent.getStringExtra(PHOTO_LOAD_URL);
photoId = intent.getStringExtra(PHOTO_ID);
}
File file = new File(Environment.getExternalStorageDirectory() + "/WallPager/" + photoId + ".jpg");
if (file.exists()) {
mLoadPhotoEvent.setPhotoId("exist");
EventBus.getDefault().post(mLoadPhotoEvent);//发送事件,图片文件已存在
return;
}
String dir = Environment.getExternalStorageDirectory() + "/WallPager/";
String name = photoId + ".jpg";
String finalPhotoId = photoId;
Log.e(TAG, photoLoadUrl);
// 注意这里回调可以通过binder通信实现,这个例子我使用的是eventbus来实现回调消息的。
mNetModel.getHttpHelper().downloadPicFromNet(photoLoadUrl, new DownLoadCallback(dir, name) {
@Override
protected void beforeProgress() {
super.beforeProgress();
SharePrefUtil.setBoolean(WallPagerApplications.getContext(), Constants.IS_RUNNING, true);
}
@Override
public void progress(long progress, long total) {
//Log.e(TAG, "Progress " + progress + " " + total);
mLoadPhotoEvent.setProgress(progress * 100 / total);
mLoadPhotoEvent.setPhotoId(finalPhotoId);
EventBus.getDefault().postSticky(mLoadPhotoEvent);//发送事件,更新UI
}
@Override
public void onSuccess(File file) {
super.onSuccess(file);
Log.e(TAG, "Success");
mLoadPhotoEvent.setMessage("success");
EventBus.getDefault().post(mLoadPhotoEvent);
SharePrefUtil.setBoolean(WallPagerApplications.getContext(), Constants.IS_RUNNING, false);
// 最后通知图库更新
WallPagerApplications.getContext().sendBroadcast(new Intent(Intent
.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" +
file.getAbsolutePath())));
}
@Override
public void onFail(String error) {
super.onFail(error);
Log.e(TAG, "Error");
SharePrefUtil.setBoolean(WallPagerApplications.getContext(), Constants.IS_RUNNING, false);
mLoadPhotoEvent.setMessage("error");
EventBus.getDefault().post(mLoadPhotoEvent);
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void doNothing(LoadPhotoEvent event) {
}
}
// 3. 绑定服务,实现后台逻辑
Intent intent = new Intent(getContext(), PhotoLoadService.class);
intent.setAction(Constants.SERVICE_ACTION);
intent.putExtra(Constants.PHOTO_LOAD_URL, mResult.getUrls().getRaw());
intent.putExtra(Constants.PHOTO_ID, mResult.getId());
getActivity().startService(intent);
对IntentService使用步骤大概如上述4点,使用比较简单,后续的源码分析时,会简单介绍一些步骤的原理。
源码分析
为了更好的理解IntentService的使用,下面我们来了解下源码。
首先来看下构造方法。
public abstract class IntentService extends Service {
/**
* 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的父类是Service并且是个抽象类,所以我们在实现的时候要继承IntentService。从构造方法,我们看不出什么逻辑,只是简单的设置了个工作线程的名字。
其次既然是Service,我们就来看看其生命周期的调度。
@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();
// 利用HandlerThread与Handler机制实现子线程调度
mServiceHandler = new ServiceHandler(mServiceLooper);
}
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);
}
}
从onCreate()方法可以看到,IntentService内部创建了HandlerThread作为工作线程,然后利用HandlerThread的Looper来创建ServiceHandler,目的是为了处理消息调度,这个原理我们在上一篇介绍HandlerThread已经描述,这里不在赘述。
既然是消息调度,我们来看看它是如何调度的。
/**
* 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(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
/*
* START_REDELIVER_INTENT 正常启动意图
* START_NOT_STICKY 表示进程死掉后,该意图也一并销亡
* */
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
我们都清楚,在使用startService()方式启动Service时会调用onStartCommand()方法,而IntentService中对onStartCommand()方法的描述是让我们不要去重写该方法,因为安卓已经给我设计好了IntentService的功能作用,而在该方法中,我们看到主要调用了onStart()方法,而在onStart()内部通过之前创建Handler向工作线程抛出了一个消息,这个消息里封装了Intent以及开始的Id。
我们在转过来看Handler的handleMessage()方法,首先调用了onHandleIntent()抽象方法,需子类实现,也正是我在前面使用举例时说的可以做耗时操作的方法,到这里IntentService里的HandlerThread与Handler调度介绍差不多了。当然用过IntentService的人都知道,IntentService会在执行完其任务后会自动销毁,其实也不是什么秘密,正是stopSelf()完成这个操作的。最后在onDestroy()方法中,会将HandlerThread中的Looper停止。
就此对于IntentService源码分析就此结束,由于笔者技术有限,如果分析过程中有出现错误,欢迎指出,谢谢。