Android 11 ContentProvider启动流程分析

Android 11 ContentProvider启动流程分析

这一篇主要介绍一下ContentProvider的启动流程,并通过query方法为代表看一下整个操作方法的执行流程。ContentProvider的主要作用是实现数据共享的,它一般是配合ContentResolver 来使用的,我们通过ContentResolver配合URI就可以轻松访问ContentProvider暴露的数据了。ContentProvider作为四大组件之一,在平时开发中并没有其他三大组件使用的那么频繁。在开发过程中我们大部分都是调用系统的ContentProvider很少自己来写,但我们还是需要学习一下它的大体的工作流程。

一、ContentProvider的注册流程

我们先把ContentProvider的启动调用时序图贴出来:

在这里插入图片描述
ContentProvider在应用启动的时候就会启动。我们都知道一个应用程序的启动入口是ActivityThread的main函数,接下来我们就来看一下ActivityThread的main函数的代码:

 public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();//1
        ...
        ActivityThread thread = new ActivityThread();//2
        thread.attach(false, startSeq);//3

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();//4

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在注释1处初始化了主线程的Looper,然后在注释4处开启Looper循环不断的从消息队列中取消息来处理。接下来再注释2处创建了一个ActivityThread对象,接着在注释3处调用了thread的attach方法:

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

在注释1处可以看出它最终调用了ActivityManagerService的attachApplication方法:

    public final void attachApplication(IApplicationThread thread, long startSeq) {
         ...
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            //1
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }

这里在注释1处直接调用了ActivityManagerService的attachApplicationLocked方法。代码如下:

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

            ...
            final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
              ...
            if (app.isolatedEntryPoint != null) {
          ...
            } else if (instr2 != null) {
                //1
                thread.bindApplication(processName, appInfo, providerList,
                        instr2.mClass,
                        profilerInfo, instr2.mArguments,
                        instr2.mWatcher,
                        instr2.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.mDisabledCompatChanges);
            } else {
              ...
            }
    ...
        return true;
    }

在注释1处直接调用了thread的bindApplication方法,这个thread指的就是ApplicationThread是ActivityThread的一个内部类,接下来我们看一下ApplicationThread的bindApplication方法。代码如下:

       public final void bindApplication(String processName, ApplicationInfo appInfo,
                ProviderInfoList providerList, 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,
                String buildSerial, AutofillOptions autofillOptions,
                ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
                  ...
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();//1
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providerList.getList();
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            data.autofillOptions = autofillOptions;
            data.contentCaptureOptions = contentCaptureOptions;
            data.disabledCompatChanges = disabledCompatChanges;
            sendMessage(H.BIND_APPLICATION, data);//2
        }

这里在注释1处创建了一个AppBindData对象,并把传过来的那些信息保存到AppBindData对象中,然后通过注释2处调用ActivityThread的sendMessage方法把data作为参数发送了一条BIND_APPLICATION消息。然后在H类的handleMessage中进行处理调用ActivityThread的handleBindApplication方法。代码如下:

 private void handleBindApplication(AppBindData data) {
            ...
            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi,
                    appContext.getOpPackageName());//1

            try {
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();//2
            } catch (Exception e) {
               ...
            }

            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);//3
            ...
        } else {
            ...
        }

        ...

        Application app;
        ...
        try {
            app = data.info.makeApplication(data.restrictedBackupMode, null);//4
            app.setAutofillOptions(data.autofillOptions);
            app.setContentCaptureOptions(data.contentCaptureOptions);
            mInitialApplication = app;
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    installContentProviders(app, data.providers);//5
                }
            }
            ...
                mInstrumentation.callApplicationOnCreate(app);//6
           ...
        }
    }

这个方法比较长,我们只看关键部分。注释1处创建了一个ContextImpl类型的对象instrContext ,接着在注释2处通过反射创建了一个Instrumentation对象,然后在注释3处调用了它的init方法进行了初始化工作,接着在注释4处创建了一个Application对象并在注释6处调用其onCreate方法,到这一步说明ContentProvider所在的应用进程已启动完毕,在这之前在注释5处调用了installContentProviders方法启动了ContentProvider。接下来我们看一下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 {
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);//2
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

这个方法通过遍历分别拿到这个应用所有的ContentProvider的信息ProviderInfo然后通过调用installProvider方法对每个ContentProvider进行启动,接着在注释2处调用ActivityManagerService的publishContentProviders的方法把ContentProvider存储到ProviderMap类型的mProviderMap当中,这个mProviderMap起到缓存的作用,防止每次使用相同的Content Provider时都会调用ActivityManagerService的getContentProvider方法。我们来着重看一下installProvider方法是如何启动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) {
            ...

            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    packageInfo = getSystemContext().mPackageInfo;
                }
                //1
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                        //2
                provider = localProvider.getIContentProvider();
               ...
               //3
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                ...
            }
        } else {
            provider = holder.provider;
            ...
        }

        ContentProviderHolder retHolder;

            ...
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                     ...
                    provider = pr.mProvider;
                } else {
                    //4
                    holder = new ContentProviderHolder(info);
                    //5
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } 
            ...
        return retHolder;
    }

