Android之ContentProvider源码解析

2,378 阅读13分钟

ContentProvider是Android四大组件之一,它的主要作用是进程间共享数据。Android中的数据存储方式主要有以下几种:网络存储、文件存储(SharedPreferences属于文件的一种)、数据库。大多数情况下这些数据存储操作都是在同一进程中进行,但如果要数据和文件在不同进程间共享就比较复杂,而ContentProvider正好擅长这个,所以在多进程之间共享数据的最好方式就是通过ContentProvider来实现。

1、ContentProvider的使用

ContentProvider是个抽象类,需要一个自定义类来实现其中的抽象方法,如下:

public class MyContentProvider extends ContentProvider {
    private static final String TAG = "MyContentProvider";
    
    //ContentProvider中的抽象方法,需要在子类实现
    @Override
    public boolean onCreate() {
        return false;
    }
    
    //ContentProvider通过反射创建对象成功后第一个调用的方法
    @Override
    public void attachInfo(Context context, ProviderInfo info) {
        //在父类中调用了onCreate方法
        super.attachInfo(context, info);
    }

    //数据查询操作,如果ContentProvider在主进程中创建则该操作在主线程中执行,非线程安全
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    //返回当前 Url所代表数据的MIME类型
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    //数据插入操作,如果ContentProvider在主进程中创建则该操作在主线程中执行,非线程安全
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    //数据删除操作,如果ContentProvider在主进程中创建则该操作在主线程中执行,非线程安全
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    //数据更新操作,如果ContentProvider在主进程中创建则该操作在主线程中执行,非线程安全
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

 仅仅一个自定义类还不够,ContentProvideractivityservice一样,需要在AndroidManifest.xml文件中进行配置。

    <provider
        android:name=".MyContentProvider"
        android:authorities="com.example.content.provider"
        android:multiprocess="false"
        android:process=":remote"
        android:exported="true"/>

 配置参数还是蛮多的,但是我们只需要关注multiprocessprocessexported这三个参数即可(其他参数可以参考ContentProvider简介这篇文章)。exported为true则表示允许其他应用访问应用中的ContentProvider(跨应用访问),默认为false。process表示ContentProvider所在的进程。multiprocess为true表示每个调用者进程都会创建一个ContentProvider实例,默认为false。当multiprocessprocess这两个参数结合起来就有点意思,会产生以下几种情况。

  • android:process=":remote"、android:multiprocess="true"ContentProvider不会随应用的启动而加载,当调用ContentProvider的时候才会加载,并且ContentProvider是在调用者的进程中初始化。这时候可能定义ContentProviderremote进程还没有启动。 -android:process=":remote"、android:multiprocess="false"(默认情况)ContentProvider不会随应用的启动而加载,当调用到ContentProvider的时候才会加载,并且ContentProvider是在“remote”进程中初始化。
  • android:multiprocess="true"ContentProvider会随着应用的启动而加载,并且ContentProvider是在应用进程的主线程中初始化的。当被调用时会在调用者进程中实例化一个ContentProvider对象。
  • android:multiprocess="false"(默认情况)ContentProvider会随着应用的启动而加载,并且ContentProvider是在应用主进程的主线程中初始化的。这种ContentProvider只有一个实例,运行在自己App的进程中。所有调用者共享该ContentProvider实例,调用者与ContentProvider实例位于两个不同的进程。

ContentProvider创建成功后,使用起来还是比较简单,首先获得一个ContentResolver对象,再对该对象的crud操作即可。

    //拿到访问的uri
    Uri uri_user = Uri.parse("content://com.example.content.provider");
    ContentResolver resolver = getContentResolver();
    //通过URI来插入数据
    resolver.insert(uri_user, ...);
    //通过URI来查询数据
    resolver.query(uri_user,...)
    //通过URI来更新数据
    resolver.update(uri_user,...)
    //通过URI来删除数据
    resolver.delete(uri_user,...)

 总体上来说,ContentProvider的使用还是蛮简单的,主要在AndroidManifest.xml中对ContentProvider进行参数配置时要注意一些。

2、ContentProvider的工作流程

