Service启动过程过程详解

Service的工作过程 Service的工作过程Service分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和Service的交互。这两种状态可以共存的,即Service既可以处于启动状态也可以同时处于绑定状态。 本篇博客是对Service的启动过程和绑定过程进行源码分析。 介绍一下几个重要的概念: 1. Context:http://blog.youkuaiyun.com/q 阅读详情

Service的几种启动方式:


1、startService

public class myActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  startService(new Intent (IMediaScannerService.class.getName()); //"com.android.providers.media.MediaScannerService"
  }

StartService()方法主要用于启动一个服务执行后台任务,不进行通信。调用者和服务之间没有联系,即使调用者退出了,服务依然在运行。


2.bindService

public class myActivity extends Activity {
       ServiceConnection conn = new ServiceConnection() {
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            
        }
    };


   @Override  public void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  bindService(new Intent (IMediaScannerService.class.getName(),conn,Context.BIND_AUTO_CREATE);

  }


使用bindService()方法来绑定服务,调用者和绑定者绑在一起,调用者一旦退出如果没有其他用户使用则服务也就终止了。

3.隐式启动服务Implicitly start service

When a service is referenced,it will be started automatically.

For example,the MediaScannerService will be started with following code:

import android.media.MediaScannerConnection;

MediaScannerConnection.MediaScannerConnectionClient client= new MediaScannerConnection.MediaScannerConnectionClient(){
   public void onMediaScannerConnected(){
      conn.scanFile("/mnt//sdcard/test.jpg");
   }
   public void onScanComplete(){
      conn.disconnect(); //close the connection here
   }
}
MediaScannerConnection conn=new MediaScannerConnection(getContext(),client);
conn.connect();         //here will trigger start MediaScannnerService

4.通过广播启动服务

下面通过广播action ACTION_MEDIA_SCANNER_SCAN_FILE从而启动Service并扫描单个文件。

Uri data = Uri.parse("file:///"+fName);  
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));  



下图描述了当一个服务所在进程已经启动,但服务尚未启动时,调用startService时的调用顺序。


由图可知,在14步时,发出SERVICE_TIMEOUT消息,在29步时,删除此消息。如果此超时消息未能及时删除,则在消息处理时将显示为anr。目前此消息超时时间设为20s。


ActivityManagerService.java

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType) {
...
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = startServiceLocked(caller, service,resolvedType, callingPid, callingUid); //入口
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

    ComponentName startServiceLocked(IApplicationThread caller,
            Intent service, String resolvedType,
            int callingPid, int callingUid) {
        synchronized(this) {
            if (DEBUG_SERVICE) Slog.v(TAG, "startService: " + service
                    + " type=" + resolvedType + " args=" + service.getExtras());

            if (caller != null) {
                final ProcessRecord callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when starting service " + service);
                }
            }

            ServiceLookupResult res =
                retrieveServiceLocked(service, resolvedType,
                        callingPid, callingUid);
            if (res == null) {
                return null;
            }
            if (res.record == null) {
                return new ComponentName("!", res.permission != null
                        ? res.permission : "private to package");
            }
            ServiceRecord r = res.record;
            int targetPermissionUid = checkGrantUriPermissionFromIntentLocked(
                    callingUid, r.packageName, service);
            if (unscheduleServiceRestartLocked(r)) {
                if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r);
            }
            r.startRequested = true;
            r.callStart = false;
            r.lastStartId++;
            if (r.lastStartId < 1) {
                r.lastStartId = 1;
            }
            r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId,
                    service, targetPermissionUid));
            r.lastActivity = SystemClock.uptimeMillis();
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startRunningLocked();
            }
            if (!bringUpServiceLocked(r, service.getFlags(), false)) {
                return new ComponentName("!", "Service process is bad");
            }
            return r.name;
        }
    }

    private final boolean bringUpServiceLocked(ServiceRecord r,
            int intentFlags, boolean whileRestarting) {
        if (r.app != null && r.app.thread != null) { //如果Service已经在运行
            sendServiceArgsLocked(r, false);
            return true;
        }

        if (!whileRestarting && r.restartDelay > 0) {
            // If waiting for a restart, then do nothing.
            return true;
        }

        // We are now bringing the service up, so no longer in the
        // restarting state.
        mRestartingServices.remove(r);
        
        final String appName = r.processName;
        ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
        if (app != null && app.thread != null) { //如果Service所在进程已经运行
            try {
                realStartServiceLocked(r, app);
                return true;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (startProcessLocked(appName, r.appInfo, true, intentFlags,
                "service", r.name, false) == null) { //进程尚未启动,启动进程并指定启动对应service
            bringDownServiceLocked(r, true);
            return false;
        }
        
        if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }
        
        return true;
    }



以下发出超时消息,此消息必须在规定时间内被删除,否则出现anr。SERVICE_TIMEOUT=20*1000ms.

    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++;
        r.executingStart = now;
    }


