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

1,774 阅读15分钟

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 permissionsReviewRequired)
        throws TransactionTooLargeException {
    ......
    // 判断是否要在进程隔离下运行
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    String hostingType = "service";
    ProcessRecord app;
    if (!isolated) {
        // 非 isolated 进程
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        if (app != null && app.thread != null) {
            // 如果进程已经启动,执行 realStartServiceLocked()
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            } ...
        }
    } else {
        // isolated 进程,isolatedProc 第一次为 null
        app = r.isolatedProc;
        ......
    }
    // 如果进程还没有启动,执行 startProcessLocked() 启动进程
    if (app == null && !permissionsReviewRequired) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                hostingType, r.name, false, isolated, false)) == null) {
            ......
            // 进程启动失败,返回异常信息
            return msg;
        }
        if (isolated) {
            // 给 isolatedProc 赋值
            r.isolatedProc = app;
        }
    }
    ......
    if (!mPendingServices.contains(r)) {
        // 将 Service 加入 mPendingServices
        mPendingServices.add(r);
    }
    ......
    return null;
}

调用bringUpServiceLocked()方法时

  • 如果进程已经启动,而且isolated属性为false,那么调用realStartServiceLocked()进入到下一步
  • 如果进程还没有启动,调用AMSstartProcessLocked()来启动进程
  • 最后,为了进程启动结束后能继续运行该Service,将其加入到mPendingServices集合中

新启动进程在哪里调用的realStartServiceLocked()来启动Service的呢?
按照进程的启动逻辑,启动完会执行AMSattachApplicationLocked()方法,然后调用ActiveServicesattachApplicationLocked()方法,方法如下:

class ActivityThread{
    public static void main(String[] args) {
        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        ...
    }
    private void attach(boolean system, long startSeq) {
        ...
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread, startSeq);
        }
        ...
    }
}
class ActivityManagerService{
    public final void attachApplication(IApplicationThread thread, long startSeq) {
        synchronized (this) {
            ...
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            ...
        }
    }
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        ...
        // Find any services that should be running in this process...
        if (!badApp) {
            // 调用 ActiveService 的 attachApplicationLocked 方法
            didSomething |= mServices.attachApplicationLocked(app, processName);
        }
        ...
        return true;
    }
}
class ActiveService{
    boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException {
        boolean didSomething = false;
        if (mPendingServices.size() > 0) {
            ServiceRecord sr = null;
            for (int i=0; i<mPendingServices.size(); i++) {
                ...
                // 此处再一次执行了 realStartServiceLocked()
                realStartServiceLocked(sr, proc, sr.createdFromFg);
                ...
            }
        }
        ...
        return didSomething;
    }
}

这样,就和bringUpServiceLocked()的逻辑衔接起来了。

ActiveService.realStartServiceLocked

realStartServiceLocked()方法的调用流程:

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        ...
            // 调用 scheduleCreateService 在服务进程中创建 Service 对象
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
        ...
        // 要求服务进程绑定Binder服务
        requestServiceBindingsLocked(r, execInFg);
        ...
    }
    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                ...
                // 调用 scheduleBindService 绑定服务
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                ...
            } ...
        }
        return true;
    }

realStartServiceLocked()主要的工作就是调用两个方法,分别是

  • ApplicationThreadscheduleCreateService()
  • requestServiceBindingsLocked()方法
    • 该方法最终也调用了ApplicationThreadscheduleBindService()方法

前面介绍过,ApplicationThreadschedule*方法其实都是发送相应的处理消息到ActivityThread中,对应的实现方法分别为handleCreateService()handleBindService()这两个方法,我们来看下:

private void handleCreateService(CreateServiceData data) {
    ...
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        // 创建 Service 对象
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
    } catch (Exception e) {
        ...
    }
    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        ...
        // 调用 Service 的 onCreate() 方法
        service.onCreate();
        mServices.put(data.token, service);
        ...
    } catch (Exception e) {
        ...
    }
}
private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    // 调用 Service 的 onBind() 方法
                    IBinder binder = s.onBind(data.intent);
                    // 调用 AMS 的 publishService 来执行 onServiceConnected() 回调通知
                    ActivityManager.getService().publishService(
                            data.token, data.intent, binder);
                } else {
                    // 调用 Service 的 onRebind() 方法
                    s.onRebind(data.intent);
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
                ...
            }
        } catch (Exception e) {
            ...
        }
    }
}

