Android 7.0 ActivityManagerService(6) Service相关流程分析

本文深入探讨Android 7.0中Service的生命周期,包括Unbounded Service和Bounded Service的启动、结束流程,涉及AMS、ContextImpl和ApplicationThread的角色。通过源码分析,揭示Service如何注册、启动、停止以及与客户端交互的过程,帮助开发者理解Service的工作原理。
摘要由CSDN通过智能技术生成

本篇博客旨在分析Android中Service相关的源码流程。

一、基础知识
Service通常被称之为“后台服务”,具体是指其本身的运行并不依赖于用户可视的UI界面。
例如:点击界面的音乐播放键,由Service进行实际的音乐播放工作。即使用户离开此界面,音乐仍能够继续播放。

不过Service与Activity等相同,都是运行于当前进程的主线程中。
因此一些耗时操作一般并没有放在Service中进行,而是在Service中启动一个工作线程来进行实际的操作。

1 Service的注册
在定义Service时,需要在AndroidManifest.xml中进行声明。
只有这样,PKMS在初始化时,才能通过解析AndroidManifest.xml,得到该Service的信息。
在客户端实际使用Service时,将申请服务的请求发往AMS,后者将通过PKMS提供的信息,检索到具体的Service。

Service在AndroidManifest.xml中的声明,语法格式如下所示:

<service android:enabled=["true" | "false"]
    android:exported[="true" | "false"]
    android:icon="drawable resource"
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string">
    <intent-filter >
        <action android:name="com.android.server.TestService.TestAction" />
    </intent-filter>
</service>

可以看出,Service相关的标签,与android中其它组件基本相同,此处不作赘述。

2 启动方式
一般而言,从Service的启动方式上,可以将Service分为Unbounded Service和Bounded Service。

2.1 Unbounded Service
Unbounded Service是指:客户端调用ContextImpl的startService等函数启动的Service。
这种启动方式的核心代码片段如下:

..............
Intent serviceIntent = new Intent(mContext, TestService.class);
serviceIntent.putExtra("Args", mArgs);
mContext.startService(serviceIntent);
..............

从上面的代码可以看出,客户端组件通过调用startService函数,向Service端发送一个Intent,该Intent中可以携带具体的业务请求信息。
Service启动后,会根据Intent中的请求,执行实际的业务。

通常情况下,Service启动、处理完请求后,并不会返回启动结果(除非在业务逻辑中主动进行通知)。
即使启动Service的应用组件已经被销毁了,服务将一直在后台运行。

因此,当客户端不再需要使用Service的服务时,需要主动调用ContextImpl的stopService函数。
此外,从在Service内部,也可以在执行完具体的业务后,通过stopSelf函数停止其本身。

Unbounded Service的基本实现方式如下所示:

public class TestService extends Service {
   
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        ...........
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        ..............
        return START_STICKY;
    }

     @Override
     public void onDestroy() {
         super.onDestroy();
        ..........
    }
}

其中,onBind函数是Service基类中的唯一抽象方法,子类必须实现。对于Unbounded Service而言,此函数直接返回 null 即可。
onCreate、onStartCommand和onDestroy都是Unbounded Service相应生命周期阶段的回调函数。

当客户端调用startService启动服务时,AMS收到请求信息后,将判断对应Service是否启动过。
如果Service还未启动,将首先回调Service的onCreate函数,然后再执行onStartCommand函数。
当客户端再次调用startService时,AMS判断Service已经启动,将只调用Service的onStartCommand函数。

在Service的onStartCommand函数中,将根据Intent的信息进行实际的业务处理。
注意到onStartCommand函数会返回一个Int型的值,该值与系统的进程管理有关,主要的取值范围如下:
START_STICKY:
当Service因为内存不足而被系统kill后,在接下来的某个时间内,当系统内存足够可用的情况下,系统将会尝试重新创建此Service;
一旦创建成功后,将回调onStartCommand方法,但一般情况下,参数中的Intent将是null。

START_NOT_STICKY:
当Service因为内存不足而被系统kill后,在接下来的某个时间内,即使系统内存足够可用,系统也不会尝试重新创建此Service。

START_REDELIVER_INTENT:
与START_STICKY相同,当Service因为内存不足而被系统kill后,在接下来的某个时间内,当系统内存足够可用的情况下,系统将会尝试重新创建此Service;唯一不同的是,Service创建成功后,回调onStartCommand方法时,传入的参数将是最后一次调用startService时使用的intent。

需要注意的是,只有系统kill掉Service时上述返回值才有意义,如果是人为地kill掉Service进程,系统不会按照onStartCommand的返回值重启Service。

最后,客户端无论调用多少次startService,只需要一次stopService即可将此Service终止(毕竟onCreate函数也之调用过一次),此时AMS将回调Service的onDestroy函数。

