当前位置: 首页 > article >正文

Android中Service在新进程中的启动流程3

       

目录

1、AMS调用客户端onCreate前的准备工作

2、AMS调用客户端onCreate方法

3、AMS调用客户端的onBind方法

4、AMS调用客户端onStart前的准备

5、AMS调用客户端onStart方法


        还是先放上Service启动流程概览图,如下:

        上一篇文章, 我们分析到了第五步scheduleCreateService,该方法位于AMS的realStartServiceLocked方法调用中,我们说过当新进程启动之后,会通过AMS调用attachApplication,该方法通过最后调用了realStartServiceLocked,所以我们继续从该方法开始分析,代码如下:

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
        //...
        //1、保存相关变量
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        app.services.add(r);
        //2、仔细看看?
        bumpServiceExecutingLocked(r, "create");
        updateLruProcessLocked(app, true, true);

        boolean created = false;
        try {
            //3、调用客户端的scheduleCreateService方法
            ensurePackageDexOpt(r.serviceInfo.packageName);
            app.thread.scheduleCreateService(r, r.serviceInfo);
            r.postNotification();
            created = true;
        } finally {
            //...
        }

        //4调用绑定方法
        requestServiceBindingsLocked(r);
        
        //5、设置相关变量
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
            r.lastStartId++;
            if (r.lastStartId < 1) {
                r.lastStartId = 1;
            }
            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
        }
        //6、调用onStart
        sendServiceArgsLocked(r, true);
    }

        这里再深入分析下该方法,主要涉及以下几点:

  1. r.app = app:把ProcessRecord保存到ServiceRecord中,表示服务ServiceRecord服务r运行于ProcessRecord对象app中。app.services.add(r):把服务ServiceRecord服务对象r保存到ProcessRecord对象的services集合中,表示ProcessRecord进程中运行了哪些服务。
  2. bumpServiceExecutingLocked(r, "create"):这里待会分析,主要是调用服务onCreate前,AMS需要做些工作。
  3. scheduleCreateService:该方法最后会调用客户端服务的onCreate方法。
  4. requestServiceBindingsLocked(r):该方法最后会调用客户端服务的onBind方法。
  5. 调用客户端onStart之前的一些准备工作。
  6. 调用客户端的onStart

        下面我们一步步分析以上几点(第一点以及比较明确,不再说明)

1、AMS调用客户端onCreate前的准备工作

        我们知道在服务的onCreate方法中执行太久会导致系统弹窗ANR,这是如何实现的呢?玄机就在这里,我们分析下调用bumpServiceExecutingLocked(r, "create"),代码如下:

    private final void bumpServiceExecutingLocked(ServiceRecord r, String why) {
        //...
        long now = SystemClock.uptimeMillis();
        if (r.executeNesting == 0 && r.app != null) {
            if (r.app.executingServices.size() == 0) {
                Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
                msg.obj = r.app;
                mHandler.sendMessageAtTime(msg, now+SERVICE_TIMEOUT);
            }
            r.app.executingServices.add(r);
        }
        r.executeNesting++;
        //主要用于计时,看服务是否允许超时,超时则会ANR
        r.executingStart = now;
    }

        发送了消息SERVICE_TIMEOUT_MSG,延时SERVICE_TIMEOUT,时间是20s;所以onCreate执行的时间最多及时20s,我们看看超时的处理,也是位于AMS中,跟踪到逻辑如下:

    void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;
        
        synchronized(this) {
            if (proc.executingServices.size() == 0 || proc.thread == null) {
                return;
            }
            long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
            Iterator<ServiceRecord> it = proc.executingServices.iterator();
            ServiceRecord timeout = null;
            long nextTime = 0;
            while (it.hasNext()) {
                ServiceRecord sr = it.next();
                if (sr.executingStart < maxTime) {
                    timeout = sr;
                    break;
                }
                if (sr.executingStart > nextTime) {
                    nextTime = sr.executingStart;
                }
            }
            if (timeout != null && mLruProcesses.contains(proc)) {
                Slog.w(TAG, "Timeout executing service: " + timeout);
                anrMessage = "Executing service " + timeout.shortName;
            } else {
                Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
                msg.obj = proc;
                mHandler.sendMessageAtTime(msg, nextTime+SERVICE_TIMEOUT);
            }
        }
        
        if (anrMessage != null) {
            appNotResponding(proc, null, null, anrMessage);
        }
    }

         遍历ProcessRecord对象中的集合executingServices,看是否有超时的服务,如果有最后会调用appNotResponding,也就弹出了ANR弹窗。