上面的两个handle*方法把Service启动的声明周期函数都调用完成了。

需要注意的是AMSpublishService()方法,该方法会调用ActiveServicespublishServiceLocked()方法,实现如下:

class ActivityManagerService{
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        ...
        ConnectionRecord c = clist.get(i);
        // 执行 IServiceConnection 的 connected() 方法
        c.conn.connected(r.name, service, false);
        ...
    }
}
final class ConnectionRecord {
    ...
    // IServiceConnection.aidl 的服务类的实现在 LoadedApk 中
    final IServiceConnection conn;
}
class LoadedApk{
    private static class InnerConnection extends IServiceConnection.Stub {
        final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
        InnerConnection(LoadedApk.ServiceDispatcher sd) {
            mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
        }
        public void connected(ComponentName name, IBinder service, boolean dead)
                throws RemoteException {
                // 此处省略一些中间调用过程
                doConnected();
        }
    }
    public void doConnected(ComponentName name, IBinder service, boolean dead) {
        ...
        // 调用 onServiceConnected 方法通知 caller
        mConnection.onServiceConnected(name, service);
        ...
    }
}

到这里,bindService()的过程就结束了,总体而言,Service部分的启动还是比较简洁的,毕竟对Service来说只有启动和停止两种操作。

ContentProvider 管理

ContentProvider并没有生命周期,也没有状态的变化。

AMS中通过ContentProviderRecord类来记录ContentProvider信息,用ContentProviderConnection类来记录进程中连接ContentProvider的信息。

AMS的成员变量mProviderMap保存了系统中所有ContentProvider的记录。

同时在ProcessRecord中也通过成员变量pubProviders来记录进程中所有创建的ContentProvider,使用成员变量conProviders来记录进程中所有使用的ContentProvider。定义如下:

    // class (String) -> ContentProviderRecord
    final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
    // All ContentProviderRecord process is using
    final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();

理解ContentProvider

ContentProvider用来提供数据的统一访问方式,如果某个应用中有数据需要提供给其他应用访问,无论数据是存储在数据库中,还是储存在文件中,甚至是网络中,都可以通过ContentProvider提供的接口来访问。官方传送门:ContentProvider

ContentProvider的使用

创建一个ContentProvider组件只需要继承ContentProvider类即可。子类必须实现父类定义的用于数据增、删、改、查的抽象接口,分别是:

  • 插入接口:根据Uri插入ContentValues对象中的数据。原型如下:
    public abstract Uri insert (Uri uri,  ContentValues values)
    
  • 删除接口:根据Uri删除selection条件所匹配的全部记录。原型如下:
    public abstract int delete (Uri uri, String selection, String[] selectionArgs)
    
  • 更新接口:根据Uri修改selection条件所匹配的全部记录。原型如下:
    public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs)
    
  • 查询接口:根据Uri查询selection条件所匹配的全部记录,并可以指定排序方式。原型如下:
    public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    

上面的接口中都使用了Uri来表示数据,Uri前面已经介绍了,但ContentProvider不能接收任意的Uri,它指定了Uri的格式。

如下为一个简单示例:

content://android.launcher.settings.provider/app/3

这个Uri分为4部分:

  • 前缀:必须是content://
  • authorityandroid.launcher.settings.provider用来标识一个唯一的ContentProvider
    • 官方建议:如果您的Android软件包名称为com.example.<appname>ContentProvider的授权应该设置为com.example.<appname>.provider
  • pathapp表示数据的类型,由ContentProvider内部定义
    • content://com.example.app.provider/table1:匹配一个名为table1的表
    • content://com.example.app.provider/table2/dataset1:匹配一个名为dataset1的表
    • 也可以使用content://com.example.app.provider/*匹配ContentProvider中的任何内容
  • id:最后的代号3表示数据的第几项
    • content://com.example.app.provider/table3/6,会匹配table3中对应6所标识行的内容
    • 也可以使用通配符#,如content://com.example.app.provider/table3/#用来匹配任意id

关于Uri的详细部分可以参考官网:内容Uri模式