 前面说不设置process时,ContentProvider则会随着应用的启动而加载、初始化,反之则会在调用时进行加载、初始化,先来看一下ContentProvider随着应用的启动而加载、初始化的流程。

2.1、ContentProvider随应用启动而初始化的工作流程

Android源码分析之Activity启动流程这篇文章说了Application实例是在ActivityThreadhandleBindApplication方法中创建。在讲解这个方法时疏漏了一点,那就是ContentProvider会在这个方法中创建。

    private void handleBindApplication(AppBindData data) {
        ...
        try {
            //通过反射创建Application实例
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
            if (!data.restrictedBackupMode) {
                //如果有ContentProvider,则创建
                if (!ArrayUtils.isEmpty(data.providers)) {
                    //创建ContentProvider实例
                    installContentProviders(app, data.providers);
                }
            }
            try {
                //调用Instrumentation的onCreate方法
                mInstrumentation.onCreate(data.instrumentationArgs);
            } catch (Exception e) {
                ...
            }
            try {
                //调用Application的onCreate方法
                mInstrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                ...
            }
        } finally {
            ...
        }
        // 预加载字体资源
        ...
    }

 上面简化了大量代码,但重要部分还在。可以看到installContentProvidersApplicationonCreate之前调用,所以可以得出结论:ContentProvideronCreateApplicationonCreate之前调用。  下面来看installContentProviders方法的实现。

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        //遍历所有需要随应用启动的ContentProvider
        for (ProviderInfo cpi : providers) {
            ...
            //创建ContentProvider实例
            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();
        }
    }

installContentProviders方法主要是创建ContentProvider实例并在AMS中发布。在调用installProvider方法时传入的holder为null,所以就会在installProvider中创建ContentProvider实例并加入HashMap中进行缓存。

    //创建ContentProvider实例
    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) {
            ...
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            if (context.getPackageName().equals(ai.packageName)) {
                //在应用主进程中创建ContentProvider实例
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                //在单独进程中创建ContentProvider实例,
                c = mInitialApplication;
            } else {
                ...
            }
            ...
            try {
                //拿到类加载器
                final java.lang.ClassLoader cl = c.getClassLoader();
                //通过反射创建ContentProvider实例
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                //拿到ContentProvider对应的IContentProvider接口
                provider = localProvider.getIContentProvider();
                //ContentProvider实例创建失败
                if (provider == null) {
                    ...
                    return null;
                }
                // 调用ContentProvider的attachInfo方法,在该方法里会调用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) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    provider = pr.mProvider;
                } else {
                    //创建ContentProviderHolder实例
                    holder = new ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    //添加ContentProvider信息到mProviderMap
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                ...
            }
        }
        return retHolder;
    }

installProviderAuthoritiesLocked方法主要是创建一个ProviderClientRecord对象来记录ContentProvider信息并存入mProviderMap这个HashMap中以备下次获取,在后面会提到mProviderMap。  关于ContentProvider随着应用的启动而加载、初始化的流程到这里就结束了。下面就来看使用ContentProvider的工作流程。

