剖析IntentService的运做机理

剖析IntentService的运做机理java

(本文以Android 5.1为准)android

侯 亮app


1 概述

在讲述Service机制的文章里,咱们曾经稍微提起过IntentService,今天再来详细剖析一下它。提及来,IntentService只是一个处理异步请求的服务基类而已。当人们经过调用startService()启动IntentService时,实质上是向其发送了一个请求。而若是有多个地方同时向同一个IntentService发送请求的话,那么这些请求会被串行化处理。因此,IntentService经常用于执行那种“一次性处理”的工做。异步

IntentService的运做机理相对比较简单,并且从Android2.3(我手头最先的版本)到Android5.1,IntentService的实现代码一直就没怎么变更过,可见其稳定程度。ide

固然,咱们既然说要详细剖析IntentService,就不可能仅仅讲这么点儿东西。咱们能够挖得再深一点儿,看看IntentService究竟是如何实现的,又是如何保证串行处理工做的。如今咱们就开始吧。函数


2 先简单说说IntentService的运做

2.1 onCreate()中启动一个消息泵线程

咱们先看一下IntentService的onCreate()函数:
【frameworks/base/core/java/android/app/IntentService.java】oop

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

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

能够看到,IntentService建立伊始就会启动一个消息泵线程HandlerThread,而后又建立了一个能够向这个消息泵线程传递消息的Handler(即ServiceHandler)。this

ServiceHandler是IntentService的一个内嵌类,其定义以下:spa

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);  // 注意,不是stopService()!
    }
}

因此,上面的代码的逻辑意义就很明确了,那就是构造一个消息泵线程以及一个能够向这个泵传递消息的Handler。然后,当这个消息泵线程最终处理这个消息时,其实就是在回调IntentService的onHandleIntent()函数而已,而且在该回调函数返回后,handleMessage()会进一步调用stopSelf()结束service。注意,这里说“结束service”只是一种简略说法,实际的状况会稍微复杂一点儿,这个咱们后文再细说。线程

2.2 onStartCommand()利用ServiceHandler向消息泵打入特殊消息

在紧随onCreate()函数以后,系统会调用到onStartCommand()函数,这个你们都很熟悉了。对于startService()动做而言,系统只会在service尚不存在的状况下,建立相应的service,并回调其onCreate()函数。之后,只要这个service没有被销毁,就不会重复再调用onCreate()了。不过,每次调用startService()都会致使走到onStartCommand()函数。IntentService的onStartCommand()函数的代码以下:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

看起来,每当用户调用startService()启动IntentService,就会自动向其内部消息泵线程打入一条消息,该消息的.obj域记录着用户传来的intent,这个intent在handleMessage()里会进一步传递给onHandleIntent()。另外,请你们注意那个startId参数,这里咱们先打个伏笔,后文还会细说。

2.3 实现你的onHandleIntent()函数

在IntentService.java文件里,onHandleIntent()只是作了简单的声明:

protected abstract void onHandleIntent(Intent intent);

其具体行为要看咱们写的IntentService派生类怎么定义这个函数啦。

消息泵里的消息队列在先天上就维护了一条串行化的执行链。消息泵线程逐个摘取消息节点,回调每一个节点的onHandleIntent()函数,一切都显得那么天然。如今咱们能够画出以下示意图:

3 再深挖一下

有了以上这些基础知识,如今咱们要再深挖一下了。

在消息队列中,消息的确被顺序排列了,但是在处理前一个消息时,调用的stopSelf()不是把service结束了吗?那它还怎么保证继续处理后续消息呢?可见,前文咱们说的“IntentService还会自动执行stopSelf()关闭本身”的说法并不许确。问题的关键在于,ServiceHandler的handleMessage()里调用的是stopSelf(),而不是stopService()!它们是不同的。

3.1 stopSelf()和stopService()是不同的!

stopSelft()的函数定义以下:
【frameworks/base/core/java/android/app/Service.java】

public final void stopSelf(int startId) {
    if (mActivityManager == null) {
        return;
    }
    try {
        mActivityManager.stopServiceToken(
                new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
}

请你们注意那个startId参数,这个参数是系统经过onStartCommand()传递给service的,也就是前文咱们打伏笔的地方。要了解这个startId参数,咱们得看一下AMS里的相关代码。

AMS中启动service的函数是startService,其代码截选以下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】

public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, int userId) {
    . . . . . .
        . . . . . .
        ComponentName res = mServices.startServiceLocked(caller, service,
                resolvedType, callingPid, callingUid, userId);
        . . . . . .
    . . . . . .
}

【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】

ComponentName startServiceLocked(IApplicationThread caller,
        Intent service, String resolvedType,
        int callingPid, int callingUid, int userId) {
    . . . . . .
    ServiceRecord r = res.record;
    . . . . . .
    r.lastActivity = SystemClock.uptimeMillis();
    r.startRequested = true;
    r.delayedStop = false;
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants));
    . . . . . .
    . . . . . .
    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

也就是说,每当咱们调用startService()启动一个服务时,不但会在其对应的ServiceRecord节点的pendingStarts里插入一个新的StartItem节点,并且会为这个StartItem节点生成一个新的id号,这个id号就是往后的startId啦。