下面函数将从AM启动Service

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
        if (app.thread == null) { //检查所在进程是否存在
            throw new RemoteException();
        }

        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); //记录启动时间

        app.services.add(r);
        bumpServiceExecutingLocked(r, "create");  //发送超时消息
        updateLruProcessLocked(app, true, true);

        boolean created = false;
        try {
            mStringBuilder.setLength(0);
            r.intent.getIntent().toShortString(mStringBuilder, false, true);
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked(); //电池数据统计启动
            }
            ensurePackageDexOpt(r.serviceInfo.packageName); //???
            app.thread.scheduleCreateService(r, r.serviceInfo); //通知Service所在进程创建Service
            r.postNotification();
            created = true;
        } finally {
            if (!created) {
                app.services.remove(r);
                scheduleServiceRestartLocked(r, false);
            }
        }

        requestServiceBindingsLocked(r); //等待Service运行成功才返回?
        
        // If the service is in the started state, and there are no
        // pending arguments, then fake up one so its onStartCommand() will
        // be called.
        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); //引起Service.onStartCommand被调用
    }

下面函数将消息SERVICE_ARGS发送到Service进程的消息队列,在此将传来的Intent传给指定Service:


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

        while (r.pendingStarts.size() > 0) {
            try {
                ServiceRecord.StartItem si = r.pendingStarts.remove(0);
                if (si.intent == null) {
                    // If somehow we got a dummy start at the front, then
                    // just drop it here.
                    continue;
                }
                si.deliveredTime = SystemClock.uptimeMillis();
                r.deliveredStarts.add(si);
                si.deliveryCount++;
                if (si.targetPermissionUid >= 0) {
                    grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid,
                            r.packageName, si.intent, si.getUriPermissionsLocked());
                }
                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;
                }
                r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent); //发送Intent到Service所在进程消息队列,引起Service.onStartCommand被调用
            } catch (RemoteException e) {
                // Remote process gone...  we'll let the normal cleanup take
                // care of this.
                break;
            } catch (Exception e) {
                Slog.w(TAG, "Unexpected exception", e);
                break;
            }
        }
    }

r.app.thread.scheduleServiceArgs将发送消息SERVICE_ARGS到ActivityThread.Handler,在此将调用handleServiceArgs来完成

ActivityThread.java

    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());
                }
                int res = s.onStartCommand(data.args, data.flags, data.startId); //调用Service.onStartCommand方法

                QueuedWork.waitToFinish(); //等待本进程所有异步操作完成

                try {
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, 1, data.startId, res); //通知操作完成,并删除超时消息
                } catch (RemoteException e) {
                    // nothing to do.
                }
                ensureJitEnabled();  //确保JIT优化启动(如果未启动则启动),但为何在此做??
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to start service " + s
                            + " with " + data.args + ": " + e.toString(), e);
                }
            }
        }
    }