2.2、ContentProvider在使用时初始化的工作流程

 前面讲过如何使用ContentProvider,所以这里以insert为例,来看ContentResolverinsert方法。  

    public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
                @Nullable ContentValues values) {
        IContentProvider provider = acquireProvider(url);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URL " + url);
        }
        try {
            ...
            //进行数据插入操作
            Uri createdRow = provider.insert(mPackageName, url, values);
            ...
            return createdRow;
        } catch (RemoteException e) {
            return null;
        } finally {
            //释放引用
            releaseProvider(provider);
        }
    }

 首先调用acquireProvider方法获取一个IContentProvider对象引用,而该方法是一个抽象方法,需要在子类实现,经查询,发现它的实现是在ApplicationContentResolver类中,该类是ContextImpl的一个静态内部类,来看这个类的实现。

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

 经查询发现mMainThread就是ActivityThread的实例,下面就来看ActivityThreadacquireProvider方法的实现。

    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 {
            //当缓存中没有ContentProvider示例时,需要通过AMS来创建一个ContentProvider示例
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        if (holder == null) {
            //通过AMS创建ContentProvider对象失败
            return null;
        }

        //由于这里的holder不为null,所以在这里调用该方法主要是为了增加或减少计数引用
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

 首先会从acquireExistingProvider中去查找ContentProvider对象,如果不存在才会调用AMS来创建。

    public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            //ProviderKey的equals与hashCode方法被被重新实现
            final ProviderKey key = new ProviderKey(auth, userId);
            //从mProviderMap中获取ContentProvider信息
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }
            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            
            if (!jBinder.isBinderAlive()) {
                //ContentProvider所在进程被系统杀死
                handleUnstableProviderDiedLocked(jBinder, true);
                return null;
            }
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                //当stable为true时则增加计数引用
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }

 前面讲解installProvider时说过ContenProvider实例创建成功后会将ProviderClientRecord信息保存在mProviderMap这个HashMap中,而这里就是直接从mProviderMap中获取ContenProvider信息。  回到acquireProvider方法。如果从acquireExistingProvider中获取的对象为null,那么就得通过AMS中的getContentProvider方法来创建,来看一下该方法的实现。

    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        ...
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
    //具体创建ContentProvider实例的方法
    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;
        //分段锁
        synchronized(this) {
            ProcessRecord r = null;
            ...
            // 首先检查该ContentProviders是否已经发布
            cpr = mProviderMap.getProviderByName(name, userId);
            ...
            //判断ContentProvider是否在运行
            boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
            //ContentProvider已经在运行
            if (providerRunning) {
                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);
                    //不给调用者提供者对象,它需要自己创建,
                    holder.provider = null;
                    return holder;
                }
                ...
                //获取ContentProviderConnection对象,它继承与Binder,主要作用是连接客户端与ContentProvider
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                        updateLruProcessLocked(cpr.proc, false, null);
                    }
                }
                final int verifiedAdj = cpr.proc.verifiedAdj;
                //更新进程的adj值,该值非常重要,值越大越容易被系统回收,系统进程的adj值基本上都小于0
                boolean success = updateOomAdjLocked(cpr.proc, true);
                //检车adj值是否更新成功,可能存在更新失败的可能
                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
                    success = false;
                }
       
                ...
                if (!success) {
                    //ContentProvider所在进程已被杀死,做一些清理数据的操作
                    appDiedLocked(cpr.proc);
                    
                    if (!lastRef) {
                        // This wasn't the last ref our process had on
                        // the provider...  we have now been killed, bail.
                        return null;
                    }
                    providerRunning = false;
                    conn = null;
                } else {
                    cpr.proc.verifiedAdj = cpr.proc.setAdj;
                }
                ...
            }
            //ContentProvider没有运行运行或者未创建
            if (!providerRunning) {
                ...
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                        != null) {
                     //未获取权限
                    throw new SecurityException(msg);
                }
                //如果ContentProvider未在系统进程中运行,并且系统尚未准备好运行其他进程,则快速失败而不是挂起。
                if (!mProcessesReady
                        && !cpi.processName.equals("system")) {
                    throw new IllegalArgumentException(
                            "Attempt to launch content provider before system ready");
                }

                //确保开启ContentProvider的应用再运行,否则返回null
                if (!mUserController.isUserRunningLocked(userId, 0)) {
                    return null;
                }

                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                //检查该ContentProviders是否已经发布
                cpr = mProviderMap.getProviderByClass(comp, userId);
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    ...
                    try {
                        ...
                        ai = getAppInfoForUser(ai, userId);
                        //创建ContentProviderRecord对象
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } catch (RemoteException ex) {
                        // pm is in same process, this will never happen.
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }
                if (r != null && cpr.canRunHere(r)) {
                    //如果这是一个多进程ContentProvider,那么只需返回其信息并允许调用者实例化它。 只有在ContentProvider与调用者进程的用户相同时才执行此操作,或者可以以root身份运行(因此可以在任何进程中运行)。
                    //当android:multiprocess="true"时会走这里
                    return cpr.newHolder(null);
                }
                //从待启动的ContentProvider查找要启动的ContentProvider
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }
                //如果ContentProvider尚未启动,则启动它。
                if (i >= N) {
                    try {
                        
                        //如果ContentProvider所在进程已存在则直接启动
                        //获取进程信息
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null && !proc.killed) {
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    //通过ActivityThread启动ContentProvider
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            //如果ContentProvider所属进程不存在则开启新的进程
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            //进程创建失败
                            if (proc == null) {
                                return null;
                            }
                        }
                        cpr.launchingApp = proc;
                        //添加到正在启动的集合中
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }
                if (firstClass) {
                    //如果是第一次的话则需要存储信息,根据ComponentName来保存信息
                    mProviderMap.putProviderByClass(comp, cpr);
                }
                //保存ContentProvider信息,根据名称保存
                mProviderMap.putProviderByName(name, cpr);
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    //需要等待
                    conn.waiting = true;
                }
            }
            ...
        }

        // 等待ContentProvider的发布,如果未发布成功则会一直在这里阻塞
        ...
        return cpr != null ? cpr.newHolder(conn) : null;
    }

 上面关于AMS如何创建ContentProviderHolder做了详细的介绍,主要分为ContentProvider是否正在运行这两种情况,如果在运行就会提高ContentProvider所在进程的优先级并创建一个ContentProviderConnection对象。如果未运行则又分为ContentProvider所在进程是否存在的两种情况。如果ContentProvider进程已存在则调用ActivityThreadscheduleInstallProvider方法。

    public void scheduleInstallProvider(ProviderInfo provider) {
        sendMessage(H.INSTALL_PROVIDER, provider);
    }
    //handler里会调用下面的方法
    public void handleInstallProvider(ProviderInfo info) {
        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
        try {
            installContentProviders(mInitialApplication, Lists.newArrayList(info));
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }
    }

 可以发现在handleInstallProvider里也调用了installContentProviders这个方法,该方法在前面就有讲解,这里就不在讲解了。如果ContentProvider进程不存在则创建一个新的进程。创建新进程的流程跟应用的启动流程一样,会创建Application对象,调用installContentProviders方法,具体流程在前面也讲解过,这里就不在过多叙述。  再次回到ActivityThreadacquireProvider方法,当通过AMS获得ContentProviderHolder对象后就会调用installProvider方法,关于该方法,前面讲了一些,这里就主要讲剩下的一些东西。

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        //传入的holder及holder.provider不会为null
        if (holder == null || holder.provider == null) {
            ...
        } else {
            //拿到创建的ContentProvider对象
            provider = holder.provider;
        }

        ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
               ...
            } else {
                //主要是增加或减少引用计数,
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            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);
                    if (noReleaseNeeded) {
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }
        return retHolder;
    }

 主要是做了一个引用计数操作,当stable和unstable引用计数都为0时则移除connection信息。

