ContentProvider 浅谈

本文主要是介绍android四大组件之一,ContentProvider 的使用以及从源码层面详细解释ContentProvider。

ContentProvider简单使用:

1:继承ContentProvider 并重写内部的onCreate 、query、getType、insert、delete、update方法

public class BookProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

2:androidManifest.xml 中注册 :


        
    
   

3: Provider的调用:

private void acceptProvider(){
        Uri uri = Uri.parse("content://com.liuluchao.multiprocess.provider");
        //通过query去查询BookProvider中的数据
        getContentResolver().query(uri,null,null,null,null);
        getContentResolver().query(uri,null,null,null,null);
        getContentResolver().query(uri,null,null,null,null);

    }

ok 以上就是 ContentProvider的基本使用。

接下来我们将从源码角度对ContentProvider进行内部分析。

ContentProvider 加载

众所周知android中当一个应用启动时,会首先执行ActivityThread.main 方法

在改方法中

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

}

可以看出创建了一个ActivityThread ,并调用了气attach方法,
而在attach方法中:

 private void attach(boolean system){
    ...
    final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
      ....
}

可以看出在attach方法中 调用 attachApplication 该方法对具体执行时在ActivityManagerService 类中执行。

@Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
           ...
            attachApplicationLocked(thread, callingPid);
            ...
        }
    }

在该方法中可以看出其实是直接调用了 attachApplicationLocked 该方法

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid){
    ....
         thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
    ....
}

其内部直接调用了thread.bindApplication(….) 方法,该方法对实现具体是在ApplicationThreadNative内部具体实现


        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List
   
     providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map
    
      services, Bundle coreSettings) { .... sendMessage(H.BIND_APPLICATION, data); }
    
   

可以看出这个过程其实是一个跨进程完成的,bindApplication 中通过mH Handler 切换到ActivityThread 中执行

   case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

最终是在handleBindApplication 方法中创建Application 以及ContentProvider

