Android IntentService

1.IntentService
IntentService是Service的子类,拥有Service的全部特点,而且增加了额外的功能。

首先看一下Service的问题:
①Service默认运行在主线程中,不能执行耗时操作,否则会出现ANR(大概20s)。
②如果要在Service中执行耗时操作,就需要在Service中创建子线程。但是这样有一个问题,就是不知道什么时候这个线程会执行完,所以就会导致停止Service事件的未知性。

IntentService扩展了Service的功能,解决了以上问题:
①IntentService会创建独立的子线程来处理耗时操作,无需关注多线程问题。
②所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()。
③为Service的onBind()提供默认实现,返回null。
④为Service的onStartCommand()提供默认实现,将请求Intent添加到消息队列中。

所以,IntentService是实现了多线程处理异步请求的一个特殊Service类。它在onHandleIntent()方法中进行耗时操作,如果有多个耗时任务,会按顺序一个一个的执行。
注意:因为IntentService也是Service,所以需要在配置文件里注册< service />节点,并以startService()的形式启动。

IntentService主要使用了HandlerThread和Handler。IntentService的特点:
①本质是一种特殊的Service,它是Android官方提供的在普通Service里搭建好了用于执行耗时任务的子线程的特殊Service,它继承自Service并且本身是一个抽象类。即IntentService的本质就是在一个普通Service里面加装了一套运行在子线程的Handler机制。
②可以用于在后台执行耗时的异步任务,当任务完成后会自动停止。
③拥有较高的优先级,不易被系统杀死(继承自Service的缘故),比较适合执行一些高优先级的异步任务。
④内部通过HandlerThread和Handler实现异步操作。
⑤使用IntentService时只需实现onHandleIntent和构造方法,onHandleIntent为异步方法,可以执行耗时操作。
注意:IntentService内部使用的是HandlerThread,所以有HandlerThread的局限性。HandlerThread本质是一个线程,在线程内部代码是串行处理的,由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,就会导致后续的任务都被延迟处理。

2.IntentService的使用
public class MyIntentService extends IntentService {
public static final String DOWNLOAD_URL = “download_url”;
public static final String INDEX_FLAG = “index_flag”;
public static UpdateUI updateUI;

public interface UpdateUI{
void updateUI(Message message);
}

public static void setUpdateUI(UpdateUI updateUIInterface){
updateUI=updateUIInterface;
}

public MyIntentService(){
super(“MyIntentService”);
}

//实现异步任务的方法,参数为Activity传递过来的Intent,数据封装在intent中
@Override
protected void onHandleIntent(Intent intent){
//在子线程中进行网络请求
Bitmap bitmap =downloadUrlBitmap( intent.getStringExtra(DOWNLOAD_URL));
Message msg1 = Message.obtain();
msg1.what = intent.getIntExtra( INDEX_FLAG, 0);
msg1.obj =bitmap;
//通知主线程去更新UI
if(updateUI!=null){
updateUI.updateUI(msg1);
}
LogUtils.e(“onHandleIntent”);
}

@Override
public void onCreate() {
LogUtils.e(“onCreate”);
super.onCreate();
}

@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
LogUtils.e(“onStart”);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.e(“onStartCommand”);
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
LogUtils.e(“onDestroy”);
super.onDestroy();
}

@Override
public IBinder onBind(Intent intent) {
LogUtils.e(“onBind”);
return super.onBind(intent);
}

private Bitmap downloadUrlBitmap(String urlString) {
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
Bitmap bitmap=null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream( urlConnection.getInputStream(), 8 * 1024);
bitmap= BitmapFactory.decodeStream(in);
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
}
继承IntentService有两个方法是必须实现的,一个是构造方法,必须传递一个线程名称的字符串,另外一个就是进行异步处理的方法onHandleIntent(Intent intent),其参数intent携带了从activity传递过来的数据。
这个demo主要利用onHandleIntent实现异步下载图片,然后通过回调方法把下载完的bitmap放在message中回调给Activity(当然也可以使用广播完成),最后通过Handler去更新UI。

