Android中ContentProvider的工作过程

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<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
        new ArrayList<IActivityManager.ContentProviderHolder>();
    // 关键代码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<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();
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方法。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android ContentProviderAndroid系统的一个组件,用于管理应用程序之间的数据共享。它提供了一种标准的接口,使得应用程序可以访问其他应用程序的数据,同时也可以保护数据的安全性。ContentProvider可以将数据存储在文件系统、SQLite数据库或者网络上,然后通过URI来访问这些数据。开发者可以通过继承ContentProvider类来创建自己的ContentProvider,然后在AndroidManifest.xml文件注册。在使用ContentProvider时,需要使用ContentResolver类来访问数据。 ### 回答2: Android ContentProviderAndroid Framework的一个重要组件,用于在应用间共享数据。它提供一种标准化的方式,让其它应用或组件访问原本受到封装的私有数据。ContentProvider采用标准的CRUD(Create、Retrieve、Update、Delete)逻辑来管理数据,且提供额外的查询功能,是实现数据在应用间共享的理想选择。 ContentProvider可以用来实现以下几个功能: 1. 数据共享:ContentProvider提供了一个标准的接口,让其它应用或组件来操作内容提供者的数据。 2. 数据访问保护:ContentProvider可以控制其它应用或组件只有在得到授权的情况下才能访问数据。 3. 储存树形数据:储存树形数据是很常见的需求,不像SQLite那样的关系型数据库。 4. 数据访问审计:作为应用的一个央存储库,ContentProvider可以记录其它应用或组件对它的操作记录。 下面以一个简单的例子来说明ContentProvider的使用过程: 1. 首先在AndroidManifest.xml文件定义ContentProvider,必须在所有Activity的前面定义; 2. 在代码继承ContentProvider,实现CRUD和查询方法; 3. 在应用使用CursorLoader或ContentResolver访问ContentProvider的数据; 4. 客户端进程和服务端进程都需要读写ContentProvider,为了防止多线程数据访问问题, ContentProvider通常会使用线程池来进行并发处理。 ContentProviderAndroid组件的一种,应用程序可以通过该组件来暴露自己的数据给其它应用程序使用,同时也可以访问其它应用程序的ContentProvider,以获取到这些应用程序所提供的数据。ContentProvider的实例在Android开发广泛运用。通过ContentProvider,我们可以更加方便地共享数据,能够让我们的应用程序变得更加灵活。 ### 回答3: Android提供了Content Provider框架,以便不同的应用程序之间共享数据。ContentProvider提供了一种安全访问数据的方式,防止数据被未经授权的访问。 实现Content Provider需要定义一个类,该类继承自ContentProvider类,并实现一些必要的方法,包括onCreate()、query()、insert()、update()和delete()。我们可以通过Content Resolver类来访问Content Provider,它是一个用于查询、插入、更新和删除数据的类。 Content Provider的最大好处是在应用程序之间共享数据。例如,一个应用程序可以提供联系人列表,并允许其他应用程序访问该列表。如果应用程序需要共享数据,可以使用Content Provider。 在实现Content Provider时,需要确定好URI的结构,以便其他应用程序访问数据。URI的结构可以根据应用程序的需求来定制。例如,我想创建一个共享书籍列表的Content Provider,URI可以定义为content://com.example.bookprovider/books。通过这个URI,其他应用程序可以访问该Provider的books表,并操作数据。 要使用Content Provider,需要先在AndroidManifest.xml文件注册该Provider。这需要声明一个<provider>标记,其包含Provider的名称、权限和URI。 Content ProviderAndroid被广泛使用,它可以让应用程序之间共享数据,并提高应用程序的安全性。但是,使用Content Provider也需要注意数据安全性,防止数据被未经授权的访问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值