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

欢迎读者阅读我的个人博客点击前往我的个人博客
更精美的UI,拥有更舒适的阅读体验。
本文章个人博客链接Android中Service的启动与绑定过程详解(基于api29)

前言

前面我写到一个文章是关于Activity启动流程的点击链接前往。这一篇的内容是关于Service的启动和绑定的流程详解。Service和Activity一样,都是受AMS和ActivityThread的管理,所以在启动流程上两者有一些相似。不同的是Service有绑定的流程,相对比较复杂一点,结合Service的生命周期来理解,也不是很复杂。

了解启动源码的好处是整个Service对于你来说已经是透明的了,我们所做的每一个操作,心里都很清楚他的背后发生了什么。这是一种自信,也是一种能力,一种区别于入门与高级工程师的能力。

文章涉及到很多的源码,我以“思路先行,代码后讲”的思维来讲解这一过程。先对总体有个感知,再对具体代码进行研究会有更深刻的理解。希望可以认真看完每一处的代码。我更加建议把一边看文章一边自己查看源码,这样可以加深理解。文章涉及到跨进程通信,如果对此还没有学习的话,可能看起来一些步骤比较难以理解,建议先学一下跨进程通信AIDL。

代码中我加了非常多的注释来帮助大家理解每个重点的内容,请一定要看代码内容。代码外的讲解仅仅只是总体流程的概述。

理解源码需要对Service的生命周期有一个完整的认识,可以帮助理解源码中的内容。在下面的解析中我也会穿插生命周期的一些补充,希望可以帮助大家理解。

本文内容大纲

av8XNT.png

总体工作流程思路概述

这一部分主要为大家有个整体的流程感知,不会在源码中丢失自己。分为两个部分:启动流程与绑定流程。

直接启动流程概述

概述

直接启动,也就是我们在Activity中调用startService来启动一个Service的方式。

图解
Service启动整体流程概述.png
简析

启动的步骤:

  1. Activity向AMS,即ActivityManagerService请求启动Service。
  2. AMS判断Service所在的进程是否已经创建,注意Service和Activity是可以同个进程的。
  3. 如果还没创建则通过Zygote进程fork一个进程。
  4. AMS请求Service所在进程的ActivityThread创建Service和启动,并回调Service的onCreate方法。
  5. Service创建完成之后,AMS再次请求ActivityThread进行相关操作并回调onStartCommand方法。

过程中onCreate和onStartCommand是在两次跨进程通信中完成的,需要注意一下。总共最多涉及到四个进程。

绑定流程概述

概述

绑定服务即我们在Activity中调用bindService来绑定服务。绑定的过程相对直接启动来说比较复杂一点,因为涉及到一些生命周期,需要进行一些条件判断。

图解

图有点复杂,配合下面的解析一起看

avlXND.png
简析

介绍一下步骤:

  1. Activity请求AMS绑定Service。
  2. AMS判断Service是否已经启动了。如果还没启动会先去启动Service。
  3. AMS调用Service的onBind方法获取Service的IBinder对象。
  4. Service把自己的IBinder对象发布到AMS中保存,这样下次绑定就不需要再调用onBind了。
  5. 把拿到的IBinder对象返回给Activity。
  6. Activity可以直接通过这个IBinder对象访问Service。

这里是主体的流程。绑定的过程涉及到很多情况的判断,如:进程是否创建,是否已经绑定过,是否已经调用了onUnBind方法等。后面的源码讲解会讲到。这里我只放整体的流程。

源码讲解

这一部分内容枯燥,但我把源码尽量提取相关的代码,其他的用省略号进行省略,减少无用的代码干扰。源码中的重点我用了很多的注释解析,一定要看注释,是非常重要的一部分。

直接启动流程源码讲解