生成id号时,采用的办法是最简单的加1操做,代码以下:
【frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java】

public int makeNextStartId() {
    lastStartId++;
    if (lastStartId < 1) {
        lastStartId = 1;
    }
    return lastStartId;
}

接下来,咱们绘制一张调用关系图:

图中的sendServiceArgsLocked()会将r.pendingStarts列表中的StartItem节点转移到r.deliveredStarts列表中。这主要是由于启动service的动做自己是比较复杂的,有时候甚至会遇到目标service寄身的进程还没有启动的状况,此时就得先启动一个用户进程,然后再进一步启动service。要规划好这些异步的动做,咱们经常须要把信息先记录进一个pending列表里,然后再在合适的时机将信息从pending列表里取出。

上图中最后调用的r.app.thread.scheduleServiceArgs()实际上是向service所在的进程发出SERVICE_ARGS语义,语义中携带着刚取出的StartItem节点的id和intent信息。该语义最终致使执行到目标service的onStartCommand()。这个就和前文介绍onStartCommand()的地方契合起来了。

当onStartCommand()向消息泵线程打入消息时,startId就被记录进message里了。咱们如今举个例子,假设几乎在同时有两个地方都调用startService()来启动同一个IntentService,此时极可能会造成两个StartItem和两个IntentService消息,画出示意图以下:

既然消息队列里的消息已经和AMS里的StartItem对应上了,那么在处理完消息后,调用stopSelf()时,应该也必须考虑到这种匹配关系。因此stopSelf()的参数就是startId。前文咱们已经看到,stopSelf()内部是在调用:

        mActivityManager.stopServiceToken(
                new ComponentName(this, mClassName), mToken, startId);

stopServiceToken()的代码以下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】

@Override
public boolean stopServiceToken(ComponentName className, IBinder token,
        int startId) {
    synchronized(this) {
        return mServices.stopServiceTokenLocked(className, token, startId);
    }
}

【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】

boolean stopServiceTokenLocked(ComponentName className, IBinder token, int startId) {
    . . . . . .
    ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
    if (r != null) {
        if (startId >= 0) {
            ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
            if (si != null) {
                // 用while循环删除位于startId对应的StartItem节点以及其以前的全部节点
                while (r.deliveredStarts.size() > 0) {
                    ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
                    cur.removeUriPermissionsLocked();
                    if (cur == si) {
                        break;
                    }
                }
            }

            if (r.getLastStartId() != startId) {
                return false;    // 若是不是最后一个startItem节点,则直接return false了
            }

            if (r.deliveredStarts.size() > 0) {
                Slog.w(TAG, "stopServiceToken startId " + startId
                        + " is last, but have " + r.deliveredStarts.size()
                        + " remaining args");
            }
        }
        . . . . . .
        // 当最后一个startItem摘掉后,才真正结束service
        bringDownServiceIfNeededLocked(r, false, false);  
        . . . . . .
        return true;
    }
    return false;
}

这段代码的主要意思仍是比较明确的,那就是按序从ServiceRecord的deliveredStarts列表中删除StartItem节点,直到所删除的是startId参数对应的StartItem节点,若是此时还没有抵达ServiceRecord内部记录的最后一个start Id号,则说明这次stopSelf()操做不必进一步结束service,那么直接return false就能够了。只有在所删除的startItem节点的确是最后一个startItem节点时,才会调用bringDownServiceIfNeededLocked()去结束service。这就是为何IntentService的ServiceHandler在处理完消息后,能够放心调用stopSelf()的缘由。

那么,为何要用一个while循环来删除位于startId对应的StartItem节点以前的全部节点呢?你们能够设想一下,若是service进程被异常kill了,那么它里面的消息队列确定也就销毁了。但是在AMS一侧的ServiceRecord里,那些对应的StartItem节点仍是存在的,就好像一个个孤儿同样。此时,若是用户再一次调用startService()启动了这个IntentService,那么系统最好能在处理完这次message后,一并将那些孤儿StartItem销毁。不过这只是我目前的一点儿猜测,实际上可能不大容易遇到这种状况。

3.2 说说setIntentRedelivery()

如今咱们再来看看IntentService里其余一些细节。好比setIntentRedelivery()函数:
【frameworks/base/core/java/android/app/IntentService.java】

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

通常,咱们能够在实现IntentService派生类时,在构造函数里调用这个函数。这里设置的mRedelivery成员会在onStartCommand()函数里影响最终的返回值,从而影响service的“重递送intent”的行为。

咱们再列一次onStartCommand()的代码:

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

当mRedelivery为true,会返回START_REDELIVER_INTENT,这个值的意思是说,若是在onStartCommand()以后的某个时刻,该service对应的进程被kill了,那么系统会自动重启该service,并向onStartCommand()从新传入当初startService()时指定的intent。另外,在极端状况下,可能已经有多个地方startService()了,那么系统在重启service以后,应该会将本身记录的多个intent逐条传递回service,也就是说,有可能会执行屡次onStartCommand()了。而当mRedelivery为false时,会返回START_NOT_STICKY,它表示若是在后续某个时刻,该service对应的进程被kill了,系统是不会自动重启该service的。


4 小结

有关IntentService的知识,咱们就先说这么多,有兴趣的同窗不妨对比代码看看。

相关文章
相关标签/搜索