ContentProvider启动流程及原理

QuibblerQuibbler 2020-04-21 约 20 分钟 1990 次阅读

ContentProvider启动流程及原理

ContentProvider作为Android的四大组件之一是非常重要的,它的用法参考ContentProvider的用法。本篇主要顺着源码理解ContentProvider的启动和底层原理。

1、ContentProvider启动

当一个APP启动的时候从ActivityThread的main方法入口加载的过程中就会初始化本应用的ContentProvider。主要流程如下:


应用启动的时候会从ActivityThread的入口:

public static void main(String[] args) {
        ... ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

    }

接着里面通过IActivityManager AIDL接口和系统服务对接。在ActivityManagerService中经过一系列调用链:attachApplication->attachApplicationLocked->handleAppDiedLocked。

到了IApplicationThread AIDL接口,而IApplicationThread 的实现正是ActivityThread,于是再回到APP层:bindApplication->mH->handleBindApplication->installContentProviders->installProvider

通过LoadedApk去加载实例化ContentProvider。

private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
            ...
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
        ...
    }

方法最后调用ContentProvide的dattachInfo(Context context, ProviderInfo info)方法:

localProvider.attachInfo(c, info);

最后调用ContentProvider的onCreate()方法,在这里初始化应用的ContentProvider,执行必要的初始化操作,比如LeakCanary库通过ContentProvider巧妙的实现了监听接口的注册。

private void attachInfo(Context context, ProviderInfo info, boolean 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) {
            ...
            ContentProvider.this.onCreate();
        }
    }

2、ContentProvider增删改查

ContentProvider的增删改查主要借助ContentResolver完成。增删改查流程基本差不多,这里只捋顺使用ContentProvider插入方法insert()的流程。

2.1、获取ContentResolver

调用getContentResolver()方法来获取ContentResolver执行增删改查。

getContentResolver().insert(Uri url,ContentValues values)

ContentResolver 

public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

mBase已经比较熟悉了,它的实现类是ContextImpl 

public ContentResolver getContentResolver() {
        return mContentResolver;
    }

ContextImpl在创建的时候,构造函数中会初始化自己的成员变量mContentResolver 

private ContextImpl(...) {
        ...
        mContentResolver = new ApplicationContentResolver(this, mainThread);
    }

ApplicationContentResolver类是定义在ContextImpl中的静态内部类,继承自ContentResolver。

private static final class ApplicationContentResolver extends ContentResolver {
            private final ActivityThread mMainThread;
            public ApplicationContentResolver(Context context, ActivityThread mainThread) {
                super(context);
                mMainThread = Preconditions.checkNotNull(mainThread);
            }
            ...
    }

2.2、insert()

好了,到现在已经初始化完成需要的ContentResolver对象。继续看看它的insert()方法如何实现的。

public final Uri insert(Uri url,ContentValues values) {
        Preconditions.checkNotNull(url, "url");
        try {
            if (mWrapped != null) return mWrapped.insert(url, values);
        } catch (RemoteException e) {
            return null;
        }
        IContentProvider provider = acquireProvider(url);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URL " + url);
        }
        try {
            long startTime = SystemClock.uptimeMillis();
            Uri createdRow = provider.insert(mPackageName, url, values);
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
            return createdRow;
        } catch (RemoteException e) {
            return null;
        } finally {
            releaseProvider(provider);
        }
    }

2.3、获取ContentProvider

里面通过acquireProvider(Uri uri)方法获取到对应的ContentProvider。

public final IContentProvider acquireProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
            return null;
        }
        final String auth = uri.getAuthority();
        if (auth != null) {
            return acquireProvider(mContext, auth);
        }
        return null;
    }

接着又调用了acquireProvider(Context c, String name)方法,这个方法在ContentProvider中是抽象方法:

protected abstract IContentProvider acquireProvider(Context c, String name);

该方法的实现就是前面已经提到的静态内部类ApplicationContentResolver。

protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
    }

接着到了当前APP运行的ActivityThread,经过一系列的调用链最终初始化本应用的ContentProvider。

IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable)
        ...
    }

2.4、ContentProvier insert()

再回到ContentResolve的insert()方法中,获取到IContentProvider对象实例之后,调用执行插入操作:

Uri createdRow = provider.insert(mPackageName, url, values);

IContentProvider接口的实现类是ContentProviderNative ,但是ContentProviderNative仍然是抽象类,它的最终实现在IContentProvider中定义的IContentProvider.Transport类。

内部通过IBinder实现进程间通信,Binder继承了IBinder的接口。也就是ContentProvider可以提供跨进程操作的原因。

@UnsupportedAppUsage
    private IBinder mRemote;

所有的增删改查操作都通过这个mRemote IBinder对象执行。

public Uri insert(String callingPkg, Uri url, ContentValues values) throws RemoteException
    {
            ...
            mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0);
            ...
    }

其它应用调用增删改查,通过IContentProvider->ContentProviderNative:IBinder->ContentProvier.Transport最终在本应用进程执行。ContentProvier.Transport中的insert()方法:

public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
            ...
            try {
                return maybeAddUserId(mInterface.insert(uri, initialValues), userId);
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingPackage(original);
                Trace.traceEnd(TRACE_TAG_DATABASE);
            }
        }

mInterface是ContentInterface接口类型,就是当前ContentProvider。

class Transport extends ContentProviderNative {
            ...
            volatile ContentInterface mInterface = ContentProvider.this;
    }

另外发现,处理单个的增删改查方法外,还有批量的方法。比如bulkInsert

@Override
    public int bulkInsert(String callingPkg, Uri url, ContentValues[] values) throws RemoteException {
        ...
    }

推荐博客:

       ContentProvider启动过程分析

       ContentProvider原理分析

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w