ContentProvider主要用于对外共享数据,也就是通过ContentProvider将应用中的数据共享给其他应用访问。这样其他应用就可以通过ContentProvider来访问指定应用中的数据。ContentProvider分为自定义的和系统的。系统提供的ContentProvider有联系人,图片等。其他应用可以通过ContentResolver来访问联系人和图片中的数据。
下面看看如何自定义一个ContentProvider,这里只是举例,并未在BookContentProvider中真正实现具体的CRUD。
public class BookContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
LogUtil.i("onCreate");
return false;
}
@Nullable
@android.support.annotation.Nullable
@Override
public Cursor query(@NonNull @android.support.annotation.NonNull Uri uri, @Nullable @android.support.annotation.Nullable String[] projection, @Nullable @android.support.annotation.Nullable String selection, @Nullable @android.support.annotation.Nullable String[] selectionArgs, @Nullable @android.support.annotation.Nullable String sortOrder) {
return null;
}
@Nullable
@android.support.annotation.Nullable
@Override
public String getType(@NonNull @android.support.annotation.NonNull Uri uri) {
return null;
}
@Nullable
@android.support.annotation.Nullable
@Override
public Uri insert(@NonNull @android.support.annotation.NonNull Uri uri, @Nullable @android.support.annotation.Nullable ContentValues values) {
LogUtil.i(""+values);
return null;
}
@Override
public int delete(@NonNull @android.support.annotation.NonNull Uri uri, @Nullable @android.support.annotation.Nullable String selection, @Nullable @android.support.annotation.Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull @android.support.annotation.NonNull Uri uri, @Nullable @android.support.annotation.Nullable ContentValues values, @Nullable @android.support.annotation.Nullable String selection, @Nullable @android.support.annotation.Nullable String[] selectionArgs) {
return 0;
}
}
在manifest文件中,注册自定义的BookContentProvider,具体代码如下:
<!--自定义权限-->
<permission android:name="test.cn.example.com.androidskill.permission.WRITE_BOOK_PROVIDER"/>
<permission android:name="test.cn.example.com.androidskill.permission.READ_BOOK_PROVIDER"/>
<provider
android:authorities="test.cn.example.com.androidskill"
android:writePermission="test.cn.example.com.androidskill.permission.WRITE_BOOK_PROVIDER"
android:readPermission="test.cn.example.com.androidskill.permission.READ_BOOK_PROVIDER"
android:name="test.cn.example.com.androidskill.art.chapter_nine.BookContentProvider"
android:exported="true"/>
在另外一个应用中,查询BookContentProvider所在的进程共享的数据
Uri uri = Uri.parse(“content://test.cn.example.com.androidskill”);
Cursor query = getContentResolver().query(uri, null, null, null, null);
通过以上步骤,就可以完成一个自定义的ContentProvider,并且,其他应用就可以通过ContentResolver和uri去查询相应的ContentProvider提供的数据了。下面分析下这个过程。在Activity中,通过getContentResolver()方法获取ContentResolver对象,其实是调用了ContextWrapper的getContentResolver()方法,最终会调用到ContextImpl的getContentResolver方法。
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
这个方法中,直接返回了一个mContentResolver,这个mContentResolver是ApplicationContentResolver类型的,ApplicationContentResolver是ContextImpl的静态内部类,它是继承ContentResolver的。那么getContentResolver().query方法,其实是调用的ApplicationContentResolver的query,由于ApplicationContentResolver没有这个方法,ApplicationContentResolver的query方法是其父类ContentResolver的,下面看看ContentResolver的query方法
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/ContentResolver.java
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);
}
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);
}
public final @Nullable 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);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
// ...
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(
mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// ...
return wrapper;
}
// ...
}
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;
}
这个方法最终会调用到acquireUnstableProvider,ContentResolver的acquireUnstableProvider是个抽象方法,具体的实现是它的子类ApplicationContentResolver,所以,又跳转到了ContextImpl.ApplicationContentResolver类中,下面看看这个方法
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ContextImpl.java
class ContextImpl extends Context {
// ...
private static final class ApplicationContentResolver extends ContentResolver {
// ...
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
}
}
这个方法中的mMainThread是ActivityThread类型的,所以会执行ActivityThread类的acquireProvider方法
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 关键代码1
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
// 关键代码2
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
return null;
}
//关键代码3
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
在关键代码1处,会通过acquireExistingProvider方法,先到mProviderMap中查找是否已经存在创建过的ContentProvider,如果存在,则直接返回这个已经创建的ContentProvider,否则通过AMS获取一个ContentProvider,关键代码2处就是通过AMS来创建ContentProvider,本例中,假设是第一次创建ContentProvider,所以,acquireExistingProvider方法返回的是null,需要通过AMS来创建ContentProvider,下面看看AMS的getContentProvider方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name, null, stable, userId);
}
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
// ...
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
if (providerRunning) { //ContentProvider已经启动
// ...
//关键代码1
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
// ...
}
if (!providerRunning) { //ContentProvider还未启动
// ...
if (i >= N) {
// ...
try {
//...
if (proc != null && proc.thread != null && !proc.killed) {
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
//...
//关键代码2
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
// ...
}
// ...
}
// ...
}
//...
}
//...
}
// ...
return cpr != null ? cpr.newHolder(conn) : null;
}
本例中,按照ContentProvider所在的进程还未启动为例分析,则会执行关键代码2处的代码,startProcessLocked方法会调用其重载方法,最终会调用到startProcess方法,startProcess方法内部又调用了Process类的start方法,Process类的start方法中,又会调用ZygoteProcess的start方法,这个过程是system_server进程和ZygoteProcess进程通信,它们是通过socket进行的。ZygoteProcess类的start方法又会调用startViaZygote方法startViaZygote方法又会调用zygoteSendArgsAndGetResult方法,最终完成ContentProvider所在的进程的创建。这样就
执行ActivityThread类的main方法。
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
// ...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这个方法中,主要做了如下几件事:
1.创建一个消息队列和一个Looper对象
2.创建ActivityThread对象,并调用其attach方法,
3.如果sMainThreadHandler为null,则给sMainHandler赋值
4.通过Looper循环获取消息队列中的消息
下面具体看attach方法
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
// ...
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// ...
}
//...
}
这个方法内部,主要是调用了AMS的attachApplication方法并将ApplicationThread类型的对象作为参数传过去。这样system_server进程就可以通过ApplicationThread的代理对象和ContentProvider所在的进程进行通信。下面看下AMS的attachApplication方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
// ...
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
@GuardedBy("this")
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// ...
try {
//...
if (app.isolatedEntryPoint != null) {
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else 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, isAutofillCompatEnabled);
} 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, isAutofillCompatEnabled);
}
//...
}
// ...
return true;
}
这个方法内部又会调用thread.bindApplication方法,这里的thread就是ApplicationThread类型的,下面看看ApplicationThread的bindApplication方法
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ActivityThread.java
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, boolean autofillCompatibilityEnabled) {
// ...
sendMessage(H.BIND_APPLICATION, data);
}
这个方法内部通过sendMessage方法了一个消息,最后执行handleBindApplication(data)方法
private void handleBindApplication(AppBindData data) {
// ...
//创建ContextImpl类型的对象appContext
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
// ...
final InstrumentationInfo ii;
// ...
//加载Instrumentation
if (ii != null) {
//...
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
//...
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
//...
Application app;
// ...
try {
//创建Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//关键代码
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
//...
try {
mInstrumentation.callApplicationOnCreate(app);
}
// ...
}
// ...
}
这个方法内部主要做了如下几件事:
1.创建Instrumentation对象
2.调用LoadedApk类的makeApplication方法,创建Application对象
3.调用installContentProviders方法创建ContentProvider,并调用了ContentProvider的onCreate的方法
4.调用Instrumentation的callApplicationOnCreate方法,最终使Application的onCreatea方法执行。
从上面的第3步和第4步的执行顺序可以发现,ContentProvider的onCreate方法是优先于Application的onCreate方法执行的。
下面具体看看installContentProviders方法
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
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 {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
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) {
//...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
packageInfo = getSystemContext().mPackageInfo;
}
//关键代码1
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 关键代码2
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
//关键代码3
localProvider.attachInfo(c, info);
}
// ...
}
// ...
return retHolder;
}
installContentProviders方法内部有调用了installProvider方法,在installProvider方法中,
packageInfo.getAppFactory()获取的是AppComponentFactory类型的对象,下面看看AppComponentFactory的instantiateProvider方法
http://androidxref.com/9.0.0_r3/xref/frameworks/support/compat/src/main/java/androidx/core/app/AppComponentFactory.java
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}
这个方法内部,其实就是通过类加载方式创建一个ContentProvider对象(至此ContentProvider对象就创建完成了)。并将这个对象赋值给上面的installProvider方法中的localProvider这个对象,接着调用localProvider的attachInfo方法,下面看看ContentProvider的attachInfo方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
if (mContext == null) {
// ...
ContentProvider.this.onCreate();
}
}
ContentProvider类中的attachInfo方法,最终调用了onCreate方法,这样ContentProvider的onCreate方法就得到执行。至此,ContentProvider不仅完成了创建,并且onCreate方法也得到了调用。以上便是ContentProvider的创建和启动过程的分析。
下面接着分析query方法的执行过程。回到ActivityThread类的installProvider方法
http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/app/ActivityThread.java
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) {
//...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
packageInfo = getSystemContext().mPackageInfo;
}
//关键代码1
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 关键代码2
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
//关键代码3
localProvider.attachInfo(c, info);
}
// ...
}
// ...
return retHolder;
}
这个方法的关键代码2处,通过localProvider.getIContentProvider()会返回一个Transport类型的对象。并将这个对象封装到ContentProviderHolder这个对象中,当调用getContentResolver方法是,返回但是ContentProviderHolder这个对象的provider,这个provider其实就是Transport类型的对象,Transport是继承ContentProviderNative类的,而ContentProviderNative实现了IContentProvider接口。所以getContentResolver()
方法获取的这个对象其实是system_server进程的IContentProvider在app进程的一个代理对象,通过调用这个代理对象其实就是Transport对象,所以,代理对象调用query方法,最终会调用到Transport的query方法。Transport类是ContentProvider的一个内部类,Transport类的query方法又会调用ContentProvider的query方法,最终将查询的结果通过Binder放回给客户端。
class Transport extends ContentProviderNative {
// ...
}
abstract public class ContentProviderNative extends Binder implements IContentProvider {
// ...
}
关于ContentProvider自定义权限:
如果要给自定义的ContentProvider添加权限,建议同时添加读和写的权限,自己尝试,只是加读的权限,完全可以使用,但是,只是添加写的权限,则不能使用,不知道是不是手机系统的问题,目前是在华为mate7,6.0系统上是这样的情况。所以,建议还是同时添加
读写的权限,添加完后,还需要在manifest文件中,声明这两个权限,其他app要访问这个自定义的ContentProvider时,需要在manifest文件中使用ContentProvider声明的权限。
Android组件的exported的默认值问题:
如果包含有intent-filter exported默认值为true; 没有intent-filter exported默认值为false