Android中的ContentProvider源码解析
Posted 小二玩编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的ContentProvider源码解析相关的知识,希望对你有一定的参考价值。
本文系转载文章,阅读原文可读性会更好,文章末尾有原文链接
ps:本篇文章是基于 android API 26来分析的
1、ContentProvider 初始化
在上一篇Android中的IPC进程通信方式第四篇中,我们学了用 ContentProvider 进行 IPC 通信,这一篇我们来分析 ContentProvider 的源码;当一个应用启动时,入口方法为 ActivityThread 的main方法,main 方法是一个静态方法,在 main 方法中会创建 ActivityThread 的实例并创建主线程的消息队列;ActivityThread 的 attach 方法中会远程调用 AMS 的 attachApplication 方法并将 ApplicationThread 对象提供给AMS;
public static void main(String[] args) {
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
}
ActivityManager.getService() 这行代码通过 IBinder 进程间通讯调用 AMS,并且调用 AMS 中的 attachApplication 方法;
private void attach(boolean system) {
......
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
}
我们看看 ActivityManager 是如何获取 AMS 的,点击 ActivityManager.getService() 方法查看,发现得到了一个 IActivityManager,这里的 IActivityManager 实现类是 ActivityManagerService,也就是我们上面简称的 AMS;
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
再回过头来,我们查看 mgr.attachApplication(mAppThread) 代码,也就是 AMS 中的 attachApplication 方法,发现调用了 attachApplicationLocked 方法;
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
往下查看 ActivityManagerService 的 attachApplicationLocked 方法,下面的 thread 就是 ActivityThread,发现调用了 ActivityThread 的 bindApplication 方法,又回调到ActivityThread中进行处理了;
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
......
return true;
}
查看 ActivityThread 的 bindApplication 方法,发现把一些信息赋值给 AppBindData 对象的属性,然后通过 Handler 封装 AppBindData 发送一条消息出去;
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {
......
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
sendMessage(H.BIND_APPLICATION, data);
}
看一下 Handler 的子类 H 的处理,发现调用了 ActivityThread 的 handleBindApplication 方法;
private class H extends Handler {
......
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
......
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData) msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
}
}
查看 ActivityThread 的 handleBindApplication 方法,我们重点看 Application app = data.info.makeApplication(data.restrictedBackupMode, null)、installContentProviders(app, data.providers) 和 mInstrumentation.callApplicationOnCreate(app) 这3行代码;
private void handleBindApplication(AppBindData data) {
......
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don\'t bring up providers in restricted mode; they may depend on the
// app\'s custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don\'t want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
......
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
......
}
我们来看一下 data.info.makeApplication(data.restrictedBackupMode, null) 方法,data.info 其实是 LoadedApk 类对象,在这个 LoadedApk 类的 makeApplication 方法中,获取 ClassLoader,然后通过 mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext) 进行初始化 Application;
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
......
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
......
}
......
return app;
}
上面的 mActivityThread.mInstrumentation 其实是 Instrumentation,我们点击 Instrumentation 含有3个参数的 newApplication 方法;
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
再查看 Instrumentation 含有2个参数的 newApplication 方法,发现这里才是对 Application 初始化实现并将 Context 关联起来;
static public Application newApplication(Class<?> clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
我们再回到 ActivityThread 中调用 installContentProviders(app, data.providers) 的这行代码并查看它的具体实现,发现调用了 ActivityThread 的 installProvider 方法;
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
......
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);
}
}
......
}
我们往下看 ActivityThread 的 installProvider 方法,发现初始化了 ContentProvider 调用了 ContentProvider 含有2个参数的 attachInfo 方法;
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
......
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
......
return retHolder;
}
往下看 ContentProvider 含有2个参数的 attachInfo 方法,发现调用了它自己含有3个参数的 attachInfo 方法;
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
点击查看 ContentProvider 含有3个参数的 attachInfo 方法,发现调用了 ContentProvider 的 onCreate 方法;在上面 ActivityThread 的 handleBindApplication 方法中,调用了3个最关键的方法,那就是 Application app = data.info.makeApplication(data.restrictedBackupMode, null)、installContentProviders(app,data.providers)和 mInstrumentation.callApplicationOnCreate(app),其中 installContentProviders(app,data.providers) 调用了 ContentProvider 的 onCreate 方法 , 而 mInstrumentation.callApplicationOnCreate(app) 调用了 Application 的 onCreate 方法,所以 ContentProvider 的 onCreate 方法比 Application 的 onCreate 方法先执行。
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can\'t change it.
*/
if (mContext == null) {
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();
}
}
2、ContentProvider 源码解析-query 方法
ContentProvider 的实现类要实现的方法有 onCreate、getType、delete、update、query 和 insert 方法,这里我们只解析一下 query 方法的源码,对其他5个方法有兴趣的读者可以自己去阅读一下它们的源码;一开始是获取一个 ContentResolver 对象,是交给 ContextWrapper 的 getContentResolver 方法;
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
这时候 ContextWrapper 的 getContentResolver 方法是交给 ContextImpl 的 getContentResolver 方法来实现,mBase 就是 ContextImpl 类型的对象,ContextImpl 类的 getContentResolver 返回的是 mContentResolver,它是一个 ApplicationContentResolver 类型的对象;
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
到这里之后就可以开始分析 query 方法的源码了,先看一下 ContentResolver 中含有5个参数的 query 方法,该方法又调用了 ContentResolver 中含有6个参数的 query 方法;
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
我们往下看 ContentResolver 中含有6个参数的 query 方法,发现该方法调用了 ContentResolver 中含有4个参数的 query 方法;
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
return query(uri, projection, queryArgs, cancellationSignal);
}
再查看 ContentResolver 中含有4个参数的 query 方法,发现该方法调用了 ContentResolver 中含有1个参数的 acquireUnstableProvider 方法;
Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider unstableProvider = acquireUnstableProvider(uri);
......
}
往下看 ContentResolver 中含有1个参数的 acquireUnstableProvider 方法,发现该方法调用了 ContentResolver 中含有2个参数的 acquireUnstableProvider 方法;
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
查看 ApplicationContentResolver(是 ContentResolver 类的子类,也是 ContextImpl 的内部类) 中含有2个参数的 acquireUnstableProvider 方法,发现调用了 ActivityThread 的 acquireProvider 方法,mMainThread 就是 ActivityThread;
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
点击查看 ActivityThread 的 acquireProvider 方法,首先会从 ActivityThread 中查找是否已经存在目标 ContentProvider了,如果存在就直接返回
;如果没有,就调用 AMS 获取 ContentProvider 对象;
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
ContentProviderHolder holder = null;
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
我们从客户端拿到的 ContentProvider 并不是原始的 ContentProvider,而是 ContentProvider 的 Binder 类型的对象IContentProvider,IContentProvider的具体实现是 ContentProviderNative 和 ContentProvider.Transport,其中 ContentProvider.Transport 继承了 ContentProviderNative,下面我们看 ContentProvider.Transport 的 query 方法,该方法又调用了 ContentProvider 的4个参数的 query 方法;
@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
......
try {
return ContentProvider.this.query(
uri, projection, queryArgs,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
我们再点击查看 ContentProvider 的4个参数的 query 方法,发现它调用了 ContentProvider 的6个参数的 query 方法;
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;
// if client doesn\'t supply an SQL sort order argument, attempt to build one from
// QUERY_ARG_SORT* arguments.
String sortClause = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER);
if (sortClause == null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
sortClause = ContentResolver.createSqlSortClause(queryArgs);
}
return query(
uri,
projection,
queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
queryArgs.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS),
sortClause,
cancellationSignal);
}
查看 ContentProvider 的6个参数的 query 方法,发现它调用了 ContentProvider 的5个参数的 query 方法,这时候 5个参数的 query 方法是抽象方法,最终调用到了我们自己继承 ContentProvider 类的子类的 query 方法;
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
return query(uri, projection, selection, selectionArgs, sortOrder);
}
以上是关于Android中的ContentProvider源码解析的主要内容,如果未能解决你的问题,请参考以下文章
android开发中的contentprovider使用问题
Android 开发 ContentProvider 获取歌曲列表和联系人的样例
Android19.3 ContentProvider及安卓进一步封装后的相关类