2、AMS调用客户端onCreate方法

        现在来看看app.thread.scheduleCreateService(r, r.serviceInfo)如何调用到客户端的onCreate方法呢。

        我们知道该方法会进入到ActivityThread的scheduleCreateService方法(严格来说是ApplicationThread)中,代码如下:

        public final void scheduleCreateService(IBinder token,
                ServiceInfo info) {
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;

            queueOrSendMessage(H.CREATE_SERVICE, s);
        }

        发送了个CREATE_SERVICE消息,该消息的处理如下:

    private final void handleCreateService(CreateServiceData data) {
        //...
        LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
        Service service = null;
        try {
            //1 加载服务类
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            //...
        }

        try {
            //2 创建ContextImpl对象
            ContextImpl context = new ContextImpl();
            context.init(packageInfo, null, this);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            context.setOuterContext(service);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //3 调用服务的onCreate方法
            service.onCreate();
            mServices.put(data.token, service);
            try {
                //4 服务执行完通知AMS
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, 0, 0, 0);
            } catch (RemoteException e) {
                // nothing to do.
            }
        } catch (Exception e) {
            //....
        }
    }

         主要有以下几个关键点:

  1. 通过loadClass加载我们实现的服务类,比如MyService。
  2. 创建ContextImpl对象,所以我们在服务Service里面调用的相关Context方法最终都是由这里创建的ContextImpl对象实现的。
  3. 调用服务的onCreate方法,这是我们需要实现的服务生命周期方法。
  4. 服务执行完onCreate方法后调用serviceDoneExecuting(
                            data.token, 0, 0, 0)通知AMS。

        主要看看第四点做了啥,serviceDoneExecuting的实现位于AMS中,代码如下:

    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
        synchronized(this) {
            //...
            ServiceRecord r = (ServiceRecord)token;
            boolean inStopping = mStoppingServices.contains(token);
            if (r != null) {
                if (r != token) {
                    //...
                    return;
                }

                //1、不会走这里
                if (type == 1) {
                    //...
                }
                
                final long origId = Binder.clearCallingIdentity();
                //2
                serviceDoneExecutingLocked(r, inStopping);
                Binder.restoreCallingIdentity(origId);
            } else {
                //...
            }
        }
    }

        参数type为0,所第一处不会进入;继续调用serviceDoneExecutingLocked,代码如下:

 

    public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) {
        //...
        r.executeNesting--;
        if (r.executeNesting <= 0 && r.app != null) {
            //移除正在执行的服务
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //1
                mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app);
            }
            if (inStopping) {
                //移除已经停止的服务
                mStoppingServices.remove(r);
                r.bindings.clear();
            }
            updateOomAdjLocked(r.app);
        }
    }

        标记1处移除了延时消息SERVICE_TIMEOUT_MSG ,而该消息正是执行方法bumpServiceExecutingLocked时post的,这里移除掉就不会有ANR产生了。

        所以我们是不是已经清楚了为啥Service的onCreate方法耗时太久会产生ANR呢?其实正是因为在调用客户端Service的onCreate之前,AMS悄悄的post了一个延时消息SERVICE_TIMEOUT_MSG,时间是20s,要是客户端的onCreate在20s内执行完了,就会把延时消息移除掉,这样就不会产生ANR了,反之则AMS会弹窗ANR。

