Android开发之IntentService
2020-08-27
1、什么是IntentService
android.app.IntentService 的本质就是一个 android.app.Service。
它需要在 AndroidManifest.xml 中注册 <service /> 节点,并可以 startService() 的形式启动。它是Android官方引入的用于执行“耗时任务”的服务抽象类。
传统的 Service 里的代码都是运行在主线程里的,而对于服务这种天生就被设计成更适合“在后台默默工作”的机制来说,我们又常常需要它执行一些耗时型的操作,主线程是不建议做耗时操作的。因此,我们常常会在服务中创建子线程来做耗时操作。而IntentService就是Android官方提供的在普通Service里搭建好了用于执行耗时任务的子线程的特殊Service。
2、IntentService的原理
IntentService的本质就是一个普通Service里面加装了一套运行在子线程的Handler机制。
我们还是直接去看它的原码实现吧,反正也很简单。
IntentService是一个抽象类,它不多的几个核心成员如下所示:
public abstract class IntentService extends Service { private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; protected abstract void onHandleIntent(@Nullable Intent intent); }
一个Looper, 一个Handler以及一个用于处理具体事物的抽象方法。Looper和Handler是由IntentService负责并管理的,我们仅需要关心那个抽象方法即可。
下面来看看Looper与Handler的初始化流程。
IntentService 的 onCreate() 如下图所示:
首先它创建了一个 HandlerThread 对象,这个 HandlerThread 也是Android官方提供的,它其实就是在一个子线程内部自己创建并管理了一个 Looper,HandlerThread的核心代码如下图所示:
根据上图所示代码,HandlerThread子线程在运行以后会直接卡在 Looper.loop() 方法处直至程序主动调用那个Looper的退出方法。它这么做其实就为了维护 mLooper 的对象不被GC回收掉。
我们接着看IntentService的onCreate()实现。
HandlerThread在子线程运行起来以后就去拿它刚创建出来的 mLooper 对象,然后再以这个 mLooper 对象构建一个 ServiceHandler对象。ServiceHandler是IntentService的内部类,它的实现也很简单,如下图所示:
就是一个普通的Handler,只不过它的Looper是一个由子线程创建并维护的罢了。并在接收到Message消息后传递给前面提到的那个抽象方法去处理。在消息处理完成以后立即主动结束IntentService服务。不过这里虽然在每次执行完一个请求后就立即调用停止服务的方法,但是它仍然会等到所有请求都执行完成后才会真正去 onDestroy()。
这里再额外强调一点:抽象方法onHandleIntent()是执行在子线程中的。
整个 IntentService 的原理就是这么简单,正如前面所说,它其实就是普通的Service加装了一套在子线程中执行请求的机制而已。
3、IntentService的通俗用法
IntentService不推荐使用“bindService()”的方式启动,因此,它的启动方法就只能是"startService()"了:
Intent it = new Intent(); it.setClass(MainActivity.this, MyIntentService.class); startService(it);
简单到窒息。
4、IntentServicve的另类用法:全局显示提示框
这一小节的内容适用于从事嵌入式产品系统开发的同学。
当我们在做系统开发的时候,常常会遇到需要弹出信息提示框的需求。如果只是普通的APK要弹倒也还好解决,但仍然有不少场景是framework层甚至是更底层service需要弹出提示消息的,这些就会稍微麻烦一点。
再退一步来说,即使能解决各种场景下的弹窗问题,从软件设计角度来说,或者从系统设计角度来说这种零散而重复的代码风格也是很糟糕的设计。
一个系统集成厂商往往需要一套更统一更抽象的解决办法。
笔者很喜欢的一种做法就是创建一个APK,专用于弹出消息提示框。纵观Android四大组件,最合适的莫过于Service了。
于是,在Service的onStartCommand()中弹出这个提示框就显得既简单又可靠了。
但这又会带来另一个问题:Service也是有生命周期的,虽然其生命周期结束以后不见得会立即回收相关内存资源,前面弹出的提示框仍能继续保持显示,但这总归是一个风险点。当系统的内存较为紧张时就有可能出现这个提示框“意外消失”的情况。这很不友好!
怎么办?
既然问题是因为服务的生命周期结束了导致相关资源被GC回收而引发的,那我们就让这个服务在提示框被正常关闭之前不要跑完生命周期就好了。
方案有了,如何实现呢?
答案就是IntentService。
前面我们知道IntentService是可以执行耗时操作的,且在那个运行在子线程的抽象方法执行完毕之前,服务是不会跑完它的生命周期的,那么我们完全可以在 onStartCommand() 中弹出对话框,然后在抽象方法中直接跑个死循环,监听这个提示框的显示状态,当提示框正常关闭后再退出这个抽象方法中的死循环,让IntentService跑完它的生命周期不就行了?
这么做确实可以,且与前面仅在Service中弹提示框的方式相比,它的 /proc/<pid>/oom_adj 的值要优秀上很多。这个值含义可以参考博客: https://www.jianshu.com/p/8897b7e47466
具体的代码这里就不贴了,原理这么简单实现起来同样也非常简单。