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:process
和android: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
Android
对isolated
进程的接口调用做了一些限制- 比如调用
bindService()
的进程如果是isolated
进程的话会抛出安全异常
- 比如调用
- 首先,
后面Service
的启动代码中会涉及到isolated
相关的逻辑,我们先看下Service
的生命周期
Service
的生命周期
和Activity
类似,Service
也有声明周期,但是和Activity
的声明周期相比,Service
的要简单许多。
官方图示如下:
- 图片左侧显示的是使用
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
对象- 应用进程中的
Service
在AMS
中对应的数据结构就是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
对象。
这两个接口最后调用的是AMS
的startService()
和bindService()
,两个方法的实现差不多,bindService()
更复杂一些。
后面以bindService()
方法为例进行分析,在此之前,我们先看下启动过程的时序图:
AMS
中bindService()
的代码如下:
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()
进入到下一步 - 如果进程还没有启动,调用
AMS
的startProcessLocked()
来启动进程 - 最后,为了进程启动结束后能继续运行该
Service
,将其加入到mPendingServices
集合中
新启动进程在哪里调用的realStartServiceLocked()
来启动Service
的呢?
按照进程的启动逻辑,启动完会执行AMS
的attachApplicationLocked()
方法,然后调用ActiveServices
的attachApplicationLocked()
方法,方法如下:
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()
主要的工作就是调用两个方法,分别是
ApplicationThread
的scheduleCreateService()
requestServiceBindingsLocked()
方法- 该方法最终也调用了
ApplicationThread
的scheduleBindService()
方法
- 该方法最终也调用了
前面介绍过,ApplicationThread
的schedule*
方法其实都是发送相应的处理消息到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
启动的声明周期函数都调用完成了。
需要注意的是AMS
的publishService()
方法,该方法会调用ActiveServices
的publishServiceLocked()
方法,实现如下:
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://
authority
:android.launcher.settings.provider
用来标识一个唯一的ContentProvider
- 官方建议:如果您的
Android
软件包名称为com.example.<appname>
,ContentProvider
的授权应该设置为com.example.<appname>.provider
- 官方建议:如果您的
path
:app
表示数据的类型,由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_PERMISSION
和FLAG_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
中的一个内部类,继承关系图如下:
从继承关系就可以看出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
通常应用中先通过Context
的getContentResolver()
方法得到应用中的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()
方法只是调用了ActivityThread
的acquireProvider()
方法:
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;
}
ActivityThread
的acquireProvider()
方法:
- 先调用
acquireExistingProvider()
,这个方法将检查mProviderMap
中是否已经创建了相应的ContentProvider
对象 - 如果集合中已经存在,直接返回该对象
- 否则调用
AMS
的getContentProvider()
方法来得到一个ContentProviderHolder
对象 - 最后,如果
ContentProviderHolder
对象不为null
,执行installProvider()
安装这个ContentProvider
我们看下AMS
的getContentProvider()
方法:
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);
}
AMS
的getContentProvider()
方法在简单检查参数后,调用了内部的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
添加到mLaunchingProviders
和mProviderMap
集合中- 此时
ContentProvider
还未安装完成
- 此时
- 最后,循环等待
ContentProvider
安装完成
- 首先检查这个
AMS
中可以用多种参数从mProviderMap
中查找ContentProvider
。因此,在储存数据时会调用putProviderByName()
、putProviderByClass()
等方法把ContentProvider
对象加入,方便以后查找
安装ContentProvider
前面提到ActivityThread
的scheduleInstallProvider()
方法会发送一个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
类的主要包含了ContentProvider
的IContentProvider
对象以及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()
方法中,在检测到ContentProviderRecord
的provider
对象为空时会执行ContentProviderRecord
对象的wait()
方法挂起线程
而publishContentProviders()
方法在数据完善后,会通过notifyAll()
来唤醒对应ContentProviderRecord
对象所在的所有线程(PS:同步做的不错,就是绕的圈有点大哇)