这里在注释1处通过反射对ContentProvider进行了实例化,然后在注释2处调用了ContentProvider的getIContentProvider方法,这个方法返回的其实是ContentProvider的内部类Transport,Transport继承自ContentProviderNative,然后在注释4处创建一个ContentProviderHolder类型的hodler并在注释5处把Transport赋值给holder的成员变量provider。这一段逻辑对后面分析操作方法query等的调用流程非常重要,这里我们着重提一下。这里我们还是重点看一下注释3处调用了ContentProvider的
attachInfo方法,代码如下:

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
            ...
            //1
            ContentProvider.this.onCreate();
    }

这里省略了一些配置的逻辑,重点是这里终于回调了ContentProvider的onCreate方法。到这里ContentProvider的创建过就算分析完了。接下来我们分析一下query方法的调用流程。

二、ContentResolver的调用过程

接下来我们就来分析一下ContentProvider操作方法的调用流程,这里我们主要看一下query,其他的方法也大同小异。我们想把query方法的调用时序图贴出来:
在这里插入图片描述

我们一般调用内容提供者的方法时是通过context的getContentResolver方法具体的实现类就是ContentImpl,接下来我们来看一下ContextImpl的getContentResolver方法:

private final ApplicationContentResolver mContentResolver;
@Override
public ContentResolver getContentResolver() {
    return mContentResolver;
}

这里的mContentResolver其实是ApplicationContentResolver,接下来在我们使用的时候会调用ContentResolver的query方法,代码如下:

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Objects.requireNonNull(uri, "uri");

    ...
    //1
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = SystemClock.uptimeMillis();

        ICancellationSignal remoteCancellationSignal = null;
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
            remoteCancellationSignal = unstableProvider.createCancellationSignal();
            cancellationSignal.setRemote(remoteCancellationSignal);
        }
        try {
            qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
                    queryArgs, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            ...
        }
       ...
    }
}

这个query方法有几个重载的方法,我们这里只看最终调用的那个。接着会调用acquireUnstableProvider方法,代码如下:

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

这里会在注释1处调用了两个参数的acquireUnstableProvider方法,这个方法是个抽象方法,具体的实现是在我们上面提到的ContentImpl.ApplicationContentResolver这个类。接下来我们看一下它具体的实现,代码如下:

@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
  	 //1
     return mMainThread.acquireProvider(c,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), false);
}

这里注释1处又会调用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();
    }
 	...
    return holder.provider;//3
}

这个方法会在注释1先从缓存mProviderMap这个ArrayMap中取,如果没有,再调用注释2处ActivityManagerService的getContentProvider获取ContentProviderHolder类型的holder对象。然后最后返回holder.provider这里返回的IContentProvider的实现类其实是Transport这个类。那我们是怎么知道这里返回的是Transport这个类呢?其实在上面我们在分析ContentProvider注册的时候在ActivityThread的installProvider方法时提到过,这里我们重新贴一下代码:

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) {
            ...

            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    packageInfo = getSystemContext().mPackageInfo;
                }
                //1
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                        //2
                provider = localProvider.getIContentProvider();
               ...
               //3
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                ...
            }
        } else {
            provider = holder.provider;
            ...
        }

        ContentProviderHolder retHolder;

            ...
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                     ...
                    provider = pr.mProvider;
                } else {
                    //4
                    holder = new ContentProviderHolder(info);
                    //5
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } 
            ...
        return retHolder;
    }

这里我们只看注释2的地方,这里调用了ContentProvider的getIContentProvider方法代码如下:

private Transport mTransport = new Transport();
@UnsupportedAppUsage
public IContentProvider getIContentProvider() {
    return mTransport;
}
这里的mTransport变量其实就是TransportTransport类的代码如下:
class Transport extends ContentProviderNative {
    volatile AppOpsManager mAppOpsManager = null;
    volatile int mReadOp = AppOpsManager.OP_NONE;
    volatile int mWriteOp = AppOpsManager.OP_NONE;
    volatile ContentInterface mInterface = ContentProvider.this;

    ContentProvider getContentProvider() {
        return ContentProvider.this;
    }

    @Override
    public String getProviderName() {
        return getContentProvider().getClass().getName();
    }

    @Override
    public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable ICancellationSignal cancellationSignal) {
        uri = validateIncomingUri(uri);
        uri = maybeGetUriWithoutUserId(uri);
        if (enforceReadPermission(callingPkg, attributionTag, uri, null)
                != AppOpsManager.MODE_ALLOWED) {
                     if (projection != null) {
                return new MatrixCursor(projection, 0);
            }
            Cursor cursor;
            final Pair<String, String> original = setCallingPackage(
                    new Pair<>(callingPkg, attributionTag));
            try {
                //1
                cursor = mInterface.query(
                        uri, projection, queryArgs,
                        CancellationSignal.fromTransport(cancellationSignal));
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingPackage(original);
            }
            if (cursor == null) {
                return null;
            }

            return new MatrixCursor(cursor.getColumnNames(), 0);
        }
        Trace.traceBegin(TRACE_TAG_DATABASE, "query");
        final Pair<String, String> original = setCallingPackage(
                new Pair<>(callingPkg, attributionTag));
        try {
            return mInterface.query(
                    uri, projection, queryArgs,
                    CancellationSignal.fromTransport(cancellationSignal));
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        } finally {
            setCallingPackage(original);
            Trace.traceEnd(TRACE_TAG_DATABASE);
        }
    }

这里会在注释1处调用ContentProvider的query方法。这样整体query的调用流程就算分析完了,具体细节大家可以自行分析。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值