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

Service 管理

官方对Service的描述是:java

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

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的区别是什么呢?markdown

  • 当咱们仅仅指定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的要简单许多。数据结构

官方图示以下:
imageapp

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

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

  • 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:同步作的不错,就是绕的圈有点大哇)

相关文章
相关标签/搜索