每当AM需要异步执行Service操作时,在操作完后均需调用函数serviceDoneExecuting以防止Service操作超时。

    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) { //为何?
                mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app); //删除所有object=r.app的超时消息
            }
            if (inStopping) {
                mStoppingServices.remove(r);
                r.bindings.clear();
            }
            updateOomAdjLocked(r.app); //检查是否需要kill app以避免OOM
        }
    }





        
Nacos 故障转移源码分析(FailoverReactor) SwitchRefresher 实现了 Runnable 接口,业务逻辑都在 run 方法中,首先会判断故障转移文件是否存在,不存在则直接返回,然后判断文件是否修改了,如果已经修改,则获取故障转移文件中的内容,故障转移文件内容中存储了 0 和 1 标识,0 表示故障转移关闭、1 表示故障转移开启,当为开启状态时,会执行线程 FailoverFileReader#run 方法。本篇我们来简单分析一下 Nacos 的故障转移。至此,Nacos 故障转移源码分析完毕,希望可以帮助到有需要的朋友。 阅读详情

相关推荐

C语言编写Picard迭代和牛顿迭代法

为了完成学校计算方法的作业,花了两个多星期将所有实验报告编写出来,看到同桌找代码的辛苦,优快云也没有这类代码,我只是一时兴起,想分享我写的代码与大家共飨,有错误还请多多指正,代码都不难,也是为了让优快云帮我保存下,我辛辛苦苦做出来的,倘若能给别人带来一丝一毫的帮助,我都万分荣幸。 1:什么是Picard迭代 先弄一个满足f(x) = 0的函数将其转化为x= 正在上传… 重新上传 取消 ...

mohenxiang的博客 4699

(二)SpringBoot应用运维脚本

SpringBoot应用运维脚本一、获取PID二、Kill命令三、nohup命令四、编写SpringBoot应用运维脚本4.1全局变量4.2编写核心方法4.3Info方法4.4status方法4.5start方法4.6stop方法4.7restart方法4.8测试4.9完整的server.sh内容 编写SpringBoot应用运维脚本除了基本的Shell语法要相对熟练之外,还需要解决两个比较重要的问题: 正确获取目标应用程序的进程ID,也就是获取Process ID(下面称PID)的问题。 kill命令的

爱是与世界平行 811

支持找回微信删除好友!一款人人可用的全能微信数据分析工具

找回删除好友、查看删除记录、恢复聊天信息....

1725

nacos 2.0 源码学习之服务注册

nacos源码分析

wlei18610617750的博客 2224

【深度剖析】三一重工的数字化转型(中篇)

三一重工在数字化转型方面取得了提高效率、降低成本、加强研发创新和改善内控管理等多方面的成效。本研究旨在通过对三一重工数字化转型的深入分析,引导企业思考在第四次工业革命与第三次能源革命交汇的超级技术窗口期,如何进一步把握发展机遇,并为同行业其他企业提供探索性的建议。作为装备制造行业的佼佼者,三一重工的数字化转型受到了业界的广泛关注。作为行业内首批接触并实践数字化转型的企业之一,三一重工的转型经验不仅成功,而且积累了丰富的理论与实践,为其他制造业企业的数字化转型提供了可借鉴的范例。

领信卓越的博客 2306

200SMART PLC时间不准?手把手教你用MCGS触摸屏实现自动校时(附完整代码)

本文针对西门子200SMART PLC时钟不准的常见问题,提供了一套基于MCGS触摸屏的自动校时解决方案。文章详细阐述了时钟偏差的根源、时间同步方案的技术选型,并手把手指导如何配置MCGS与PLC的通信,以及编写完整的PLC时间读取、转换与设置代码,最终实现稳定可靠的自动时间同步功能。

jupyter5notebook的博客 453

Nacos客户端本地缓存和故障转移

