ContentProvider是一种内容共享型组件,它通过Binder向其它组件乃至其它应用提供数据。关于ContentProvider是如何使用的,我们在之前文章《Android里内容提供者ContentProvider的使用》中已经有介绍过和实例演示。今天主要是对ContentProvider的一些工作过程作分析和学习。

ContentProvider的启动过程

一般来说,ContentProvider都应该是单实例的。因为android:multiprocess默认是false,当android:multiprocess为true时,ContentProvider为多实例,这时每个调用者的进程都存在一个ContentProvider对象。但由于实际开发中,并未发现多实例的具体使用场景(虽然官方解释说可以避免进程问通信的开销),因此我们可以简单认为ContentProvider都是单实例。下面来看看单实例的ContentProvider的启动过程。

当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到ActivityManagerService中。而且,ContentProvider的onCreate很特殊,会先于Application的onCreate执行。我们在前面文章《Android应用程序启动详解(二)从源码了解App的启动过程》中也曾经一句话提过到这点,来回顾一下源码:

ActivityThread.java

private void handleBindApplication(AppBindData data) {    ……    final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();    try {        // 关键代码1        Application app = data.info.makeApplication(data.restrictedBackupMode, null);        mInitialApplication = app;         if (!data.restrictedBackupMode) {            if (!ArrayUtils.isEmpty(data.providers)) {                // 关键代码2                installContentProviders(app, data.providers);                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);            }        }         try {            mInstrumentation.onCreate(data.instrumentationArgs);         }        catch (Exception e) {            throw new RuntimeException( "Exception thrown in onCreate() of "                + data.instrumentationName + ": " + e.toString(), e);        }         try {            // 关键代码3            mInstrumentation.callApplicationOnCreate(app);        } catch (Exception e) {            if (!mInstrumentation.onException(app, e)) {                throw new RuntimeException(                    "Unable to create application " + app.getClass().getName()                    + ": " + e.toString(), e);            }        }    } finally {        StrictMode.setThreadPolicy(savedPolicy);    }}

关键代码1中就是创建Application对象,而关键代码3就是在创建出Application对象后,调用了callApplicationOnCreate(app)方法,也就是回调Application的onCreate方法。我们重要来看看关键代码2,它就是ContentProvider的创建过程逻辑。继续跟踪installContentProviders方法看它做了啥事情:

private void installContentProviders(        Context context, List providers) {    final ArrayList results =        new ArrayList();    // 关键代码1    for (ProviderInfo cpi : providers) {        if (DEBUG_PROVIDER) {            StringBuilder buf = new StringBuilder(128);            buf.append("Pub ");            buf.append(cpi.authority);            buf.append(": ");            buf.append(cpi.name);            Log.i(TAG, buf.toString());        }        // 关键代码2        IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);        if (cph != null) {            cph.noReleaseNeeded = true;            results.add(cph);        }    }    try {        // 关键代码3        ActivityManagerNative.getDefault().publishContentProviders(            getApplicationThread(), results);    } catch (RemoteException ex) {        throw ex.rethrowFromSystemServer();    }}

方法内逻辑很清楚,关键代码1中遍历当前进程的ProviderInfo的列表并一一调用关键代码2中的installProvider方法来启动它们,最后就是关键代码3中,将已经启动的ContentProvider发布到ActivityManagerService中(关于ActivityManagerNative.getDefault()为什么是ActivityManagerService,我们在前面也提到很多次,不明白的话可以查看之前的文章《Android应用程序启动详解(一)》)。来看看关键代码2的installProvider方法:

private IActivityManager.ContentProviderHolder installProvider(Context context,        IActivityManager.ContentProviderHolder holder, ProviderInfo info,        boolean noisy, boolean noReleaseNeeded, boolean stable) {    ……        try {            // 关键代码1            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.            // 关键代码2            localProvider.attachInfo(c, info);        } catch (java.lang.Exception e) {            ……;        }    } else {        ……    }    IActivityManager.ContentProviderHolder retHolder;    synchronized (mProviderMap) {        if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider                + " / " + info.name);        IBinder jBinder = provider.asBinder();        if (localProvider != null) {            ……        } else {            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);            if (prc != null) {                ……            } else {                // 关键代码3                ProviderClientRecord client = installProviderAuthoritiesLocked(                        provider, localProvider, holder);                ……            }            retHolder = prc.holder;        }    }    return retHolder;}

上述关键代码1中,通过类加载器完成了ContentProvider对象的创建,还通过关键代码2中attachInfo方法来设置一些信息,来看看attachInfo的方法代码:

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();    }}

方法中可以看到ContentProvider的onCreate方法就是在这里被调用的了,

再看回installProvider方法的关键代码3的installProviderAuthoritiesLocked方法是做了什么事情:

final ArrayMap mProviderMap = new ArrayMap();private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,        ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {    final String auths[] = holder.info.authority.split(";");    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);    final ProviderClientRecord pcr = new ProviderClientRecord(            auths, provider, localProvider, holder);    for (String auth : auths) {        final ProviderKey key = new ProviderKey(auth, userId);        final ProviderClientRecord existing = mProviderMap.get(key);        if (existing != null) {            Slog.w(TAG, "Content provider " + pcr.mHolder.info.name                    + " already published as " + auth);        } else {            // 关键代码            mProviderMap.put(key, pcr);        }    }    return pcr;}

原来是将刚创建的IContentProvider对象通过Map的形式来存储起来。好了,到此为止,ContentProvider就已经启动完成。

ContentProvider的操作过程

当ContentProvider启动后,外界就可以通过它所提供的insert、delete、update 和 query这四个接口来对ContentProvider中的数据源进行增删改查操作。这四个方法都是通过Binder来调用,外界无法直接访问ContentProvider,它只能通过ActivityManagerService根据Uri来获取对应用ContentProvider的Binder接口IConentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。

四个操作接口逻辑很类似,我们就从query方法入手,先来看下实例代码:

Cursor cursor = getContentResolver().query(Uri.parse("content:// com.zyx.PersonProvider/person/1"), null, null, null, null);

访问ContentProvider需要通过getContentResolver方法来获取ContentResolver的对象,ContentResolver是一个抽象类,getContentResolver方法实际上是返回了ApplicationContentResolver对象,来看代码:

ContentResolver.java

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,        @Nullable String[] projection, @Nullable String selection,        @Nullable String[] selectionArgs, @Nullable String sortOrder,        @Nullable CancellationSignal cancellationSignal) {    Preconditions.checkNotNull(uri, "uri");    // 关键代码1    IContentProvider unstableProvider = acquireUnstableProvider(uri);    if (unstableProvider == null) {        return null;    }    IContentProvider stableProvider = null;    Cursor qCursor = null;    try {        long startTime = SystemClock.uptimeMillis();        ICancellationSignal remoteCancellationSignal = null;        if (cancellationSignal != null) {            cancellationSignal.throwIfCanceled();            remoteCancellationSignal = unstableProvider.createCancellationSignal();            cancellationSignal.setRemote(remoteCancellationSignal);        }        try {            // 关键代码2            qCursor = unstableProvider.query(mPackageName, uri, projection,                    selection, selectionArgs, sortOrder, remoteCancellationSignal);        } catch (DeadObjectException e) {            unstableProviderDied(unstableProvider);            // 关键代码3            stableProvider = acquireProvider(uri);            if (stableProvider == null) {                return null;            }            // 关键代码4            qCursor = stableProvider.query(mPackageName, uri, projection,                    selection, selectionArgs, sortOrder, remoteCancellationSignal);        }        ……        return wrapper;    } catch (RemoteException e) {        return null;    } finally {        ……    }}

上述代码中,关键代码1 和 关键代码3都是为了获取一个IContentProvider接口对象,分别调用了acquireUnstableProvider 和 acquireProvider 方法,然后再调用其获取对象的query方法,acquireUnstableProvider方法的内部也是调用了acquireProvider方法,ApplicationContentResolver继承ContentResolver:

ContextImpl.java

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

再来看看acquireProvider方法的代码:

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;    }    IActivityManager.ContentProviderHolder holder = null;    try {        // 关键代码2        holder = ActivityManagerNative.getDefault().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;}

关键代码1中,通过acquireExistingProvider方法来获得已经存在的ContentProvider,如果存在就直接返回。否则,就会到关键代码2处发送一个进程间请求给ActivityManagerService让其启动目标ContentProvider,这样最终就会到了进程的启动入口方法ActivityThread的main方法,通过一系列过程后就又了”ContentProvider的启动过程”中所述的installContentProviders方法去。我们来看看关键代码1 的acquireExistingProvider方法的代码:

public final IContentProvider acquireExistingProvider(        Context c, String auth, int userId, boolean stable) {    synchronized (mProviderMap) {        final ProviderKey key = new ProviderKey(auth, userId);        final ProviderClientRecord pr = mProviderMap.get(key);        if (pr == null) {            return null;        }        IContentProvider provider = pr.mProvider;        IBinder jBinder = provider.asBinder();        if (!jBinder.isBinderAlive()) {            ……            return null;        }        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);        if (prc != null) {            incProviderRefLocked(prc, stable);        }        return provider;    }}

我们看到了熟悉的mProviderMap对象,也就是在启动过程中调用了onCreate后将其保存起来的Map对象。知道了IContentProvider的获取,现在回到ContentResolver的query中的关键代码2和关键代码4中去看它们调用了。

IContentProvider是ContentProvider的Binder对象,它的具本实现是ContentProviderNative和ContentProvider.Transport,其中ContentProvider.Transport继承了ContentProviderNative。来看看ContentProvider.Transport的query方法代码:

ContentProvider.java

class Transport extends ContentProviderNative {    ……    @Override    public Cursor query(String callingPkg, Uri uri, String[] projection,            String selection, String[] selectionArgs, String sortOrder,            ICancellationSignal cancellationSignal) {        validateIncomingUri(uri);        uri = getUriWithoutUserId(uri);        if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {            if (projection != null) {                return new MatrixCursor(projection, 0);            }            Cursor cursor = ContentProvider.this.query(uri, projection, selection,                    selectionArgs, sortOrder, CancellationSignal.fromTransport(                            cancellationSignal));            if (cursor == null) {                return null;            }            return new MatrixCursor(cursor.getColumnNames(), 0);        }        final String original = setCallingPackage(callingPkg);        try {            // 关键代码            return ContentProvider.this.query(                    uri, projection, selection, selectionArgs, sortOrder,                    CancellationSignal.fromTransport(cancellationSignal));        } finally {            setCallingPackage(original);        }    }……}

关键代码明显不过,它调用了ContentProvider的query方法,也就是我们自定义继承ContentProvider的类的query方法,query方法的执行结果再通过Binder返回给调用者,就这样整个调用过程就完成了。

总结

  1. ContentProvider一般是单实例,也可通过设置android:multiprocess为true使其变为多实例,虽官方说可以避免进程问通信的开销,但目前并未发现多实例的具体使用场景
  2. ContentProvider是在Application创建完成后进行,而且ContentProvider的onCreate方法是先于Application的onCreate方法
  3. ContentProvider创建完成后,会将IContentProvider(ContentProvider的Binder对象)会保存于一个ArrayMap对象中,当外部调用insert、delete、update 和 query方法时便先检查该对象是否存在
  4. IContentProvider的实现类是ContentProviderNative和ContentProvider.Transport,当外部调用insert、delete、update 和 query方法时,就是调用到IContentProvider的insert、delete、update 和 query方法,最终也是会调用到ContentProvider的insert、delete、update 和 query方法。

 

更多相关文章

  1. Android状态栏透明方法,与工具栏颜色一致
  2. Android手机应用开发(八) | 制作简单音乐播放器
  3. Android(安卓)Jetpack 之 LifeCycle
  4. [置顶] Android消息机制Handler、Looper、MessageQueue源码分析
  5. Android(安卓)状态栏下拉列表添加自定义item开关
  6. Android(安卓)获取判断是否有悬浮窗权限的方法
  7. Android简单记录和恢复ListView滚动位置的方法
  8. Android(安卓)使用PDF.js浏览pdf的方法示例
  9. Android中Bitmap用法实例分析

随机推荐

  1. 使用Handler实现定时器
  2. 你需要知道的Android上下文Context
  3. Android Gradle构建学习(二):构建定制
  4. Android应用程序键盘(Keyboard)消息处理机
  5. Error occurred during initialization o
  6. 【Android】利用intent拨打电话
  7. android中相关的图形类
  8. Android之Handler详解(三)
  9. (20120801)android文件的读写SD卡总结
  10. Android(安卓)Binder框架实现之Framework