前言

上篇文章复习总结了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)

单独通过该方式启动服务,当目标服务还没运行会调用服务的onCreateonStartCommand,如果已经运行了就只会调用onStartCommand,当服务开启后就与启动方没有任何关系了,只有当自己调用stopSelf,或者外界调用stopService才会停止,并且无论启动多少次都只需要停止一次就行了

  • 例一 Activity A连续调用startService10次启动Service S,则最后S会调用1次onCreate、10次onStartCommand,并且当A被退出后S会继续运行

2. BindService()

单独通过该方式启动服务拥有以下特性

  • 当目标服务还没运行会调用服务的onCreateonBind,如果已经运行了就只会调用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的onCreateonStartCommandonBind
  • 先调用bindService然后调用startService, 会调用Service的onCreateonBindonStartCommand

当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
}
复制代码
  1. 创建了一个HandlerThread实例
  2. 启动了该线程
  3. 获取HandlerThread在run方法中创建的Looper对象
  4. 使用该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;
}
复制代码
  1. 获取到了当前线程的tid将其赋值给mTid
  2. 调用Looper.myLooper为当前线程创建了一个Looper对象(关于Looper会在Handler的文章中总结)
  3. 获取当前线程的Looper对象并将其赋值给mLooper
  4. 唤醒getLooper方法中调用wait而阻塞的线程
  5. 设置线程优先级
  6. 死循环不停的从消息队列中取数据

再来看看其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在子线程中完成工作,完成以后看看有没有新任务到来,如果没有就把自己关闭,否则就继续执行下一个任务