在Nacos客户端从Server获得服务的时候,在某些时候出现了一些故障, 这时候为了保证服务正常,Nacos进行了故障转移,原理就是将之前缓存的服务信息拿出来用,防止服务出现问题,涉及到的核心类为ServiceInfoHolder和FailoverReactor。 本地缓存有两方面,第一方面是从注册中心获得实例信息会缓存在内存当中,也就是通过Map的形式承载,这样查询操作都方便。第二方面便是通过磁盘文件的形式定时缓存起来,以备不时之需。 故障转移也分两方面,第一方面是故障转...

7817

锁定放大器

锁定放大器

qq_46757048的博客 4706

6、Nacos服务注册——PushService类功能

如果您对Nacos工作流程和原理还不是很清楚的话,建议从前面的文章开始看: 1、nacos功能简介 2、Nacos服务注册-客户端自动注册流程 3、Nacos服务注册-客户端(nacos-client)逻辑 4、Nacos服务注册-服务端(nacos-naming)逻辑 5、Nacos心跳机制与健康检查    前言 PushService类挺复杂的,我觉得很有必要先整体说下PushService类的功能,这将有助于理解后面将要讲得内容。 如果你是从前面的文章一篇一篇看下来的话,那么你应该很清楚我们是如何进入

jb84006的博客 2706

5G NR LDPC码(2)—— 从基图到速率匹配:解码5G高速数据信道的标准化设计

本文深入解析了5G NR LDPC码的标准化设计流程。从作为核心骨架的基图(BG1/BG2)出发,详细阐述了其如何通过扩展因子Z灵活生成实际校验矩阵,并协同传输块大小计算与分段策略。文章重点剖析了基于循环缓存和冗余版本的速率匹配机制,以及其如何高效支撑增量冗余混合自动重传请求,从而确保5G高速数据信道在复杂环境下的可靠与高效传输。

weixin_29206669的博客 272

基于ROS2的无人机野生动物巡查系统设计

自主巡检系统是嵌入式人工智能与机器人技术融合的关键应用方向,其核心在于多源感知融合、实时路径规划与端侧智能识别的协同实现。原理上依赖SLAM定位构建环境地图,结合栅格化建模与混合路径算法(如蚁群+BFS)达成全覆盖遍历;技术价值体现在资源受限平台下的确定性响应与时空对齐能力,支撑高鲁棒性野外作业。典型应用场景包括生态监测、禁飞区巡检及小尺度智能安防。本文围绕ROS2 Humble中间件架构、Livox激光雷达与MaixCAM视觉协同、YOLO11n INT8模型端侧部署等关键技术展开,突出嵌入式AI与实时控

weixin_30598047的博客 649

Android中Service的启动与绑定过程详解(基于api29)

前言 前面我写到一个文章是关于Activity启动流程的点击链接前往。这一篇的内容是关于Service的启动和绑定的流程详解。Service和Activity一样,都是受AMS和ActivityThread的管理,所以在启动流程上两者有一些相似。不同的是Service有绑定的流程,相对比较复杂一点,结合Service的生命周期来理解,也不是很复杂。 了解启动源码的好处是整个Service对于你来说已经是透明的了,我们所做的每一个操作,心里都很清楚他的背后发生了什么。这是一种自信,也是一种能力,一种区别于入门

一只修仙的猿 8355

JavaSwing_1.1: FlowLayout(流式布局)

FlowLayout,流式布局管理器。按水平方向依次排列放置组件,排满一行,换下一行继续排列。

谢TS的博客 7万+

android启动过程详解(二)——解析service

最重要的一个service是zygote,之后的博文均以zygote为例讲解service。   上篇讲到init_parser.c中parse_new_section函数中根据关键字service可以开始对service的处理。处理函数为parse_service()和parse_line_service().   在看这两个函数之前,首先看下init.rc中对zygote servic

huxueyan521的专栏 3723

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

深度详解 Android R(11.0)Service 启动过程

服务 (Service) 是 Android 系统中 4 大应用程序组件之一,主要用途有:后台运行和跨进程访问。通过启动服务可以在不显示界面的前提下在后台执行那些不需要和用户交互且长期运行的任务,即使程序切换到后台,或者用户打开了另一个应用程序,服务仍然能够保持独立运行。通过 AIDL 服务可以实现不同进程之间的数据通信,后面详细解析。Service 的存在形式:本地服务、远程服务和前台服务。Service 的状态:启动状态和绑定状态,也可以说成是服务的两种启动方式。.........