2.2 Bounded Service
Bounded Service一般使用过程如下:
1、服务端定义继承于基类Service的服务,并重写其onBind方法。
在此方法中,需要返回具体的Binder对象供客户端使用。

2、客户端通过实现ServiceConnection接口,自定义ServiceConnection对象,
在其中实现onServiceConnected函数,用于回调获取服务端提供的Binder对象。
同时,客户端也可以选择性地实现onServiceDisconnected接口,以便在与Service解绑时执行一些操作。

3、客户端调用bindService函数与Service端绑定,该函数的参数中包含服务对应的Intent和自定义的ServiceConnection对象。
与Unbounded Service一样,若Service未启动,AMS将负责启动Service,然后才会调用Service的onBind方法,得到服务端返回Binder对象。
一旦得到Binder对象后,AMS将回调ServiceConnection中实现的onServiceConnected函数,将Binder对象返回给客户端。

4、客户端得到Binder对象后,就可以通过Binder对象调用Service提供的公有函数,完成实际的业务请求。
Service端收到业务请求后,就可以执行对应的处理。

5、当客户端不再需要使用服务时,就需要通过调用unbindService函数,解除与Service的绑定。
若客户端实现了ServiceConnection的onServiceDisconnected接口,那么与Service解绑后,onServiceDisconnected将被AMS回调。

网上使用Bounded Service的代码示例较多,此处就不再具体给出,可以参考这篇博客Android总结篇系列:Android Service
我们仅看看源码中的使用方式,例如在packages/services/mms/src/com/android/mms/service/SendRequest.java中:

/**
* Sends the MMS through through the carrier app.
*/
private final class CarrierSendManager extends CarrierMessagingServiceManager {
   
    ........
    void sendMms(Context context, String carrierMessagingServicePackage,
            CarrierSendCompleteCallback carrierSendCompleteCallback) {
        mCarrierSendCompleteCallback = carrierSendCompleteCallback;
        //调用父类CarrierMessagingServiceManager的bindToCarrierMessagingService函数
        if (bindToCarrierMessagingService(context, carrierMessagingServicePackage)) {
            .............
        } else {
            ..............
        }
    }
}

看看bindToCarrierMessagingService函数:

public boolean bindToCarrierMessagingService(Context context, String carrierPackageName) {
    .........
    //显示指定服务的action和package
    Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
    intent.setPackage(carrierPackageName);
    //客户端定义自己的ServiceConnection
    mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();

    //作为客户段绑定服务端
    return context.bindService(intent, mCarrierMessagingServiceConnection,
            Context.BIND_AUTO_CREATE);
}

根据IntentAMS将绑定CarrierMessagingService,其onBind函数如下:

public @Nullable IBinder onBind(@NonNull Intent intent) {
    if (!SERVICE_INTERFACE.equals(intent.getAction())) {
        return null;
    }
    //mWrapper的类型为ICarrierMessagingWrapper,继承ICarrierMessagingService.Stub
    //即以AIDL的方式返回Binder对象
    return mWrapper;
}

最后看看客户端CarrierMessagingServiceConnection的onServiceConnected函数:

public void onServiceConnected(ComponentName name, IBinder service) {
    //按照AIDL的规则,得到Binder对象
    //之后,客户端就可以按照AIDL定义的接口,调用服务端的接口
    onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
}

3 生命周期
这里写图片描述

两种Service生命周期对应的回调函数,可以用上面这张比较经典的图来表示,其中:
Unbounded Service回调函数的顺序依次为:onCreate、onStartCommand、onDestroy;
Bounded Service回调函数的顺序依次为:onCreate、onBind、onUnbind、onDestroy。
后文分析源码时,可以看到具体的调用过程,此处不做赘述。

4 显示启动和隐式启动
最后谈一下Service的显示启动和隐式启动。

4.1 显示启动
显示启动是指:
客户端将service的具体名称写入到Intent中,此时AMS就可以直接根据名称来找到对应的service。

代码示例:

//1、startService显示启动服务
...........
Intent startIntent = new Intent();
ComponentName componentName = new ComponentName(
        "com.android.server",
        "com.android.server.TestService");
startIntent.setComponent(componentName);
mContext.startService(startIntent);
............

可以看到显示启动服务,就是在Intent中指定ComponentName。
ComponentName由具体的包名和类名组成。

如果启动的service与当前组件在同一个包内,也可以使用下述方式显示启动service:

//2、startService显示启动同一个包中的服务
...........
Intent startIntent = new Intent(mContext, TestService.class);
mContext.startService(startIntent);
............

同样,bindService也可以显示启动服务,对应的写法如下:

//3、bindService显示启动服务
............
Intent bindIntent = new Intent();
ComponentName componentName = new ComponentName(
        "com.android.server",
        "com.android.server.TestService");