再来看看Acitvity的代码:
public class IntentServiceActivity extends Activity implements MyIntentService.UpdateUI {
private String url[] = {
“https://img-blog.csdn.net/20160903083245762”,
“https://img-blog.csdn.net/20160903083252184”,
“https://img-blog.csdn.net/20160903083257871”,
};

private static ImageView imageView;
private static final Handler mUIHandler = new Handler() {
@Override
public void handleMessage(Message msg){
imageView.setImageBitmap((Bitmap) msg.obj);
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Intent intent = new Intent( this, MyIntentService.class);
for (int i=0;i<7;i++) { //启动多个任务
intent.putExtra( MyIntentService.DOWNLOAD_URL,url[i]);
intent.putExtra( MyIntentService.INDEX_FLAG, i);
startService(intent); //虽然在for循环里startService,但是并不会开始多个服务,服务只有一个实例,而且只有第一次startService时会调用IntentService的onCreate方法(在该方法里创建HandlerThread和Handler对象),之后每次startService都只是调用IntentService的onStartCommand()方法(在该方法里调用onStart方法,在onStart方法里把intent封装到Message里,然后调用Handler对象的sendMessage方法往消息队列里发送消息)
}
MyIntentService.setUpdateUI(this);
}
//必须通过Handler去更新,该方法为异步方法,不可更新UI(updateUI()是回调方法,真正调用的地方在IntentService的onHandlerIntent()方法里,onHandlerIntent()是异步方法,所以updateUI也是异步方法,不能操作UI)
@Override
public void updateUI(Message message) {
mUIHandler.sendMessageDelayed( message,message.what * 1000);
}
}
通过for循环多次启动IntentService,然后去下载图片,注意即使多次启动IntentService,但IntentService的实例只有一个,这跟传统的Service是一样的,最终IntentService会去调用onHandleIntent执行异步任务。
这里可能还会担心for循环去启动任务,而实例又只有一个,那么任务会不会被覆盖掉呢?其实是不会的,因为IntentService真正执行异步任务的是HandlerThread+Handler,每次启动都会把下载图片的任务添加到依附的消息队列中,最后由HandlerThread+Handler去执行。
运行一下代码:
每间隔一秒去更新图片,看一组log:
在这里插入图片描述
从Log可以看出onCreate只启动了一次,而onStartCommand和onStart多次启动,这就证实了之前所说的,启动多次但IntentService的实例只有一个,最后任务都执行完成后IntentService自动销毁。

注意:
①用户通过调用Context.StartService(Intent)发送请求,服务根据启动请求,使用工作线程依次处理每个Intent任务请求,并在处理完所有任务请求后自身停止服务。
②使用时,扩展 IntentService ,即实现它的子类并具体实现onHandleIntent(Intent)方法。IntentService接收Intent,启动工作线程,并在适当时机停止服务。
③所有的请求都在同一个工作线程上处理,一次处理一个请求,所以处理完所有的请求可能会花费很长的时间,但由于IntentService是另外创建的线程来工作,所以保证不会阻止App的主线程,防止 App出现 ANR。

3.IntentService源码
IntentService的源码比较简单,只有100多行。
在开启服务的时候有两种方式,分别为startService和bindService。startService方式启动时生命周期方法执行顺序是 onCreate()、onStartCommand() 、onStart()、onDestroy(),下面按照生命周期执行顺序查看IntentService的源码。

先来看看IntentService的onCreate方法:
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread( “IntentService[” + mName + “]”); // 首先创建HandlerThread对象,HandlerThread是一个子线程,在该子线程里创建了Looper和MessageQueue对象并调用了Looper.loop()方法
thread.start();

mServiceLooper = thread.getLooper(); // 将HandlerThread对象的Looper赋值给IntentService的mServiceLooper成员变量
mServiceHandler = new ServiceHandler( mServiceLooper); // 利用HandlerThread对象中的Looper创建一个handler并赋值给IntentService的mServiceHandler成员变量
}
当第一次启动IntentService时,它的onCreate方法将会被调用,其内部会创建一个HandlerThread并启动它,接着创建一个ServiceHandler并传入HandlerThread的Looper对象,这样ServiceHandler就变成可以处理异步线程的执行类了(因为Looper对象与HandlerThread绑定,而HandlerThread是一个异步线程,把HandlerThread持有的Looper对象传递给Handler后,ServiceHandler内部就持有异步线程的Looper,自然就可以执行异步任务了)。

