Android之ContentProvider源码解析

栏目: Android · 发布时间: 5年前

内容简介:仅仅一个自定义类还不够,

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 引发闪退之谜


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java技术手册(第6版)

Java技术手册(第6版)

Benjamin J Evans、David Flanagan / 安道 / 人民邮电出版社 / 2015-12-1 / 79.00

《Java技术手册 第6版》为《Java 技术手册》的升级版,涵盖全新的Java 7 和Java 8。第1部分介绍Java 编程语言和Java 平台,主要内容有Java 环境、Java 基本句法、Java 面向对象编程、Java 类型系统、Java的面向对象设计、Java 实现内存管理和并发编程的方式。第2部分通过大量示例来阐述如何在Java 环境中完成实际的编程任务,主要内容有编程和文档约定,使......一起来看看 《Java技术手册(第6版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码