一、Activity访问AMS的过程
图解
Service启动流程-1.png
源码解析
  1. 我们在Activity中调用startService方法,其实是调用ContentWrapper的startService,因为Activity是继承自ContextWrapper的。startService是Context的抽象方法,ContentWrapper继承自Content,但是他运行了装饰者模式,本身没有真正实现Context,他的内部有一个真正的实例mBase,他是ContextImpl的实例,所以最终的真正实现是在ContextImpl中。

    /frameworks/base/core/java/android/content/ContextWrapper.java
    
    // 这里的mBase是startService的真正实现。他是从哪里来的?从activit的创建开始看
    Context mBase;
    public ComponentName startService(Intent service) {
         
        return mBase.startService(service);
    }
    

    想要知道什么时候得到Context,要回到ActivityThread创建Activity的时候创建的Context。最终可以看到就是ContextImpl这个类是具体的实现。

    /frameworks/base/core/java/android/app/ActivityThread.java
    
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
          
    // 在这里创建了Content,可以看到他的类型是ContentImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    ...
    // 在这里把appContext给到了activity。说明这个就是Activity里面的Context的具体实现
    appContext.setOuterContext(activity);
    activity.attach(appContext, this, getInstrumentation(), r.token,
          r.ident, app, r.intent, r.activityInfo, title, r.parent,
          r.embeddedID, r.lastNonConfigurationInstances, config,
          r.referrer, r.voiceInteractor, window, r.configCallback,
          r.assistToken);
    ...
    }
    
  2. ContextImpl的处理逻辑是直接调用AMS在本地的代理对象IActivityManagerService来请求AMS。

    /frameworks/base/core/java/android/app/ContextImpl.java;
    
    // 直接跳转下个方法
    public ComponentName startService(Intent service) {
         
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
         
        // 如果了解过activity启动流程看到这个ActivityManager.getService()应该很熟悉
        // 这里直接调用AMS在本地的IBinder接口,进行跨进程通信。不了解的可以去看一下我的
        // 关于解析activity启动流程的文章。链接在上头。
        ComponentName cn = ActivityManager.getService().startService(
        mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                    getContentResolver()), requireForeground,
                    getOpPackageName(), user.getIdentifier());
        
    }
    

    这里再次简单介绍一下这个IActivityManager。这一步是通过AIDL技术进行跨进行通信。拿到AMS的代理对象,把启动任务交给了AMS。

    /frameworks/base/core/java/android/app/ActivityManager.java/;
    //单例类
    public static IActivityManager getService() {
          
    	return IActivityManagerSingleton.get();
    }
    
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
          
    
    protected IActivityManager create() {
          
    //得到AMS的IBinder接口
    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
    //转化成IActivityManager对象。远程服务实现了这个接口,所以可以直接调用这个
    //AMS代理对象的接口方法来请求AMS。这里采用的技术是AIDL
    final IActivityManager am = IActivityManager.Stub.asInterface(b);
    return am;
    }
    };
    
二、AMS处理服务启动请求的过程
图解
Service启动流程-2.png
源码解析
  1. AMS调用ActivityServices进行处理。有点类似activity启动中的调用ActivityStarter进行启动。把业务分给他的小弟。

    /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
         
        final ActiveServices mServices;
        ...
        // 这里的mServices是ActivityServices,跳转->
        res = mServices.startServiceLocked(caller, service,
                            resolvedType, callingPid, callingUid,
                            requireForeground, callingPackage, userId);
        ...
    }
    
  2. ActivityServices类是整个服务创建过程的主要逻辑类。基本上AMS接收到请求后就把事务直接交给ActivityServices来处理了,所以整个流程都在这里进行。ActivityServices主要是获取Service的信息ServiceRecord,然后判断各种信息如权限,进程是否创建等。然后再创建一个StartItem,这个StartItem是后续执行onStartCommand的事务。这里要注意一下他。

    /frameworks/base/services/core/java/com/android/server/am/ActiveServices.java;
    // 方法中涉及到mAm就是AMS
    final ActivityManagerService mAm;
    
    // 直接跳下个方法
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
         
        // 跳转下个方法
        return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
                callingPackage, userId, false);
    }
    
    // 这一步主要是获取ServiceRecord。也就是服务的各种信息,来检查服务的各个资源是否已经完备
    // 同时会新建一个StartItem,这个Item是后面负责回调Service的onStartCommand事务
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
            final int userId, boolean allowBackgroundActivityStarts)
            throws TransactionTooLargeException {
         
        ...
        // 这里会去查找ServiceRecord,和ActivityRecord差不多,描述Service的信息。
        // 如果没有找到则会调用PackageManagerService去获取service信息
        // 并封装到ServiceLookResult中返回
        ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
        if (res == null) {
         
            return null;
        }
        if (res.record == null) {
         
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }
    	// 这里从ServiceLookResult中得到Record信息
        ServiceRecord r = res.record;    
        
        // 这里新建一个StartItem,注意他的第二个参数是false,这个参数名是taskRemove
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                    service, neededGrants, callingUid));
        ...
        // 继续跳转
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }
    
  3. 这一步主要是检查Service所在进程是否已经启动。如果没有则先启动进程。

    /frameworks/base/services/core/java/com/android/server/am/ActiveServices.java;
    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
         
        ...
        // 继续跳转
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        if (error != null) {
         
            return new ComponentName("!!", error);
        }
        ...
    }
    
    // 此方法主要判断服务所在的进程是否已经启动,如果还没启动那么就去创建进程再启动服务;
    // 第一次绑定或者直接启动都会调用此方法
    // 进程启动完了就调用realStartServiceLocked下一步
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
         
        ...
        // 这里获取进程名字。在启动的时候可以指定进程,默认是当前进程
        final String procName = r.processName;
        HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);    
        ProcessRecord app;
        if (!isolated) {
         
            // 根据进程名字获取ProcessRecord
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            ...
            // 这里判断进程是否为空。如果进程已经启动那就直接转注释1;否则转注释2
            // 创建进程的流程这里不讲,所以后面的逻辑直接进入启动service
            if (app != null && app.thread != null) {
         
               ...
                // 1
                // 启动服务
                realStartServiceLocked(r, app, execInFg);
                return null;
            }
            ...
        }
        // 如果进程为空,那么执行注释2进行创建进程,再去启动服务
        if (app == null && !permissionsReviewRequired) {
         
            // 2
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true<
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值