为了让其他应用可以访问ContentProvider,必须在AndroidManifest文件中使用<provider/>标签声明。<provider/>标签定义如下:

<provider android:authorities="list"
              android:directBootAware=["true" | "false"]
              android:enabled=["true" | "false"]
              android:exported=["true" | "false"]
              android:grantUriPermissions=["true" | "false"]
              android:icon="drawable resource"
              android:initOrder="integer"
              android:label="string resource"
              android:multiprocess=["true" | "false"]
              android:name="string"
              android:permission="string"
              android:process="string"
              android:readPermission="string"
              android:syncable=["true" | "false"]
              android:writePermission="string" >
        . . .
</provider>

官网的使用指南在这里

<provider/>标签的大部分属性和<application/>标签的属性相同,我们这里只简单介绍下<provider/>标签特有的一些属性:

  • authorities:鉴权字符串列表。每种鉴权字符串表示一种ContentProvider提供的数据。多个鉴权字符串之间用分号分割。
    • 为避免冲突,授权方名称应遵循Java 样式的命名惯例(如com.example.provider.cartoonprovider)。
    • 通常,它是实现提供程序的ContentProvider子类的名称。
    • 没有默认值。必须至少指定一个鉴权字符串。
  • grantUriPermissions:用来设置能否允许本来没有权限访问本组件数据的其他应用通过授权的方式访问。例如:
    • Email应用收到一个带有附件的邮件时,应用可能需要调用其他应用来浏览附件,通过授权的方式可以让没有权限的应用读取附件的数据
    • 授权是通过在启动组件的Intent中添加标记FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION实现
    • 如果希望打开授权功能,或者把这个属性设置为true,或者在<grant-uri-permission/>中定义相关权限
  • initOrder:指定ContentProvider的初始化顺序,数据类型为整型,数值越大越早创建
  • multiprocess:用来设置该ContentProvider能否在其他应用中创建。默认值为false
    • 如果设为true,则每个应用进程都有自己的内容提供程序对象。这样可以减少进程间通信的开销
    • 如果设为false,则应用的进程仅共享一个内容提供程序对象。

ContentProvider的定义

ContentProvider类的定义非常简单,除了几个保存属性值的成员变量外,最重要的就是下面代码中的mTransport变量:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ...
    private Transport mTransport = new Transport();
    ...
}

mTransport变量的类型是Transport,它是定义在ContentProvider中的一个内部类,继承关系图如下:
image

从继承关系就可以看出Transport其实是一个Binder服务类,它实现了IContentProvider接口。而其他组件对ContentProvider的操作就是通过Transport提供的接口来完成的

ActivityThread中管理ContentProvider的成员变量有4个,分别是:

    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
        = new ArrayMap<ProviderKey, ProviderClientRecord>();
    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
        = new ArrayMap<IBinder, ProviderRefCount>();
    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
        = new ArrayMap<IBinder, ProviderClientRecord>();
    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
            = new ArrayMap<ComponentName, ProviderClientRecord>();

这些变量的类型都是ArrayMap,它们储存的都是ProviderClientRecord的引用,对象ProviderClientRecord中保存了ContentProvider的信息

  • mProviderMap保存的是包括本地对象和引用对象在内的所有ContentProvider对象
  • mProviderRefCountMap中保存的是对其他进程中的ContentProvider对象的引用计数对象,其中保存着被引用对象的IContentProvider对象
  • mLocalProviders只保存本地的ContentProvider对象
  • mLocalProvidersByName保存的和mLocalProviders中的一样,只是可以通过ComponentName来查找ContentProvider对象

获取ContentProvider

通常应用中先通过ContextgetContentResolver()方法得到应用中的ContentResolver对象,然后再调用它的acquireProvider()方法来获取IContentProvider

acquireProvider()方法对于应用来说是隐藏的,对于普通应用来说可以使用acquireContentProviderClient()方法来获取ContentResolver对象,不过,最后都会调用到acquireProvider()方法。

ContentResolver是一个抽象类,真正的实现是ApplicationContentResolver类,这个类在ContextImpl.java中定义,acquireProvider()方法的实现也很简单,如下所示:

    private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        ...
        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
        ...
    }