bindIntent.setComponent(componentName);
mContext.bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
............

4.2 隐式启动
隐示启动是指:
客户端通过在intent中添加service申明的Action,此时AMS通过Action来匹配具体的service。
这种启动方式要求,service在定义Action时,要尽可能的和其它服务区分开来。
通常来说,Action将被命名为具体包名、类名和Action名的组合。

代码示例:

............
Intent startIntent = new Intent();
startIntent.setAction("com.android.server.TestService.TestAction");
mContext.startService(startIntent);
............

需要注意的是:Android 5.0以后,只能以显示方式来启动Service,隐式启动服务将抛出异常。

接下来,我们分析一下这两种类型的服务对应的源码流程。

二、Unbounded Service的启动流程

1 ContextImpl中的startService
我们从ContextImpl中的startService函数入手,看看Unbounded Service的启动流程。

public ComponentName startService(Intent service) {
    .............
    return startServiceCommon(service, mUser);
}

private ComponentName startServiceCommon(Intent service, UserHandle user) {
    try {
        //判断Intent中是否符合要求
        validateServiceIntent(service);

        //Intent可能跨进程传输,因此先要做一下安全性检查
        service.prepareToLeaveProcess(this);

        //通过AMS的接口拉起服务
        ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), getOpPackageName(), user.getIdentifier());
        ...............
        return cn;
    } catch (RemoteException e) {
        ................
    }
}

从上面的代码可以看出,拉起服务还是要依赖于AMS的接口。
在这里,我们进一步看看validateServiceIntent函数:

private void validateServiceIntent(Intent service) {
    //Intent中既没有指定Component,也没有指定Package,即以隐式启动的方式拉起服务
    if (service.getComponent() == null && service.getPackage() == null) {
        //当版本大于Android L,即Android 5.0时,会抛出异常
        if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
            IllegalArgumentException ex = new IllegalArgumentException(
                    "Service Intent must be explicit: " + service);
            throw ex;
        } else {
            //低版本只是打印log而已
            Log.w(TAG, "Implicit intents with startService are not safe: " + service
                    + " " + Debug.getCallers(2, 3));
        }
    }
}

validateServiceIntent就是上文提到的,Android 5.0后不再支持隐式启动Service的原因。

2 AMS中的startService
接下来,我们看看AMS中的startService函数:

public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, String callingPackage, int userId)
        throws TransactionTooLargeException {
    //参数有效性检查
    .................
    synchronized(this) {
        ..........
        //mServices在AMS初始化时得到,类型为ActiveServices
        ComponentName res = mServices.startServiceLocked(caller, service,
                resolvedType, callingPid, callingUid, callingPackage, userId);
        ..........
        return res;
    }
}

2.1 ActiveServices中的startServiceLocked
ActiveServices在AMS初始化时创建,用于管理AMS启动的Service。
上述代码进行参数有效性检查后,将调用ActiveServices的startServiceLocked函数。
我们分段看看startServiceLocked函数:

2.1.1 startServiceLocked Part-I

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    ................
    final boolean callerFg;
    if (caller != null) {
        //通过AMS得到调用方的进程信息
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        ................
        //判断调用方是否属于前台进程
        callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
    } else {
        callerFg = true;
    }

    //检索待启动的Service
    ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);
    ..................
    //从ServiceLookupResult中取出ServiceRecord
    ServiceRecord r = res.record;
    .................

startServiceLocked函数的第一部分,主要是利用参数信息检索出待启动的Service,这部分工作主要由retrieveServiceLocked函数完成。
由于retrieveServiceLocked使用的频率较高(后文还会遇到),同时涉及到ActivesService管理Service的一些数据结构,
因此这里深入分析一下该函数:

private ServiceLookupResult retrieveServiceLocked(.......) {
    ServiceRecord r = null;
    ..........
    //得到当前用户的UserId
    userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
            ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);

    //每个UserId有对应的ServiceMap,统一保存在ActiveServices中
    ServiceMap smap = getServiceMap(userId);

    //对于显示启动
    final ComponentName comp = service.getComponent();
    if (comp != null) {
        //根据ComponentName从ServiceMap中取出对应的ServiceRecord
        r = smap.mServicesByName.get(comp);
    }

    //对于隐式启动
    if (r == null && !isBindExternal) {
        Intent.FilterComparison filter = new Intent.FilterComparison(service);
        //根据Intent对应的Filter,从ServiceMap中取出匹配的ServiceRecord
        r = smap.mServicesByIntent.get(filter);
    }

    //特殊情况的处理
    //对于包含FLAG_EXTERNAL_SERVICE的service,将运行于调用方进程中
    //对于这种特殊服务,
    //如果根据Component或Filter找到了一个正在运行的Service,但其运行进程与当前调用进程不一致
    //那么必须重新在调用进程中创建该ServiceRecord,于是将r置为null
    if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
            && !callingPackage.equals(r.packageName)) {
        // If an external service is running within its own package, other packages
        // should not bind to that instance.
        r = null;
    }

    //以上均是在缓存信息中,查找ServiceRecord
    //如果查询不到,则必须通过PKMS进行查找
    if (r == null) {
        try {
            //PKMS根据参数得到对应Pkg中Serivce的ResolveInfo
            ResolveInfo rInfo = AppGlobals.getPackageManager().resolveService(service,
                    resolvedType, ActivityManagerService.STOCK_PM_FLAGS
                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                    userId);

            //从ResolveInfo中取出ServiceInfo
            ServiceInfo sInfo =
                    rInfo != null ? rInfo.serviceInfo : null;

            //构造出Service对应的ComponentName
            ComponentName name = new ComponentName(
                    sInfo.applicationInfo.packageName, sInfo.name);

            //特殊情况处理
            if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
                if (isBindExternal) {
                    ..............
                    // Run the service under the calling package's application.
                    //FLAG_EXTERNAL_SERVICE将运行在调用方进程中,此处就是修改PKMS检索出的ServiceInfo
                    //先得到调用方的应用信息
                    ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
                            callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
                    ............
                    //将ServiceInfo中的信息,改为调用方应用的信息
                    sInfo = new ServiceInfo(sInfo);
                    sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
                    sInfo.applicationInfo.packageName = aInfo.packageName;
                    sInfo.applicationInfo.uid = aInfo.uid;
                    name = new ComponentName(aInfo.packageName, name.getClassName());
                    service.setComponent(name);
                } else {
                    //抛出异常
                    .....
                }
            } else if(isBindExternal) {
                //抛出异常
                ............
            }

            //多用户的处理
            if (userId > 0) {
                //检查服务是否为单例且可被调用的
                if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                        sInfo.name, sInfo.flags)
                        && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
                    //对于多用户而言,每个用户启动的服务,运行于对应用户所在进程组中
                    //但如果待启动服务为单例的,那么该服务还是得运行在系统用户的进程组中
                    //于是此次将userId置为0
                    userId = 0;

                    //ServiceMap都被调整为系统用户对应的
                    smap = getServiceMap(0);
                }
                sInfo = new ServiceInfo(sInfo);
                //此处使用了userId
                sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
            }

            r = smap.mServicesByName.get(name);
            if (r == null && createIfNeeded) {
                ..............
                //创建出对应的ServiceRecord
                r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
                ..............
                //保存到ServiceMap中
                smap.mServicesByName.put(name, r);
                smap.mServicesByIntent.put(filter, r);
            }
        } catch(RemoteException ex) {
            ...........
        }
    }

    if (r != null) {
        //进行一些权限检查和有效性检查
        .................

        //没有问题时,返回正常结果
        return new ServiceLookupResult(r, null);
    }
    return null;
}

retrieveServiceLocked函数看起来比较长,但逻辑还是比较清晰的,最主要的步骤是:
1、从ActiveServices保存的一些服务对应的记录信息中,找出ServiceRecord;
2、如果没有找到ServiceRecord,就通过PKMS找出对应的Service信息,然后重新构建出ServiceRecord;
3、将ServiceRecord保存到ActiveServices的数据结构中。

其中,由于考虑到了带有FLAG_EXTERNAL_SERVICE的Service及单例Service的情况,
因此单独进行了处理,在了解整体逻辑后,单独看这些特殊处理还是比较好理解的。

ActiveServices中,保存ServiceRecord涉及的数据结构主要如下图所示:

2.1.2 startServiceLocked Part-II

接下来我们继续看看startServiceLocked下一部分的内容:

    //进行一些检查工作
    .............

    //如果这个服务在重启列表中,清空对应的信息
    if (unscheduleServiceRestartLocked(r, callingUid, false)) {
        .................
    }

    r.lastActivity = SystemClock.uptimeMillis();
    r.startRequested = true;
    r.delayedStop = false;

    //startService可以多次向Service传递信息,每次的信息都是一个StartItem,对应着一个StartId
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants));
    ............
    //addToStarting决定是否将待启动的Service
    //加入到ActiveServices维护的mStartingBackground队列
    boolean addToStarting = false;

    //如果启动服务的不是前台进程
    //同时服务对应的ServiceRecord中没有记录对应进程的信息(即初次使用)
    if (!callerFg && r.app == null
            //并且user已经启动过其它进程
            && mAm.mUserController.hasStartedUserState(r.userId)) {

        //通过AMS查询Service对应的进程信息
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);

        //若Service对应的进程未启动,或优先级过低,则有可能需要延迟启动服务
        if (proc ==
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值