3、AMS调用客户端的onBind方法

        我们继续分析,接下来是requestServiceBindingsLocked方法调用,代码如下:

    private final void requestServiceBindingsLocked(ServiceRecord r) {
        Iterator<IntentBindRecord> bindings = r.bindings.values().iterator();
        while (bindings.hasNext()) {
            IntentBindRecord i = bindings.next();
            if (!requestServiceBindingLocked(r, i, false)) {
                break;
            }
        }
    }

         IntentBindRecord代表了绑定到该服务的客户端,这里遍历绑定了的对象,然后调用方法requestServiceBindingLocked,代码如下:

    private final boolean requestServiceBindingLocked(ServiceRecord r,
            IntentBindRecord i, boolean rebind) {
        //假如服务还没起来,不能调用onBind,直接返回了
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, "bind");
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (RemoteException e) {
                return false;
            }
        }
        return true;
    }

        套路与调用客户端的onCreate方法是一致的,先调用bumpServiceExecutingLocked方法post一个延时消息,然后调用scheduleBindService,该方法最终会走到ActivityThread的 handleBindService中,代码如下:

    private final void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                try {
                    if (!data.rebind) {//绑定服务
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {//重新绑定
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, 0, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                }
            } catch (Exception e) {
                //...
            }
        }
    }

         根据标识data.rebind判断是重新绑定还是首次绑定,首次绑定会调用onBind(然后调用AMS的publishServie发布服务,读者可以自己分析发布服务做了些啥);重新绑定调用onRebind,然后调用AMS的serviceDoneExecuting方法,从AMS调用onCreate的分析中我们知道方法serviceDoneExecuting的作用就是移除延时消息,避免ANR产生(publishService最后也会调用方法serviceDoneExecuting,所以也不会出现ANR)。

4、AMS调用客户端onStart前的准备

        这比较简单,代码注释清楚了,不再重复说明,代码如下:

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
        //....
        
        //在调用onStart前创建了个StartItem并放入集合pendingStarts中
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
            r.lastStartId++;
            if (r.lastStartId < 1) {
                r.lastStartId = 1;
            }
            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1));
        }
        
        sendServiceArgsLocked(r, true);
    }

5、AMS调用客户端onStart方法

        最后是AMS对onStart方法的调用,sendServiceArgsLocked代码如下:

    private final void sendServiceArgsLocked(ServiceRecord r,
            boolean oomAdjusted) {
        final int N = r.pendingStarts.size();
        if (N == 0) {
            return;
        }

        while (r.pendingStarts.size() > 0) {
            try {
                //1 
                ServiceRecord.StartItem si = r.pendingStarts.remove(0);
                if (si.intent == null) {
                    continue;
                }
                si.deliveredTime = SystemClock.uptimeMillis();
                r.deliveredStarts.add(si);
                si.deliveryCount++;
                //...
                //2
                bumpServiceExecutingLocked(r, "start");
                if (!oomAdjusted) {
                    oomAdjusted = true;
                    updateOomAdjLocked(r.app);
                }
                int flags = 0;
                if (si.deliveryCount > 0) {
                    flags |= Service.START_FLAG_RETRY;
                }
                if (si.doneExecutingCount > 0) {
                    flags |= Service.START_FLAG_REDELIVERY;
                }
                //3
                r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
            } catch (RemoteException e) {
                //....
                break;
            } catch (Exception e) {
                Slog.w(TAG, "Unexpected exception", e);
                break;
            }
        }
    }

        还是对几个关键点说明如下:

  1. 取出上一步放入pendingStarts中的StartItem对象,并记录了deliveredTime和deliveredCount。
  2. 调用bumpServiceExecutingLocked(r, "start"),套路与上面分析的一致。
  3. 调用客户端的scheduleServiceArgs。

        我们重点看看scheduleServiceArgs在客户端侧的实现,一路跟踪下去,最后调用了方法handleServiceArgs方法,代码如下:

    private final void handleServiceArgs(ServiceArgsData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                }
                //1 调用onStartCommand
                int res = s.onStartCommand(data.args, data.flags, data.startId);

                QueuedWork.waitToFinish();

                try {
                    //2 调用serviceDoneExecuting
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, 1, data.startId, res);
                } catch (RemoteException e) {
                    // nothing to do.
                }
                ensureJitEnabled();
            } catch (Exception e) {
                //...
            }
        }
    }

        首先是调用onStartCommand方法,然后其返回值作为参数调用AMS侧的方法serviceDoneExecuting(data.token, 1, data.startId, res),注意第二个参数为1,返回值为最后一个参数,我们再次看看serviceDoneExecuting的相关实现,代码如下:

    public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
        synchronized(this) {
            //...
            ServiceRecord r = (ServiceRecord)token;
            boolean inStopping = mStoppingServices.contains(token);
            if (r != null) {
                if (r != token) {
                    //...
                    return;
                }

                //1 这次会进入这个分支了
                if (type == 1) {
                    // This is a call from a service start...  take care of
                    // book-keeping.
                    r.callStart = true;
                    switch (res) {
                        case Service.START_STICKY_COMPATIBILITY:
                        case Service.START_STICKY: {
                            // We are done with the associated start arguments.
                            r.findDeliveredStart(startId, true);
                            // Don't stop if killed.
                            r.stopIfKilled = false;
                            break;
                        }
                        case Service.START_NOT_STICKY: {
                            // We are done with the associated start arguments.
                            r.findDeliveredStart(startId, true);
                            if (r.lastStartId == startId) {
                                // There is no more work, and this service
                                // doesn't want to hang around if killed.
                                r.stopIfKilled = true;
                            }
                            break;
                        }
                        case Service.START_REDELIVER_INTENT: {
                            // We'll keep this item until they explicitly
                            // call stop for it, but keep track of the fact
                            // that it was delivered.
                            ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
                            if (si != null) {
                                si.deliveryCount = 0;
                                si.doneExecutingCount++;
                                // Don't stop if killed.
                                r.stopIfKilled = true;
                            }
                            break;
                        }
                        default:
                            throw new IllegalArgumentException(
                                    "Unknown service start result: " + res);
                    }
                    if (res == Service.START_STICKY_COMPATIBILITY) {
                        r.callStart = false;
                    }
                }
                
                final long origId = Binder.clearCallingIdentity();
                serviceDoneExecutingLocked(r, inStopping);
                Binder.restoreCallingIdentity(origId);
            } else {
                //...
            }
        }
    }

        这次type参数为1,所以会用到onStartCommond的返回值res了,我们知道onStartCommond的返回值有START_STICKY_COMPATIBILITY、START_STICKY、START_NOT_STICKY、START_REDELIVER_INTENT。对这些参数的处理就在AMS侧的这个方法中。

  1. START_STICKY:粘性的”。服务被异常kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
  2. START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
  3. START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
  4. START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

         所以我们可以根据业务需求,在重写onStartCommond方法的时候返回不同的值。

        startService的启动流程终于说完了;当然还有很多地方没有提到,但是我们可以这个流程为线索,一步步探索AMS中相关的实现,不至于密室在代码的海洋中,让自己对AMS有更深入的理解。

        最后恭喜你已学习完了startService的启动流程,还有疑问或者需要从新再次学习该启动流程,可以从这里进入:

  1. Android中Service在新进程中的启动流程-CSDN博客
  2. Android中Service在新进程中的启动流程2-CSDN博客

