深入Android系统(十一)AMS-3-Service和ContentProvider管理

本文深入剖析了Android系统中Service和ContentProvider的管理机制。详细介绍了Service的生命周期、启动过程及AMS中的管理类ActiveServices。同时,对ContentProvider的使用、获取及安装流程进行了全面解读。

Service 管理

官方对Service的描述是:

Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信IPC

Service使用上,应用需要在AndroidManifest.xml文件中通过标签<service/>来声明一个Service,标签的属性如下:

<service android:description="string resource"
         android:directBootAware=["true" | "false"]
         android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:foregroundServiceType=["connectedDevice" | "dataSync" |
                                        "location" | "mediaPlayback" | "mediaProjection" |
                                        "phoneCall"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string" >
    . . .
</service>

<service/>标签属性比较简单,大部分和前面介绍的<application/>的属性重复,关于标签的描述大家可以参考官网:<service/>标签

我们重点记录下特殊的几个属性:

  • android:directBootAware:服务是否支持直接启动,即其是否可以在用户解锁设备之前运行
    • 在直接启动期间,应用中的服务仅可访问存储在设备保护存储区的数据
  • android:process:指定将运行服务的进程的名称。
    • 正常情况下,应用的所有组件都会在为应用创建的默认进程中运行。该名称与应用软件包的名称相同。
    • <application>元素的process属性可为所有组件设置不同的默认进程名称
    • 不过,组件可以使用自己的process属性替换默认值,将应用散布到多个进程中
    • 如果为此属性分配的名称以冒号(:)开头,则系统会在需要时创建应用专用的新进程,并且服务会在该进程中运行
    • 如果进程名称以小写字符开头,则服务将在使用该名称的全局进程中运行,前提是它拥有相应的权限。如此一来,不同应用中的组件便可共享进程,从而减少资源使用。
  • android:isolatedProcess:如果设置为true,则此服务将在与系统其余部分隔离的特殊进程下运行。

都是创建新的进程,那么android:processandroid:isolatedProcess的区别是什么呢?

  • 当我们仅仅指定android:process属性时,服务启动后进程信息如下:
        u0_a50        7889  3220 1114656  94464 0                   0 S lee.hua.skills_android
        u0_a50        8358  3220 1078100  58700 0                   0 S lee.hua.skills_android:newProcess
    
    • 创建了新的进程,不过user是一样的,都是u0_a50
    • Android在新进程的权限和接口调用方面未做限制
  • 当我们仅仅指定android:isolatedProcess属性时,服务启动后进程信息如下:
        u0_a50        7889  3220 1114656  94464 0                   0 S lee.hua.skills_android
        u0_i0         8358  3220 1078100  58700 0                   0 S lee.hua.skills_android:newProcess
    
    • 首先,user不同了,查看uid分别是:(u0_a50)10050(u0_i0)99000
    • Android中定义的isolated进程的起始号为99000
    • Androidisolated进程的接口调用做了一些限制
      • 比如调用bindService()的进程如果是isolated进程的话会抛出安全异常

后面Service的启动代码中会涉及到isolated相关的逻辑,我们先看下Service的生命周期

Service的生命周期

Activity类似,Service也有声明周期,但是和Activity的声明周期相比,Service的要简单许多。

官方图示如下:
image

  • 图片左侧显示的是使用startService()创建的服务的生命周期
  • 图片右侧显示的是使用bindService()创建的服务的生命周期

Service生命周期(从创建到销毁)可遵循以下任一路径:

  • startService()

    • 该服务在其他组件调用startService()时创建,然后无限期运行,且必须通过调用stopSelf()来自行停止运行。
    • 此外,其他组件也可通过调用stopService() 来停止此服务。服务停止后,系统会将其销毁。
  • bindService()

    • 该服务在其他组件(客户端)调用bindService()时创建。然后,客户端通过IBinder接口与服务进行通信。
    • 客户端可通过调用unbindService() 关闭连接。
    • 多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。(服务不必自行停止运行。)

Service 有两种运行模式

  • 前台模式:前台服务执行一些用户能注意到的操作,前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。
  • 后台模式:后台服务执行用户不会直接注意到的操作,在API 26或更高版本,当应用本身未在前台运行时,系统会对运行后台服务施加限制

关于生命周期的回调函数就不细讲了,比较简单,大家可以参考官网:Service基础知识

而在ActivityThread的成员变量mServices中保存了应用中所有Service对象,定义如下:

   final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();

对于Service类来说,重要的成员变量如下:

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
   
   
    // 引用 ActivityThread 对象
    private ActivityThread mThread = null;
    // Service 的类名
    private String mClassName = null;
    // service 的唯一标识符
    private IBinder mToken = null;
    // 引用 Application
    private Application mApplication = null; 
}

Service的管理类

AMS 中对于Service的管理是通过ActiveServices类来进行的。ActiveServices类的构造方法如下:

    public ActiveServices(ActivityManagerService service) {
   
   
        mAm = service;
        int maxBg = 0;
        try {
   
   
            maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
        } catch(RuntimeException e) {
   
   
        }
        mMaxStartingBackground = maxBg > 0
                ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
    }