2.3、inset操作的实现

 前面基本上就把ContentResolver中的acquireProvider讲解完毕,最后该方法返回了一个IContentProvider对象,它的实现是ContentProvider中的Transport类。

    class Transport extends ContentProviderNative {
        ...
        @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);
            }
        }

        @Override
        public String getType(Uri uri) {
            ...
            return ContentProvider.this.getType(uri);
        }

        @Override
        public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
            ...
            try {
                return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId);
            } finally {
                setCallingPackage(original);
            }
        }

        ...
        @Override
        public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
            ...
            try {
                return ContentProvider.this.delete(uri, selection, selectionArgs);
            } finally {
                setCallingPackage(original);
            }
        }

        @Override
        public int update(String callingPkg, Uri uri, ContentValues values, String selection,
                String[] selectionArgs) {
            ...
            try {
                return ContentProvider.this.update(uri, values, selection, selectionArgs);
            } finally {
                setCallingPackage(original);
            }
        }
        ...
    }

 可以发现Transport中的crud操作就是直接对ContentProvider进行crud操作,而Transport又能够通过Binder进行进程间通信。  到此就把ContentProvider的工作流程梳理完毕了。

3、总结

 前面两节主要讲解了ContentProvider的使用、ContentProvider的创建及示例insert方法的具体实现。下面就总结以下几点。

  • 当不设置android:process=":remote"时,ContentProvider会随着应用的启动而初始化,此时ContentProvideronCreate方法会在ApplicationonCreate之前调用。当设置时,ContentProvider会在第一次使用时初始化。
  • 当设置android:multiprocess="true"时,会在每个调用者进程创建一个ContentProvide实例。其设置的android:process=":remote"属性也就无效了
  • 如果ContentProvider在应用主进程创建则crud也在主线程中进程,因为并没有开启子线程,在ContentProvider创建时。

【参考资料】 《Android艺术探索》 [深入理解Android卷二 全文-第七章]深入理解ContentProvider 从源码角度看ContentProvider Android ContentProvider 多进程multiprocess 详解 ContentProvider简介 Android:关于ContentProvider的知识都在这里了! ContentProvider 引发闪退之谜