0
点赞
收藏
分享

微信扫一扫

Android 10 Content Provider 工作过程

高子歌 2021-09-19 阅读 107
Android

前言

Content Provider作为四大组件之一,即内容提供者,与其他组件比使用率低,它主要用于进程内和进程间的数据共享。ContentProvider启动后外界都可以通过它提供的增删改查四个接口来操作数据源,通过这任何一个方法都可以触发ContentProvider所在进程的启动。这部分内容我们将分为两部分讲解,query到AMS过程和AMS到Content Provider的启动过程。

query到AMS过程

为了便于理解query到AMS过程,我们先看它调用过程序列图,如图所示:



在查询数据时,我们需要调用ContentResolver的query方法,在这方法里面又调用acquireUnstableProvider方法,如代码所示。

   public final Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
                              @Nullable String[] projection, @Nullable Bundle queryArgs,
                              @Nullable CancellationSignal cancellationSignal) {
        //1
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        //2
        qCursor = unstableProvider.query(mPackageName, uri, projection,
                queryArgs, remoteCancellationSignal);
    }

1 调用acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象。
2 unstableProvider去调用query方法查询数据。
IContentProvider实则是ContentProvider的本地代理,实现的方法在ContentProvider里,然后我们再看看acquireUnstableProvider方法做了什么,代码所示。

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

acquireUnstableProvider调用了ContentResolver的抽象方法,实则调用了ContentResolver的子类ApplicationContentResolver,也是ContextImpl的内部类。我们在看下什么时候初始化ApplicationContentResolver子类对象的,去ContextImpl构造方法,代码如下。

   private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
                        @NonNull LoadedApk packageInfo, @Nullable String splitName,
                        @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
                        @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
        mOuterContext = this;
        mMainThread = mainThread;
        mActivityToken = activityToken;
        //1
        mContentResolver = new ApplicationContentResolver(this, mainThread);
    }

1 当Application初始化时候前都创建了ContextImpl实例,并且把ApplicationContentResolver对象赋值给mContentResolver。
ApplicationContentResolver的acquireUnstableProvider方法调用了ActivityThread的acquireProvider方法。

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;
        }
        ContentProviderHolder holder = null;
        try {
            synchronized (getGetProviderLock(auth, userId)) {
                //2
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
            }
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        //3
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

1 在ActivityThread的mProviderMap全局变量查找是否存在IContentProvider类型的对象即具体实现者ContentProvider,没有都继续走第二步,后面会讲mProviderMap的存入过程。
2 在AMS的getContentProvider去获取。
3 获取到新的IContentProvider对象包装后存在mProviderMap中。
第二点调用了AMS的getContentProvider方法,这是跨进程调用。

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

然后调用了getContentProviderImpl方法,如代码所示。

  private ContentProviderHolder 
  getContentProviderImpl(IApplicationThread caller,
                                                         String name, IBinder token, int callingUid, String callingPackage, String callingTag,
                                                         boolean stable, int userId) {
    ContentProviderRecord cpr;
    cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
    if (proc != null && proc.thread != null && !proc.killed) {
            //1
            proc.thread.scheduleInstallProvider(cpi);
        } else {
           //2
            proc = startProcessLocked(cpi.processName,
                    cpr.appInfo, false, 0,
                    new HostingRecord("content provider",
                            new ComponentName(cpi.applicationInfo.packageName,
                                    cpi.name)), false, false, false);
        }
        mProviderMap.putProviderByName(name, cpr);
        return cpr.newHolder(conn);
    }

1 创建的ContentProvider的进程是否存在,都会调用这个方法。
2 需要创建新的进程。
由于分析2点包含了1点内容,我们继续分析2点的方法。startProcessLocked方法创建一个新的进程,新进程创建好后调用了ActivityThread的main方法,绑定进程和应用关联,下面是main的具体代码。

    public static void main(String[] args) {
        Process.setArgV0("<pre-initialized>");
        //为主线程创建loop对象,我们在主线程使用Handler时候没有初始化都可以使用,因为这里做了初始化。
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        //开始死循环获得消息
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

调用了ActivityThread的attach方法。接着我们继续看源码,如下。

    private void attach(boolean system, long startSeq) {
        if (!system) {
            final IActivityManager mgr = ActivityManager.getService();
            try {
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

attach方法中,调用了AMS attachApplication方法。上面的分析都是query到AMS过程的代码。

Content Provider 启动过程

再看源码之前先看一张启动过程的代码时序图,让我们更容易读懂源码过程,如图:



在创建Application时需要调用AMS的attachApplication方法,代码如下。

    private void handleBindApplication(AppBindData data) {
        //设置进程名字
        Process.setArgV0(data.processName);

        //创建LoadedApk实例
        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
        //1
        final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                appContext.getOpPackageName());

        final ClassLoader cl = instrContext.getClassLoader();
        //2
        mInstrumentation = (Instrumentation)
                cl.loadClass(data.instrumentationName.getClassName()).newInstance();
        //3
        mInstrumentation.init(this, instrContext, appContext, component,
                data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
        Application app;
        //4
        app = data.info.makeApplication(data.restrictedBackupMode, null);

        mInitialApplication = app;

        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                //5
                installContentProviders(app, data.providers);
            }
        }
        //6
        mInstrumentation.onCreate(data.instrumentationArgs);
    }

1)创建ContextImpl上下文。2) 利用反射机制创建Instrumentation的实例。3)初始化Instrumentation且调用init方法。4)调用LoadedApk的makeApplication方法创建Application对象。5)创建ContextProvider并且调用onCreate方法。6)调用Application的onCreate方法。
从上面的5和6的标记点看出ContentProvider比Application先调用onCreate方法,这是与其他三大组件有所不同的地方。继续看5点调用installContentProviders方法。

 private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        for (ProviderInfo cpi : providers) {
            //1
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
            //2
            ActivityManager.getService().publishContentProviders(
                    getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

1)installContentProviders调用了installProvider来创建ContentProviderHolder类的对象。2)通过调用AMS的publishContentProviders方法将ContentProviderHolder数组存在ProviderMap中。在1点中调用了installProvider方法,代码如下。

  private ContentProviderHolder installProvider(Context context,
                                                  ContentProviderHolder holder, ProviderInfo info,
                                                  boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        final ClassLoader cl = c.getClassLoader();
        LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
        if (packageInfo == null) {
            packageInfo = getSystemContext().mPackageInfo;
        }
        //1
        localProvider = packageInfo.getAppFactory()
                .instantiateProvider(cl, info.name);
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            return null;
        }
        //2
        localProvider.attachInfo(c, info);
        holder = new ContentProviderHolder(info);
        holder.provider = provider;
        holder.noReleaseNeeded = true;
        //3
        pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
        mLocalProviders.put(jBinder, pr);
        mLocalProvidersByName.put(cname, pr);
        
        return retHolder;
    }

在installProvider方法中,1 通过反射创建ContentProvider对象。2 调用了attachInfo方法,然后调用了ContextProvider的onCreate方法,完成了ContextProvider的创建。3 调用了installProviderAuthoritiesLocked方法,主要工作是把IContentProvider类和ContentProvider类封装在ProviderClientRecord中且保存在mProviderMap的map里面,这个都是之前分析acquireExistingProvider方法获取存在缓存的IContentProvider类型对象。上面都是所有的Content Provider 启动过程的分析。

举报

相关推荐

0 条评论