http://www.kler.cn/a/517565.html

相关文章:

  • [ Spring ] Spring Cloud Gateway 2025 Comprehensive Overview
  • 为AI聊天工具添加一个知识系统 之69 详细设计 之10 三种中台和时间度量 之2
  • 案例研究丨浪潮云洲通过DataEase推进多维度数据可视化建设
  • Linux查看服务器的内外网地址
  • 奖励模型:解析大语言模型的关键工具
  • 《Chart.js 饼图:深度解析与最佳实践指南》
  • Vue 3 的 setup 函数
  • Gaea项目的挑战与机遇:去中心化AI平台的未来发展
  • 洛谷 B2031:计算三角形面积 ← 叉积
  • 飞行器半实物联合仿真:技术解析与应用实践
  • shell中for循环的用法
  • 深圳大学-智能网络与计算-实验一:RFID原理与读写操作
  • Charles 4.6.7 浏览器网络调试指南:功能详解下(五)
  • HarmonyOS NEXT边学边玩:从零实现一个影视App(七、今日票房页面的设计与实现)
  • 结构生物学3-冷冻电镜单颗粒重构:
  • 【C++】异常与智能指针
  • 分组表格antd+ react +ts
  • Python数据分析-数据加载与存储(六)
  • .NET 9.0 的 Blazor Web App 项目、Bootstrap Blazor 组件库、自定义日志 TLog 使用备忘
  • 【开源实录】从App Store审核失败到开源:一个AI辅助开发的SwiftUI项目
  • Docker 在Linux 系统中的使用说明
  • 数据结构与算法再探(六)动态规划
  • AIP-128 声明友好接口
  • Java语言程序设计(第3版) 课后练习
  • 《jEasyUI 动态改变列》
  • 解决lombok注解失效