那么IntentService是怎么启动异步任务的呢?其实IntentService启动后会调用onStartCommand方法,而onStartCommand方法又会调用onStart方法,看看它们的源码:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId); //调用onStart方法
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMess age(); //新建一个Message,并将intent包装到Message的obj中
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg); //通过sendMessage()将消息插入消息队列中,最终消息会被looper取出并分发,然后调用handleMessage()处理消息
}
在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) {
//ServiceHandler的handleMessage()将接收到的消息交给onHandlerIntent处理,onHandlerIntent就是需要实现的方法,它获取到Intent并处理
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1); //执行完,结束服务
}
}
这里也说明onHandleIntent确实是一个异步处理方法(ServiceHandler本身就是一个异步处理的handler类),在onHandleIntent方法执行结束后,IntentService会通过stopSelf(int startId)方法来尝试停止服务。
这里采用stopSelf(int startId)而不是stopSelf()来停止服务,是因为stopSelf()会立即停止服务,而stopSelf(int startId)会等待所有消息都处理完后才终止服务。
这里虽然每次执行完一个请求后就立即调用停止服务的方法,但是它仍然会等到所有请求都执行完成后才会真正去onDestory()。

对于stopSelf(int startId)需要解释一下:
IntentService处理完请求后,调用stopSelf(int startId)尝试停止Service,也就是说调用stopSelf(int starId)方法Service不一定会立刻停止。
startId是每次调用startService()方法时系统自动生成的。stopSelf(int startId)方法会在其参数startId与最后一次启动该service时生成的ID相等时才会执行停止服务。
下面需要结合另外两个方法来解释。
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
//1
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
//2
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
看到代码1,刚刚stopSelf(startId)方法传入的startId就是来自这里。stopSelf方法会判断传入的startId和onStartCommand的startId是否相同。相同就停止服务,不相同就暂时不停止它。现在把onStartCommand的startId记为A,把stopSelf传入的startId记为B。A不等于B说明在任务1完成前,任务2请求就来了,如果完成了任务1立即停止服务,任务2的请求就不能得到处理了。同理,A等于B说明任务1完成时还没有新的任务请求发过来,这时立即停止服务并不会有什么不妥,新的任务请求发来了再重新启动服务就可以了,如此一来IntentService就实现了自动停止的功能。

接下来再看看服务停止时做了什么。
@Override
public void onDestroy() {
mServiceLooper.quit();
}
调用了Looper类的退出方法:
Looper.java
public void quit() {
mQueue.quit(false);
}
销毁服务的操作很简单,让Looper停下来就可以了,这里Looper是非安全退出,也就是说,队列里的任务会被取消掉。调用stopSelf(-1)不需要等所有的任务处理完,可以直接停止服务。