neuHenry 3010

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Android开发让广播来启动Service进行耗时工作的处理过程详解

今天复习了服务的基础知识,下面尝试使用广播来启动一个Service进行耗时工作的处理。 首先明白,当启动服务的时候有两种方法,一种是使用StartService,另一种是使用bindService,使用bindService启动的话执行的方法onCreate---&gt;onbind,而且这种方法启动的话,只要该service对象存在,onbind只执行一次,以后再点击bindService按钮...

yaoyaoyao_123的博客 3528

Android筑基——Service的启动过程之同进程启动(基于api21)

目录1. 前言2. 正文2.1 ContextWrapper.startService() 方法2.2 ContextImpl.startService() 方法2.3 ContextImpl.startServiceCommon() 方法2.3.1 ActivityManagerNative.getDefault() 方法2.4 ActivityManagerProxy.startService() 方法2.5 ActivityManagerNative.onTransact() 方法2.6 Activi

willwaywang6 4054

Android进阶笔记-3. Service 启动过程 & 绑定过程

startService和bindService Service的启动方式主要有两种,分别是startService和bindService 使用startService启动时是单独开一个服务,与Activity没有任何关系,而bindService方式启动时,Service会和Activity进行绑定,当对应的activity销毁时,对应的Service也会销毁 startService多次,onStart()会执行多次,onCreate()只执行一次,onStartCommand()也会执行多次。bi

o118abcdef的博客 2204

3.安卓service详解

1.service生命周期 2.Android中Service(服务)详解:http://blog.youkuaiyun.com/ryantang03/article/details/7770939

strugglelin'blog 323

Android系统启动过程详解

前言 一直想研究Android完整的启动过程,网上看了不少资料,也看了书上的一些说明,对这些观点有些怀疑,于是自己分析了系统启动的完整过程。从内核启动第一个用户程序init开始说起,直到Home应用的启动,每一步都有源代码展示。希望能解除读者对Android系统启动过程中的困惑,若有什么疑问,欢迎留言交流。本研究基于CM10.1源码,读者若能对照源代码查看效果会更好。 1) init启

byszy的专栏 1508

解析Service的启动过程

Service 的启动过程 Service 的启动过程和根 Activity 启动过程有部分相似的知识点,另外 Service 启动过程涉及上下文 Context 的知识点,这里只关注流程而不会详细介绍 Context ,Service 的启动过程将分为两个部分来进行讲解,分别是 Contextimpl到ActivityManageService 调用过程和 ActivityThread 启动 Service。本文基于Android8.1.0系统分析Service的启动过程 ...

lixiong0713的博客 2万+

Android Service的启动过程

刚开始学习Service的时候以为它是一个线程的封装,也可以执行耗时操作。其实不然,Service是运行在主线程的。直接执行耗时操作是会阻塞主线程的。长时间就直接ANR了。我们知道Service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔。 这样就很容易想到音乐播放器,天气预报这些应用是要用到Service的。当然如果要在Service中执行耗时操作的话,开个线程就可以了

yinhuanxu 5200

Service的启动过程

Service的启动过程 startService(); ContextImpl

zhaonhui的博客 506

Android Service的启动过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 刚开始学习Service的时候以为它是一个线程的封装,也可以执行耗时操作。其实不然,Service是运行在主线程的。直接执行耗时操作是会阻塞主线程的。长时间就直接ANR了。 我们知道Service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔。 这样就很容易想到音乐播放器,天气预报这些应用是要用到Serv

阳光灿烂的日子的博客 1450
上一篇: 实例分析:android.process.media由于调用进程crash而退出
下一篇: Tablet不断重启原因分析
cloudwu007
博客等级 码龄18年 42粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值