acquireProvider()方法只是调用了ActivityThreadacquireProvider()方法:

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        // 先查询已创建的 ContentProvider 集合
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider; // 如果已存在,直接返回
        }
        ContentProviderHolder holder = null;
        try {
            synchronized (getGetProviderLock(auth, userId)) {
                // 调用 AMS 的 getContentProvider 得到一个 ContentProviderHolder 对象
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), auth, userId, stable);
            }
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        if (holder == null) {
            return null; // 获取失败直接返回
        }
        // 安装获得的 ContentProvider 对象
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

ActivityThreadacquireProvider()方法:

  • 先调用acquireExistingProvider(),这个方法将检查mProviderMap中是否已经创建了相应的ContentProvider对象
  • 如果集合中已经存在,直接返回该对象
  • 否则调用AMSgetContentProvider()方法来得到一个ContentProviderHolder对象
  • 最后,如果ContentProviderHolder对象不为null,执行installProvider()安装这个ContentProvider

我们看下AMSgetContentProvider()方法:

    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        // 确保不是 isolated 进程
        enforceNotIsolatedCaller("getContentProvider");
        if (caller == null) {
            String msg = "null IApplicationThread when getting content provider "
                    + name;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        return getContentProviderImpl(caller, name, null, stable, userId);
    }

