Android 源码阅读 | IntentService

302 阅读6分钟

Android 源码阅读.png

相关推荐:

Thanks

详细剖析IntentService的运作机理

类注释

IntentService is a base class for {@link Service}s that handle asynchronous
requests (expressed as {@link Intent}s) on demand.  Clients send requests
through {@link android.content.Context#startService(Intent)} calls; the
service is started as needed, handles each Intent in turn using a worker
thread, and stops itself when it runs out of work.

IntentService 继承 Service ,客户端通过 android.content.Context#startService(Intent) 发送Intent给Service,Service便开始工作,他使用工作线程处理每一个Intent,并且自动结束生命周期在完成所有的工作后。

 * <p>This "work queue processor" pattern is commonly used to offload tasks
 * from an application's main thread.  The IntentService class exists to
 * simplify this pattern and take care of the mechanics.  To use it, extend
 * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 * will receive the Intents, launch a worker thread, and stop the service as
 * appropriate.

工作线程采用队列的机制,队列通常接受来自于主线程的任务。IntentService 简化了这种处理机制,为了使用它,你需要实现 onHandleIntent(Intent),当他接收到意图的时候,会启动一个工作线程去处理意图,并停止服务当他完成的时候。

 * <p>All requests are handled on a single worker thread -- they may take as
* long as necessary (and will not block the application's main loop), but
* only one request will be processed at a time.

所有的请求都在同一个工作线程去处理,也许会需要比较长的时间(但不会阻塞UI线程),但同一个时刻只会有同一个的请求在处理。

初始化HandlerThread & Handler

public void onCreate() {
   super.onCreate();
   HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
   thread.start();
   mServiceLooper = thread.getLooper();
   mServiceHandler = new ServiceHandler(mServiceLooper);
}

在onCreate中初始化HandlerThread,其里面有已经集成好了Loop,所以,IntentService的核心是基于子线程的一个Handler消息处理机制,所以看到有一个ServiceHandler的自定义Handler处理类:

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);
    }
}

在上面看到,在handlerMessage,回调:onHandlerIntent,这个方法需要我们外部去实现,当然,此方法明显是在工作线程中回调,而且回调完这个方法后,自动调用了stopSelf来停止服务。

    protected abstract void onHandleIntent(@Nullable Intent intent);

setIntentRedelivery

设置意图是否重新发送,描述在service的onStartCommand调用后,被系统杀死,是否重新发送未完成的意图。

public void setIntentRedelivery(boolean enabled) {
    mRedelivery = enabled;
}
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

设置true返回START_REDELIVER_INTENT:表示在系统杀死后,会被重启启动,送入未完成发送的Intent,如果都完成了,则送入最近一个Intent重启服务。设置false返回START_NOT_STICKY,则被杀死后,不会自动重启。

当然,onStartCommand在原生系统上是生效的,但是在国产ROM,可能都失效了吧,毕竟这有点流氓。

一个疑问

ServiceHandlerhandlerMessage方法内部,消息处理完成后就stopSelf,那么在处理前一个消息时,调用的stopSelf()不是把service结束了吗?那它还怎么保证继续处理后续消息呢?

我们通过代码重现一下上面的疑问,假如有这样的一个Service: TestIntentService

public class TestIntentService extends IntentService {