private void handleBindApplication(AppBindData data){

    ....
        // 创建ContextImpl 以及  Instrumentation
        final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                    + data.instrumentationName + ": " + e.toString(), e);
            }
            ...
    //创建Application 
 Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;
      //启动当前ContentProvider 并调用其 onCreate()方法
    if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                }
            }

    ....
    mInstrumentation.onCreate(data.instrumentationArgs);
}
    //该方法中可以看到 遍历ProviderInfo  并调用 installProvider来启动它们 同时将已启动的ContentProvider 存储到result  (map) 中
    private void installContentProviders(
            Context context, List
   
     providers) { final ArrayList
    
      results = new ArrayList
     
      (); 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()); } IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } try { ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { .... //通过类加载器加载ContentProvider final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); ... localProvider.attachInfo(c, info); } //该方法内部执行了ContentProvider.onCreate 方法 private void attachInfo(Context context, ProviderInfo info, boolean testing) { mNoPerms = testing; .... ContentProvider.this.onCreate(); } }
     
    
   

需要注意点是 ActivityThread 中会先加载ContentProvider 然后在调用 Application的onCreate方法
至此 ContentProvider 的创建已经完成。

以下是大致流程图:

《ContentProvider 浅谈》

ok 到这里相信各位看官对ContentProvider的创建已经有了一定的了解。

接下来我们看外部是如果通过ContentProvider 提供的四个接口方法对来操作内部的数据的。
下面我们就拿query方法进行分析

我们都知道访问ContentProvider 需要通过ContentResolver来进行访问。而ContentResolver 是需要通过Context.getContentResolver() 方法进行获取;

@Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }<

通过上述源码 我们可以看到 其实最终是在ContextImpl中执行了 getContentResolver 该方法

    private final ApplicationContentResolver mContentResolver;
   mContentResolver = new ApplicationContentResolver(this, mainThread, user);
  @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

  
 

    可以看出 ContentResolver 其实是一个ApplicationContentResolver对象;而通过源码 我们可以看出
    ApplicationContentResolver 本质上是一个ContentReslover 子类

     private static final class ApplicationContentResolver extends ContentResolver {}

    而 ContentReslover 本质上时一个抽象类

    public abstract class ContentResolver{}

    ok 现在ContentResolver 对象的获取已经基本明白了,接下来我们看query方法是如何实现数据查询的。

    首先在query方法中我们可以看出:

    public final @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
                @Nullable String selection, @Nullable String[] selectionArgs,
                @Nullable String sortOrder) {
            return query(uri, projection, selection, selectionArgs, sortOrder, null);
        }

    内部直接调用了query 方法,而在该方法中我们可以看到

    public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
                @Nullable String selection, @Nullable String[] selectionArgs,
                @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
            Preconditions.checkNotNull(uri, "uri");
            IContentProvider unstableProvider = acquireUnstableProvider(uri);
            if (unstableProvider == null) {
                return null;
            }
            IContentProvider stableProvider = null;
            Cursor qCursor = null;
            ...
                try {
                    qCursor = unstableProvider.query(mPackageName, uri, projection,
                            selection, selectionArgs, sortOrder, remoteCancellationSignal);
    
                } catch (DeadObjectException e) {
                   ...
                    stableProvider = acquireProvider(uri);
                    qCursor = stableProvider.query(mPackageName, uri, projection,
                            selection, selectionArgs, sortOrder, remoteCancellationSignal);
                }
                if (qCursor == null) {
                    return null;
                }
    
                // Force query execution.  Might fail and throw a runtime exception here.
                qCursor.getCount();
               ....
            } catch (RemoteException e) {
    
                return null;
            } finally {
                ...
               }
        }
    

    内部首先通过acquireUnstableProvider 方法取获取一个 IContentProvider 接口对象。然后直接 调用了 query方法 返回了一个Cursor 对象。

    下面我们对上述代码进行详细的分析 ,首先分析 IContentProvider 接口的获取;

    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 我们可以看到 内部 调用了

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

    该抽象方法,在上面我们已经提到了ContentResolver 的实现类是 ApplicationContentResolver

    因此 我们只需要看 ApplicationContentResolver 类中 acquireProvider 方法是如何去实现的。

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

    可以看到该方法内部 实质上时调用了ActivityThread.acquirProvider(…)方法;
    因此我们继续看ActivityThread中该方法是如何实现的。

    
        public final IContentProvider acquireProvider(
                Context c, String auth, int userId, boolean stable) {
            final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
            if (provider != null) {
                return provider;
            }
            IActivityManager.ContentProviderHolder holder = null;
            try {
                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;
        }

    从上述代码中可以看出 ActivityThread 中会首先去寻找 目标中是否已经存在ContentProvider 如果存在则直接返回;

      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;
                ....
                return provider;
            }
        }

    通过上述代码我们可以看到 ActivityThread是通过Map 来存储一个已经启动的ContentProvider对象的。
    如果当前没有启动的ContentProvider 则通过源码我们可以看到内部通过 ActivityManagerNative.getDefault().getContentProvider(…)该方法向AMS发送请求,让其启动目标ContentProvider。

    那么ActivityManagerService中是如何启动目标ContentProvider的。接下来我们通过源码可以看到

    
        @Override
        public final ContentProviderHolder getContentProvider(
                IApplicationThread caller, String name, int userId, boolean stable) {
             ...
            return getContentProviderImpl(caller, name, null, stable, userId);
        }
    

    其内部追饿调用了 getContentProviderImpl 方法

    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
                String name, IBinder token, boolean stable, int userId){
                            .....
                              proc = startProcessLocked(cpi.processName,
                                        cpr.appInfo, false, 0, "content provider",
                                        new ComponentName(cpi.applicationInfo.packageName,
                                                cpi.name), false, false, false);
                                       }

    内部会首先启动ContentProvider所在的进程,然后在启动ContentProvider,所以 ContentProvider被启动时会伴随着进程的启动。

    而新进程启动后的入口方法为ActivityThread的main方法,在新进程中如何启动一个ContentProvider 我们在文章的开头已经详解解释了,所以这里不再做过多的解释。

    至此 ContentProvider 已经成功启动,然后其他应用可以通过AMS来访问这个ContentProvider。拿到ContentProvider以后,就可以通过它所提供的接口方法来访问了;

    注: 这里的ContentProvider 并不是原始的ContentProvider,而是ContentProvider的Binder类型对象 IContentProvider。而当我们执行query方法时,实质上最终调用到了 ContentProvider.Transport 中的query方法。

    
            @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) {
                    // The caller has no access to the data, so return an empty cursor with
                    // the columns in the requested order. The caller may ask for an invalid
                    // column and we would not catch that but this is not a problem in practice.
                    // We do not call ContentProvider#query with a modified where clause since
                    // the implementation is not guaranteed to be backed by a SQL database, hence
                    // it may not handle properly the tautology where clause we would have created.
                    if (projection != null) {
                        return new MatrixCursor(projection, 0);
                    }
    
                    // Null projection means all columns but we have no idea which they are.
                    // However, the caller may be expecting to access them my index. Hence,
                    // we have to execute the query as if allowed to get a cursor with the
                    // columns. We then use the column names to return an empty cursor.
                    Cursor cursor = ContentProvider.this.query(uri, projection, selection,
                            selectionArgs, sortOrder, CancellationSignal.fromTransport(
                                    cancellationSignal));
                    if (cursor == null) {
                        return null;
                    }
    
                    // Return an empty cursor for all columns.
                    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.Transport的query方法调用了ContentProvider的query方法,query方法的执行结果在通过Binder返回给调用者,这样一来整个调用过程就完成了。

    总结:ContentProvider中的查询、添加、更新、删除等操作,实际上都是通过IContentProvider该Binder接口来调用,外界无法直接访问ContentProvider,只能通过ActivityManagerService根据Uri来获取对应的ContentProvider的Binder接口IContentProvider,然后在通过IContentProvider来访问ContentProvider中的数据源。

    《ContentProvider 浅谈》

        原文作者:ActivityManagerService
        原文地址: https://juejin.im/entry/581d9e628ac247004ff3b377
        本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
    点赞