AMSgetContentProvider()方法在简单检查参数后,调用了内部的getContentProviderImpl()方法:

    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;
        boolean providerRunning = false;
        synchronized(this) {
            long startTime = SystemClock.uptimeMillis();
            ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
                if (r == null) {
                    // 如果调用者的进程不存在,抛出异常
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                          + " (pid=" + Binder.getCallingPid()
                          + ") when getting content provider " + name);
                }
            }
            ...
            // 检查 ContentProvider 是否已经发布
            cpr = mProviderMap.getProviderByName(name, userId);
            ...
            if (providerRunning) {
                // ContentProvider 已经发布的情况
                cpi = cpr.info;
                String msg;
                // 权限检查
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                        != null) {
                    throw new SecurityException(msg);
                }
                if (r != null && cpr.canRunHere(r)) {
                    // ContentProvider 所在的进程已经存在,并且设置了可以在调用者的进程创建
                    // 创建ContentProviderHolder对象
                    ContentProviderHolder holder = cpr.newHolder(null);
                    // 并设置一个空的 provider,这样客户进程会重新创建一个 ContentProvider
                    holder.provider = null;
                    return holder;
                }
                ...
                // ContentProvider 已经存在,增加引用计数
                conn = incProviderCountLocked(r, cpr, token, stable);
                ... // 省略进程优先级调整部分
            }
            if (!providerRunning) {
                // ContentProvider 没有发布的情况
                try {
                    // 从 PMS 中获取 ContentProvider 信息
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                } catch (RemoteException ex) {
                }
                if (cpi == null) {
                    return null;
                }
                ...// 省略一些参数的处理
                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                // 先从 mProviderMap 根据 ComponentName 查找 ContentProvider
                cpr = mProviderMap.getProviderByClass(comp, userId);
                // 此时如果 ContentProvider 为 null,可以理解为第一次启动的情况
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    ...
                    try {
                        // 从 PMS 获取 ContentProvider 所在应用的信息
                        ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                        if (ai == null) {
                            Slog.w(TAG, "No package info for content provider "
                                    + cpi.name);
                            return null;
                        }
                        ai = getAppInfoForUser(ai, userId);
                        // 创建一个新的 ContentProviderRecord 对象
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } ...
                }
                if (r != null && cpr.canRunHere(r)) {
                    // 如果 ContentProviderRecord 能安装在调用者进程中,返回一个空的 ContentProvider
                    return cpr.newHolder(null);
                }
                // 检测请求的 ContentProvider 是否在 mLaunchingProviders集合中
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }
                // 如果请求的 ContentProvider 不在 mLaunchingProviders 集合中,启动它
                if (i >= N) {
                    try {
                        ...
                        // 获取 ContentProvider 所在进程的 ProcessRecord
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            // 如果对应的进程已经存在
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                // 将 ContentProvider 保存到 pubProviders 中
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    // 调用进程的方法来安装 ContentProvider
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            // 如果对应的进程没有启动,先启动进程
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
                        cpr.launchingApp = proc;
                        // 将 ContentProvider 添加到 mLaunchingProviders 集合中
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }
                if (firstClass) {
                    // 第一次启动,保存到 mProviderMap 中,key 为 ComponentName
                    mProviderMap.putProviderByClass(comp, cpr);
                }
                // 添加到 mProviderMap 中,key 为 String
                mProviderMap.putProviderByName(name, cpr);
                // 增加 ContentProvider 的引用计数
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    // 设置等待状态为 true
                    conn.waiting = true;
                }
            }
            ...
        }
        // 挂起调用者线程,直到 ContentProvider 安装完成
        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
        synchronized (cpr) {
            while (cpr.provider == null) {
                if (cpr.launchingApp == null) {
                    ...
                    return null;
                }
                try {
                    ...
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    // 挂起等待
                    cpr.wait(wait);
                    if (cpr.provider == null) {
                        ...
                        return null;
                    }
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;
    }

getContentProviderImpl()方法虽然有点长,但逻辑还是比较容易理解的,方法中分两种情况进行了处理:

  • 第一种是如果请求的ContentProvider已经发布了
    • 先是通过cpr.canRunHere(r)判断这个ContentProvider是否可以运行在请求者的进程中
      • 如果允许,那么不会将已发布的ContentProvider返回,而是将返回的ContentProviderHolder对象的provider对象会设置为null
      • 不允许多进程的情况程序会继续执行
    • 接下来就是做一些进程优先级的调整和ContentProvider计数等数据的处理
    • 最后,返回cpr.newHolder(conn)

canRunHere()方法判断的条件之一就是provider是否设置了multiprocess属性而且两个进程有相同的uid

  • 第二种情况就是ContentProvider没有发布
    • 首先检查这个ContentProvider能否在调用者的进程中创建
    • 接下来判断如果进程还没有创建出来,调用startProcessLocked()方法来启动进程
    • 如果进程已经启动了,则调用应用进程的scheduleInstallProvider()方法来安装ContentProvider
      • scheduleInstallProvider()方法仅仅是发送一个INSTALL_PROVIDER的消息
    • 然后将ContentProvider添加到mLaunchingProvidersmProviderMap集合中
      • 此时ContentProvider还未安装完成
    • 最后,循环等待ContentProvider安装完成

AMS中可以用多种参数从mProviderMap中查找ContentProvider。因此,在储存数据时会调用putProviderByName()putProviderByClass()等方法把ContentProvider对象加入,方便以后查找

安装ContentProvider

前面提到ActivityThreadscheduleInstallProvider()方法会发送一个INSTALL_PROVIDER的消息来开始ContentProvider的安装过程。

消息的处理方法是handleInstallProvider()方法,这个方法又会调用installContentProviders()方法,代码如下:

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        // 循环调用 installProvider() 方法来创建应用所有的 ContentProvider
        for (ProviderInfo cpi : providers) {
            ...
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
            // 调用 AMS 的 publishContentProviders 方法来进行注册
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

installContentProviders()方法主要做了两件事情:

  • 根据传递进来的参数循环调用installProvider()方法来安装ContentProvider
  • 将安装好的ContentProvider对象注册到AMS

我们分别来看下

ActivityThread.installProvider

installProvider()方法如下:

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            // holder.provider 为空的情况,说明要新建相关的 ContentProvider
            // 先创建 provider 对象
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            ...// 省略大量的 Context 获取逻辑
            try {
                // 采用反射的方式获取创建 ContentProvider 
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                // instantiateProvider 方法其实就是执行了 cl.loadClass(className).newInstance() 操作
                // 如果 class 当前进程中不存在,localProvider 就为空了
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                // 得到 ContentProvider 的 Binder 对象
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    // Binder 对象获取失败
                    return null;
                }
                // localProvider 初始化后执行此方法,最后会执行 ContentProvider 的 onCreate 方法
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                ...
                return null;
            }
        } else {
            // 不为空,无需新建,直接使用
            provider = holder.provider;
        }
        ContentProviderHolder retHolder;
        synchronized (mProviderMap) {
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                // localProvider 不为空的情况
                ComponentName cname = new ComponentName(info.packageName, info.name);
                // 根据名称查找 ContentProvider 是否已经存在
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    // pr 不为空
                    // 说明已经有人抢先一步创建了,直接使用
                    provider = pr.mProvider;
                } else {
                    // 否则新建一个 ContentProviderHolder 对象
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    // 通过 installProviderAuthoritiesLocked 方法新建 ProviderClientRecord 对象
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    // 并将pr添加到 mLocalProviders 集合中
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                // localProvider 为空说明安装的是对其他进程中的 ContentProvider 的引用
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    // 引用对象已经存在的情况
                    // noReleaseNeeded=true 表示可以永久安装
                    // android 通过noReleaseNeeded 和 stable 组合来确定引用是否重新创建
                    // 感觉此处可先忽略,重点了解流程
                    if (!noReleaseNeeded) {
                        // 增加引用计数
                        incProviderRefLocked(prc, stable);
                        try {
                            // 在 AMS 中 移除旧的
                            ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {
                    // 引用对象不存在,创建一个新的引用对象,并添加到集合中
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    ...
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

需要注意installProvider()方法的返回值类型,ContentProviderHolder类的主要包含了ContentProviderIContentProvider对象以及ProviderInfo对象,简要结构如下:

    public class ContentProviderHolder implements Parcelable {
        public final ProviderInfo info;
        public IContentProvider provider;
        ...
    }

而对于installProvider()方法的调用来说,源码中只有两个调用链:

  • 一个是*()->installContentProviders()->installProvider()

    • 前面讲到的scheduleInstallProvider()方法就是走的这个
    • 这个调用过程中的ContentProviderHolder参数是空的
    • 这会导致localProvider不为空
    • 因此会走新建ProviderClientRecord对象的分支
  • 另一个是*()->acquireProvider()->installProvider()

    • 一般情况ContentProviderHolder参数不为空

    • 这会导致localProvider为空

    • 因此会执行创建ProviderRefCount引用对象的分支

AMS.publishContentProviders()

方法如下:

    public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
        if (providers == null) {
            // 空集合检查
            return;
        }
        // 对 isolated 标记的进程进行限制
        enforceNotIsolatedCaller("publishContentProviders");
        synchronized (this) {
            // 得到发布 ContentProvider 的进程的 ProcessRecord 对象
            final ProcessRecord r = getRecordForAppLocked(caller);
            if (r == null) {
                throw new SecurityException(...;
            }
            ...
            final int N = providers.size();
            for (int i = 0; i < N; i++) {
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    // 对要发布的 ContentProvider 进行检查,异常情况直接跳过
                    continue;
                }
                // 查看要发布ContentProvider进程的 pubProviders 集合
                ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                if (dst != null) {
                    // 如果集合中存在该 ContentProvider 的相关信息
                    // 将其添加到 AMS 的 mProviderMap 中,共两个接口
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    // 接口1:putProviderByClass
                    mProviderMap.putProviderByClass(comp, dst);
                    String names[] = dst.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        // 接口2:putProviderByName
                        // 这个主要是按照 authorities 属性的内容进行存放
                        mProviderMap.putProviderByName(names[j], dst);
                    }
                    int launchingCount = mLaunchingProviders.size();
                    int j;
                    boolean wasInLaunchingProviders = false;
                    for (j = 0; j < launchingCount; j++) {
                        // 将 ContentProvider 从 mLaunchingProviders中移除
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true;
                            j--;
                            launchingCount--;
                        }
                    }
                    if (wasInLaunchingProviders) {
                        // 结束超时检测机制
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
                    synchronized (dst) {
                        dst.provider = src.provider;
                        dst.proc = r;
                        // 还记得 getContentProviderImpl() 方法中最后的 wait() 方法么
                        // 在这里就会通知唤醒等待的线程
                        dst.notifyAll();
                    }
                    ...
                }
            }
        }
    }

publishContentProviders()会先在调用者的进程中查找ContentProvider的信息,然后进一步完善ContentProviderRecord数据

  • 前面的方法中已经填充了部分数据,这里主要的是对authorities属性进行关联
  • 同时也通过putProviderByClass()增加ComponentName映射

前面的getContentProviderImpl()方法中,在检测到ContentProviderRecordprovider对象为空时会执行ContentProviderRecord对象的wait()方法挂起线程

publishContentProviders()方法在数据完善后,会通过notifyAll()来唤醒对应ContentProviderRecord对象所在的所有线程(PS:同步做的不错,就是绕的圈有点大哇)