    private final static String TAG = "TestIntentService";
    public final static String Action_Sleep = "Action_Sleep";
    public final static String Action_Soon = "Action_Soon";

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        LogUtils.i(TAG,"onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        LogUtils.i(TAG, intent==null ? "null intent" : "action:" + intent.getAction());
        if (intent!=null && intent.getAction()!=null) {
            switch (intent.getAction()) {
                case Action_Sleep:
                    try {
                        Thread.sleep(5*1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    break;
                case Action_Soon:
                    break;
            }
        }
    }

    @Override
    public void onDestroy() {
        LogUtils.i(TAG,"onDestroy");
        super.onDestroy();
    }
}

定义了两个Action,一个模拟阻塞。

findViewById(R.id.btn_1).setOnClickListener(v -> {
    Intent intent = new Intent(this, TestIntentService.class);
    intent.setAction(TestIntentService.Action_Sleep);
    startService(intent);
});
findViewById(R.id.btn_2).setOnClickListener(v -> {
    Intent intent = new Intent(this, TestIntentService.class);
    intent.setAction(TestIntentService.Action_Soon);
    startService(intent);
});

然后,点击完btn_1后立即点击btn_2,日志如下:

15:09:23.985 8941-8941/com.chestnut.ui I/TestIntentService: onStartCommand
15:09:23.987 8941-8959/com.chestnut.ui I/TestIntentService: action:Action_Sleep
15:09:24.848 8941-8941/com.chestnut.ui I/TestIntentService: onStartCommand
15:09:28.989 8941-8959/com.chestnut.ui I/TestIntentService: action, done
15:09:28.990 8941-8959/com.chestnut.ui I/TestIntentService: action:Action_Soon
15:09:28.990 8941-8959/com.chestnut.ui I/TestIntentService: action, done
15:09:28.991 8941-8941/com.chestnut.ui I/TestIntentService: onDestroy

从上面日志看到,阻塞的时候,收到一个第二个Intent请求,此时不会立即处理,而是等之前的Intent请求处理完成,前一个intent处理完成,调用stopself,但并不会立即结束service,是在所有的intent处理完成后,才结束service,可是为啥呢?我们从源码中分析一下:

先看stopSelf()

stopSelf()位于Service.java

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

再找到:mActivityManager.stopServiceToken

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

mServices是mServices = new ActiveServices(this)mServices.stopServiceTokenLocked 如下:

boolean stopServiceTokenLocked(ComponentName className, IBinder token,
            int startId) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "stopServiceToken: " + className
                + " " + token + " startId=" + startId);
        //找出对应的Service记录器
        ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
        if (r != null) {
            if (startId >= 0) {
                // Asked to only stop if done with all work.  Note that
                // to avoid leaks, we will take this as dropping all
                // start items up to and including this one.
                ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
                if (si != null) {
                    while (r.deliveredStarts.size() > 0) {
                        ServiceRecord.StartItem cur = r.deliveredStarts.remove(0);
                        cur.removeUriPermissionsLocked();
                        if (cur == si) {
                            break;
                        }
                    }
                }
                //在这里,如果说,当前要结束的startId不等于最后一个ID,说明,
                //有其他的intent进来想要启动service,这种情况下,就会直接返回
                //返回后,也就不停止service。
                if (r.getLastStartId() != startId) {
                    return false;
                }
                if (r.deliveredStarts.size() > 0) {
                    Slog.w(TAG, "stopServiceToken startId " + startId
                            + " is last, but have " + r.deliveredStarts.size()
                            + " remaining args");
                }
            }
            synchronized (r.stats.getBatteryStats()) {
                r.stats.stopRunningLocked();
            }
            r.startRequested = false;
            if (r.tracker != null) {
                r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
                        SystemClock.uptimeMillis());
            }
            r.callStart = false;
            final long origId = Binder.clearCallingIdentity();
            bringDownServiceIfNeededLocked(r, false, false);
            Binder.restoreCallingIdentity(origId);
            return true;
        }
        return false;
    }

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

ServiceRecord是记录着正在运行服务的一些信息。而,ServiceRecord.StartItem 是每调用一次startService方法,就会生成一个Item对象:

ActivityManagerService.java

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

ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, int userId) { 
    . . . . . . 
    //获取到对应的service记录器
    ServiceRecord r = res.record;
     . . . . . . 
    r.startRequested = true; 
    r.delayedStop = false; 
    //添加 startItem
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants)); 
    . . . . . . . . . . . . 
    return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}

由上可见,在每一次startService的时候,都会新建一个startItem,并添加到ServiceRecord中记录起来,其中的一个makeNextStartId就是我们service回调方法中的startId,一个ID号。


码字不易,方便的话素质三连,或者关注我的公众号 技术酱,专注 Android 技术,不定时推送新鲜文章,如果你有好的文章想和大家分享,欢迎关注投稿!

技术酱