构造方法很简单,只是从系统属性ro.config.max_starting_bg中读取了允许后台允许的Service的最大数量。

ActiveServices类中还有一些重要的成员变量,如下所示:

    // How long we wait for a service to finish executing.
    static final int SERVICE_TIMEOUT = 20*1000;
    // How long we wait for a service to finish executing.
    static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
    // How long the startForegroundService() grace period is to get around to
    // calling startForeground() before we ANR + stop it.
    static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

    final ActivityManagerService mAm;

    // 系统运行运行的最大Service数量
    final int mMaxStartingBackground;

    final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();
    final ArrayList<ServiceRecord> mPendingServices = new ArrayList<>();
    final ArrayList<ServiceRecord> mRestartingServices = new ArrayList<>();
    final ArrayList<ServiceRecord> mDestroyingServices = new ArrayList<>();
  • mServiceMap是一个SparseArray类型的数组,索引是用户Id
    • 不同用户的Service记录存放在数组元素中,每个元素是一个ServiceMap
    • ServiceMap中存储了某个用户所有的ServiceRecord对象
    • 应用进程中的ServiceAMS中对应的数据结构就是ServiceRecord
  • mServiceConnections储存的是所有连接记录ConnectionRecord的对象
    • 连接记录指的是某个进程绑定Service时传递的信息
    • mServiceConnections记录了AMS和使用服务的进程之间的联系
  • mPendingServices保存的是正在等待进程启动的Service
    • 当启动一个服务时,服务所在的进程可能还没有启动
    • 这时AMS会先去启动服务所在的进程,这个时间可能比较长
    • 因此,先把Service的信息保存在mPendingServices
  • mRestartingServices保存的是正在等待重新启动进程的Service
    • 如果Service所在的进程崩溃了,会把该Service加入到mRestartingServices中,准备重新启动进程
  • mDestroyingServices保存的是正在等待进程销毁的Service
    • 销毁进程也需要一段时间,因此在完成销毁前,先把Service保存在mDestroyingServices

Service 的启动过程

Context提供了两个接口来启动Service,分别是startService()bindService()。定义如下:

   public abstract ComponentName startService(Intent service);
   public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);

从接口定义可以看出,bindService()需要传递一个回调接口ServiceConnection来接收Binder对象。

这两个接口最后调用的是AMSstartService()bindService(),两个方法的实现差不多,bindService()更复杂一些。

后面以bindService()方法为例进行分析,在此之前,我们先看下启动过程的时序图:
image

AMSbindService()的代码如下:

    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
   
   
        // 检查调用进程是否是isolate Process,如果是抛出安全异常
        enforceNotIsolatedCaller("bindService");
        // 检查启动Service的Intent中是否包含文件描述符
        if (service != null && service.hasFileDescriptors() == true) {
   
   
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        // 检查 callingPackage 是否为空
        if (callingPackage == null) {
   
   
            throw new IllegalArgumentException("callingPackage cannot be null");
        }
        synchronized(this) {
   
   
            // 调用 ActiveService 类的 bindServiceLocked 方法
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }

ActiveService.bindServiceLocked

ActiveService类的bindServiceLocked()方法如下:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, final IServiceConnection connection, int flags,
        String callingPackage, final int userId) throws TransactionTooLargeException {
   
   
    ......// 省略一些权限和错误检查
    // 创建 ServiceRecord 对象
    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                Binder.getCallingUid(), userId, true, callerFg, isBindExternal, allowInstant);
    ......// 省略 res 的异常检查
    ServiceRecord s = res.record;
    ......
    try {
   
   
        // 将 service 从 mRestartingServices 列表中移除
        if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) {
   
   
            ......
        }
        ......
        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
   
   
            s.lastActivity = SystemClock.uptimeMillis();
            // 通过 bringUpServiceLocked 启动 Service 所在的进程
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                    permissionsReviewRequired) != null) {
   
   
                // 请注意,这里做了退出操作
                return 0;
            }
        }
        if (s.app != null) {
   
   
            ......
            // 调整进程的优先级
            mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
                    || s.app.treatLikeActivity, b.client);
            mAm.updateOomAdjLocked(s.app, true);
        }
        if (s.app != null && b.intent.received) {
   
   
            // 如果进程已经启动并且绑定过了,直接调用 IServiceConnection 的 connected()方法
            // 此方法最终会调用 ServiceConnection 的 onServiceConnected() 方法
            try {
   
   
                c.conn.connected(s.name, b.intent.binder, false);
            } catch (Exception e) {
   
   
                ......
            }
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
   
   
                // Intent 参数中要求重新绑定的情况,执行 requestServiceBindingLocked()
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
   
   
            // 还没有进行绑定,执行 requestServiceBindingLocked() 方法
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }
        ......
    }
    return 1;
}

bindServiceLocked()方法中:

  • 如果Service所在的进程还没有启动,会执行bringUpServiceLocked()来启动进程
  • 如果所在进程已经启动,那么只需要调用requestServiceBindingLocked()方法来绑定服务

ActiveService.bringUpServiceLocked

bringUpServiceLocked()方法的主要作用是启动Service,核心代码如下:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequi
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值