IntentService还有一个方法,它可以让Intent实现重传:
public void setIntentRedelivery(boolean enabled){
mRedelivery = enabled;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
如果setIntentRedelivery方法传入true,那么在进程被杀死的时候Intent会被保存下来,等进程重启了再重新发送这个Intent。但是对于多个Intent只有那个最新的Intent能够重传。其实这是通过onStartCommand方法的返回值来实现的。

最后看看onHandleIntent方法的声明:
protected abstract void onHandleIntent(Intent intent);
可见IntentService的onHandleIntent方法是一个抽象方法,所以在创建IntentService时必须实现该方法,通过上面的分析可知,onHandleIntent方法也是一个异步方法。

这里要注意的是如果后台任务只有一个的话,onHandleIntent执行完,服务就会销毁,但如果后台任务有多个的话,onHandleIntent执行完最后一个任务时,服务才销毁。

最后要知道每次执行一个后台任务就必须启动一次IntentService,而IntentService内部则是通过消息的方式发送给HandlerThread的,然后由Handler中的Looper来处理消息,而Looper是按顺序从消息队列中取任务的,也就是说IntentService的后台任务是顺序执行的,当有多个后台任务同时存在时,这些后台任务会按外部调用的顺序排队执行。
在这里插入图片描述
总结一下整体流程:
开启服务是会执行onCreate()方法,方法中创建好了一个子线程(HandlerThread),(子线程中创建好了一个Looper对象同时创建好了一个MessagqQueue消息队列,然后开启轮询消息队列),IntentService内部创建好的Handler与子线程中的Looper对象绑定。onCreate只有在服务第一次创建的时候才会调用,之后每次调用都只会执行onStartCommand方法,在此方法中构建好了一个Message对象,并且将传递进来的Intent封装在Mesage,一起发送到消息队列中。经过轮询将消息分发到Handler的handleMessage中处理,此时获取到Message中携带的Intent传递给我们实现好的onHandleIntent方法中进行任务的处理,处理完毕自动调用StopSlef来结束服务。在onDestroy方法会把所有消息都给退出。

以上分析了startService方法启动的服务,那bindService方式启动服务的分析如下(绑定服务,建立长期通信),bindService启动会执行 onCreate -> onBind -> onUnBind -> onDestroy方法。
public IBinder onBind(Intent intent) {
//直接返回null了。所以不会创建Mesage对象和发送消息到队列中,也不会回调handleIntent方法
return null;
}
所以bindService方式启动服务,不会进行多线程的操作。

最后贴一下到IntentService的全部源码,大家再次感受一下:
public abstract class IntentService extends Service {
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((Intent)msg.obj);
stopSelf(msg.arg1);
}
}

public IntentService(String name) {
super();
mName = name;
}

public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}

@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread(“IntentService[” + mName + “]”);
thread.start();

mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

@Override
public void onDestroy() {
//将消息队列中的所有消息都移除,包括处理中的和未处理的
mServiceLooper.quit();
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

@WorkerThread
protected abstract void onHandleIntent(Intent intent);
}

我的理解:其实IntentService就是一个service,只不过在里面加了HandlerThread和Handler。使用IntentService时要用startService()而不能用bindService()。在第一次调用startService时,会首先执行IntentService的onCreate方法,在onCreate方法里主要是为IntentService的两个成员变量(nServiceLooper和mServiceHandler)赋值,赋值过程是通过新建一个HandlerThread对象实现的:新建一个HandlerThread,把HandlerThread对象的looper赋值给IntentService的mServiceLooper成员变量;利用HandlerThread对象的looper创建一个Handler对象,并把该Handler对象赋值给IntentService的mServiceHandler成员变量。到此IntentService的onCreate方法完毕。接下来该执行IntentService的onStartCommand方法了。除了第一次调用startService会执行IntentService的onCreate之外,以后每次startService时都不会再执行onCreate方法,而是直接执行onStartCommand。在onStartCommand方法里调用了onStart方法,重点就在onStart方法,在onStart方法里新建一个Message对象,将intent封装到Message对象的obj中,然后调用在onCreate方法中创建好的mServiceHandler成员变量通过sendMessage方法将该Message对象放入消息队列,所以每次startService其实就是往消息队列中放入消息的过程。真正处理消息是在mServiceHandler的handleMessage方法里,因为mServiceHandler是通过异步的Looper对象创建的,所以它的handleMessage方法是在异步线程里执行。在mServiceHandler的handleMessage方法里调用了onHandlerIntent方法,所以onHandlerIntent方法也是在异步线程中。onHandlerIntent方法正是使用IntentService必须要实现的方法,这就是为什么使用IntentService时只需要重新onHandlerIntent方法就可以在里面进行耗时的异步操作了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值