前言
上篇文章复习总结了Android的启动模式,现在开始复习Service相关的知识,Service是一种可以在后台执行长时间运行操作而没有用户界面的应用组件。首先从Service的生命周期开始
一、Service的生命周期
-
onCreate()
当实例创建时调用,一个实例只会被调用一次该方法 -
onBind(Intent)
当通过bindService启动服务时,会调用该方法,该方法会返回一个IBinder实例 -
onStartCommand()
当通过startService启动服务时,会调用该方法,不停的调用startService会重复调用该方法 -
onUnbind(Intent)
当所有的连接都断开后会回调该方法,默认该方法返回false,如果返回true,再有新的连接会回调onRebind()
-
onRebind(Intent)
当onUnbind返回true后如果服务还在运行,这时又有新的连接会回调该方法 -
onDestory()
当Service将要被销毁时调用,用于资源的回收
二、Service的启动方式
1. startService(Intent)
单独通过该方式启动服务,当目标服务还没运行会调用服务的onCreate
、onStartCommand
,如果已经运行了就只会调用onStartCommand
,当服务开启后就与启动方没有任何关系了,只有当自己调用stopSelf
,或者外界调用stopService
才会停止,并且无论启动多少次都只需要停止一次就行了
- 例一 Activity A连续调用
startService
10次启动Service S,则最后S会调用1次onCreate
、10次onStartCommand
,并且当A被退出后S会继续运行
2. BindService()
单独通过该方式启动服务拥有以下特性
- 当目标服务还没运行会调用服务的
onCreate
、onBind
,如果已经运行了就只会调用onBind
,重复bindService
不会重复调用onBind
。 - 我们可以在
onServiceConnected
获取到一个IBinder对象(注意Service的onBind
方法返回的必须不是null,不然不会调用该方法),如果Service与启动的Activity同属于一个进程那么返回的就是Service的onBind
返回的那个对象,不然返回的是一个BinderProxy对象,如果是相同进程我们可以直接将其强转然后调用其方法。不同进程则需要通过AIDL生成的类将其上转型成一个接口,然后才能调用。关于AIDL知识以后单独总结 - 当通过该方式启动它的Activity退出时,如果没有手动调用
unBindService
,系统会给我们调用该方法并打印出一段错误,当Service的所有的连接都断开了以后会调用Service的onUnbind
,然后调用onDestroy
3. 结合调用bindService和startService
- 先调用
startService
然后调用bindService
,会调用Service的onCreate
、onStartCommand
、onBind
- 先调用
bindService
然后调用startService
, 会调用Service的onCreate
、onBind
、onStartCommand
当Service的所有连接都断了并且调用了stopService
,这种混合启动的Service才会被销毁
三、IntentService
由于Service中的各个回调方法都是运行在主线程的所以没法做耗时任务,不然会导致ANR,因此IntentService就因运而生了
IntentService是Service的子类,客户端通过调用context.startService(Intent)发送请求,它根据需要启动,在工作线程中处理每个Intent,执行完所有任务后关闭自己
1. IntentService.onCreate
// IntentService
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); // 1
thread.start(); // 2
mServiceLooper = thread.getLooper(); // 3
mServiceHandler = new ServiceHandler(mServiceLooper); // 4
}
复制代码
- 创建了一个HandlerThread实例
- 启动了该线程
- 获取HandlerThread在run方法中创建的Looper对象
- 使用该Looper对象创建了ServiceHandler对象
里面用到了HandlerThread,那么就先来看看HandlerThread其继承了Thread,其run方法如下所示
// HandlerThread
public void run() {
mTid = Process.myTid(); //1
Looper.prepare(); //2
synchronized (this) {
mLooper = Looper.myLooper(); //3
notifyAll(); //4
}
Process.setThreadPriority(mPriority); //5
onLooperPrepared();//6
Looper.loop();//7
mTid = -1;
}
复制代码
- 获取到了当前线程的tid将其赋值给mTid
- 调用
Looper.myLooper
为当前线程创建了一个Looper对象(关于Looper会在Handler的文章中总结) - 获取当前线程的Looper对象并将其赋值给mLooper
- 唤醒
getLooper
方法中调用wait而阻塞的线程 - 设置线程优先级
- 死循环不停的从消息队列中取数据
再来看看其getLooper
方法
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
复制代码
- 如果线程已经死了,那么返回null
- 线程活着但是mLooper还等于null,就调用wait()将锁让给运行在子线程的run方法,让其创建Looper。
- 线程活着并且mLooper不等于null,那么就返回这个子线程的Looper对象
思考:这里有个问题,为什么getLooper
里面需要使用while循环?照道理当调用getLooper
的线程被唤醒的时候mLooper = Looper.myLooper();
已经被执行了,而synchronized又能够提供可见性,强制调用getLooper
的线程去主存里面读取mLooper值。
2. IntentService.onStart
// IntentService
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
复制代码
这里将接收到intent和startId组装成消息发送给ServiceHandler,来看看ServiceHandler的handleMessage
方法
// ServiceHandler
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
复制代码
注意由于该Handler创建时传入的Looper是在子线程创建的,所以handleMessage
方法是运行在子线程的,而onHandleIntent
是个抽象方法由子类实现当其调用完后会调用stopSelf(startId)
该方法会判断给定的startId是不是最近一次启动的startId,如果不是那么什么都不做,如果是那么就会关闭服务
3. IntentService.onDestroy
public void onDestroy() {
mServiceLooper.quit();
}
复制代码
调用了Looper.quit,那么HandlerThread的run方法退出Looper.loop阻塞继续向下执行mTid = -1;
然后HandlerThread线程就执行完毕了
4. 总结
IntentService的原理其实就是在子线程创建了一个Looper,然后根据该Looper创建一个Handler,当每次调用context.startService
的时候调用handler.sendMessage
在子线程中完成工作,完成以后看看有没有新任务到来,如果没有就把自己关闭,否则就继续执行下一个任务