博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(Source)IntentService
阅读量:6822 次
发布时间:2019-06-26

本文共 9241 字,大约阅读时间需要 30 分钟。

Thanks

类注释

IntentService is a base class for {@link Service}s that handle asynchronousrequests (expressed as {@link Intent}s) on demand.  Clients send requeststhrough {@link android.content.Context#startService(Intent)} calls; theservice is started as needed, handles each Intent in turn using a workerthread, and stops itself when it runs out of work.复制代码

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

* 

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),当他接收到意图的时候,会启动一个工作线程去处理意图,并停止服务当他完成的时候。

* 

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: onStartCommand15:09:23.987 8941-8959/com.chestnut.ui I/TestIntentService: action:Action_Sleep15:09:24.848 8941-8941/com.chestnut.ui I/TestIntentService: onStartCommand15:09:28.989 8941-8959/com.chestnut.ui I/TestIntentService: action, done15:09:28.990 8941-8959/com.chestnut.ui I/TestIntentService: action:Action_Soon15:09:28.990 8941-8959/com.chestnut.ui I/TestIntentService: action, done15: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) {    }}复制代码

再找到::

@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号。

转载地址:http://imrzl.baihongyu.com/

你可能感兴趣的文章
selenium webdriver (11) -- 截图
查看>>
sublime插件安装
查看>>
网络配置多会话实验
查看>>
如何挑选适合自己的HTML5视频课程
查看>>
windows提权
查看>>
苹果Siri再新增服务内容 航班查询、餐点外送和血糖监控
查看>>
学习笔记
查看>>
远程协助,TeamViewer之外的另一种选择
查看>>
Centos 6.5 部署 LNMP
查看>>
网安天目
查看>>
rsync远程同步及rsync+inotify实时同步
查看>>
Redis分布式锁的正确实现方式(Java版)
查看>>
Linux20180427
查看>>
linux系统配置及服务管理第一章系统部署
查看>>
SQL语句优化:show参数
查看>>
webstorm那些常用快捷键
查看>>
MySQL5.7 group by新特性报错1055的解决办法
查看>>
网易企业邮箱的萨班斯归档是什么?
查看>>
阿里架构师告诉你最新Java架构师学习路线图
查看>>
飞天技术汇“2018云栖大会·重庆峰会”专场,“一出好戏”等你加入
查看>>