Android源码解析--ContentProvider的创建及启动流程

分析源码:Android 8.0

本文主要分析调用getContentResolver方法以后,获取到ContentProvider的流程,或者说getContentResolver方法以后,和ContentProvider建立联系的流程。

Provider的启动及创建流程

在前面一篇博客 ContentResolver与ContentProvider的搭配使用 讲到了ContentResolver的使用以及对ContentProvider的访问, 那么这其中的具体流程是怎么执行的呢, 接下来就将这个过程进行具体的分析。

通常我们都是通过Context获取到ContentResolver去读取ContentProvider的数据,以下是在前一篇博客Activity中的示例代码:

...
mResolver = getContentResolver();
...
//通过ContentResolver去查询数据
Cursor cursor = mResolver.query(URI_DICT, mProjections, null, null, null);

ContentResolver对象从何而来?

那获取到的ContentResolver是一个什么对象呢?它又是如何跟ContentProvider进行交互的呢? 跟踪getContentResolver()的源码:

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

返回的是 mBase.getContentResolver(), 了解过Context家族就知道,mBase其实是ContextImpl对象, 继续在ContextImpl代码中去跟进:

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

mContentResolver实际上是一个ApplicationContentResolver对象,它是ContextImpl的内部类,也是在ContextImpl的构造方法中被初始化的, 所以所有的Context对象都可以获取ContentResolver。

ContentResolver的query方法

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Preconditions.checkNotNull(uri, "uri");
	// 注释1 注释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, uri, projection,
                    queryArgs, remoteCancellationSignal);
        } catch (DeadObjectException e) {
			// 注释2 注释2
            // The remote process has died...  but we only hold an unstable
            // reference though, so we might recover!!!  Let's try!!!!
            // This is exciting!!1!!1!!!!1
            unstableProviderDied(unstableProvider);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(
                    mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
		// 注释3 注释3
        // Force query execution.  Might fail and throw a runtime exception here.
        qCursor.getCount();
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);

        // Wrap the cursor object into CursorWrapperInner object.
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        return wrapper; // 注释4 注释4
    } catch (RemoteException e) {
        // Arbitrary and not worth documenting, as Activity
        // Manager will kill this process shortly anyway.
        return null;
    } finally {
        if (qCursor != null) {
            qCursor.close();
        }
        if (cancellationSignal != null) {
            cancellationSignal.setRemote(null);
        }
        if (unstableProvider != null) {
            releaseUnstableProvider(unstableProvider);
        }
        if (stableProvider != null) {
            releaseProvider(stableProvider);
        }
    }
}

在上述代码中,写了4个注释:

  • 注释1: 首先调用了acquireUnstableProvider()方法,没有获取到provider才在下面 注释2 的代码中通过acquireProvider(uri)去获取provider;

  • 注释2: 此处的英文注释也解释了, 远程的provider所在的进程已经挂了,但是我们持有的是unstable的provider, 所以不影响我们自己的进程,现在尝试通过acquireProvider(uri)再去启动provider所在的进程,并获取provider对象;

  • 注释3: 此处调用了一次qCursor.getCount(), 这个只是用来访问一次远程的provider,如果provider还没有正常启动起来,则此处直接会抛出RuntimeException;

  • 注释4: 此处最终通过query方法获取到了Cursor对象,但是这个Cursor对象实际上是一个CursorWrapperInner对象;

另外,需要特别解释的是,在Android4.1之前,我们的应用程序访问了ContentProvider,但是这个ContentProvider意外挂了,这个时候我们的应用程序也将被连带杀死!这是Android处于对数据安全的考虑而做的决定,不过貌似Google也感觉这样的方式不太友好,所以在4.1以后提出了stable和unstable的概念。对于ContentResolver的query方法,我们将默认使用unstable的ContentProvider。所以注释1先调用了acquireUnstableProvider(),没获取到时注释2 才调用acquireProvider(uri),实际上这两个方法的具体实现都在ContentResolver的具体实现类,即前面说到的ApplicationContentResolver, 他们内部都是调用ActivityThread的一个方法,只是最后传入了一个 isStable的 boolean值,具体代码如下:

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

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

跨进程通信:ContentResolver与ContentProvider建立联系的过程

以上代码已经解释了通过ContentResolver方法返回的Cursor对象, 不过还没解释清楚我们ContentResolver是如何与ContentProvider关联到一起的, 这个就的继续深挖 mMainThread.acquireProvider()这个方法了。 实际上毫无意外,Android中绝大部分跨进程通信都是通过Binder通信完成的,我们先到ActivityThread中接着跟踪:

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 {
	// 注释2    
        holder = ActivityManager.getService().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;
    }
	// 注释3
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

上述代码标注的注释里面:

  • 注释1: 这里先去查找是否已经查找过相同的provider,有则直接返回;
  • 注释2: 如果之前没有获取过,则需要与AMS跨进程通信,获取到provider对象的本地代理;
  • 注释3: installProvider()方法比较特殊,对于当前进程(即调用getContentResolver方法的进程)来说,此方法对IContentProvider进行一些封装, 而对与ContentProvider所在的进程来说,则是初始化ContentProvider所在进程以及ContentProvider本身;

针对注释3,额外解释一下,每个进程,都存在一个ActivityThread对象,ActivityThread对象又可以与AMS(ActivityManagerService)进行交互,而AMS就是用来管理四大组件的服务。这里我们的调用getContentResolver方法的应用进程中,会通过ActivityThread对象去与AMS通信,然后AMS启动ContentProvider所在进程以及ContentProvider对象, 可用一个图来简单表示:

在这里插入图片描述

注释1很简单,没什么好讲的,注释2 会和AMS通信, 这里先结束注释2,然后再解释注释3.

注释2 ActivityManager.getService().getContentProvider()

这个方法会调用到AMS的getContentProvider()方法, 然后调用到getContentProviderImpl()方法

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, boolean stable, int userId) {
 ...
	if (providerRunning) {
	//如果provider已经在运行了,做相应处理
	}
    if (!providerRunning) {
         try {
            //查询PMS,得到指定的ProviderInfo
            cpi = AppGlobals.getPackageManager().
                resolveContentProvider(name,
                  STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
             checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
          } catch (RemoteException ex) {
          }
	}
...
   if (firstClass) {
    	try {
			//查询PMS,得到Provider所在的Applciation信息
    	 	ApplicationInfo ai =  AppGlobals.getPackageManager().
                            getApplicationInfo(
                                    cpi.applicationInfo.packageName,
                                    STOCK_PM_FLAGS, userId);

             ai = getAppInfoForUser(ai, userId);
			//AMS内部通过ContentProviderRecord对象来保存ContentProvider
			//类似于Activity使用ActivityRecord来保存,四大组件都是类似的
             cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
             } catch (RemoteException ex) {
             }
		...
		if (proc != null && proc.thread != null && !proc.killed) {
		//如果ContentProvider已经启动, 做相应操作
		} else {
		//如果未启动,则启动ConTentProvider所在进程
			proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0, "content provider",
                                new ComponentName(cpi.applicationInfo.packageName,
                                        cpi.name), false, false, false);
		}
	...
 synchronized (cpr) {
     while (cpr.provider == null) {
       if (cpr.launchingApp == null) {
           return null;
        }
...
        try {
           cpr.wait();//等待ContentProvider进程启动完毕
        } catch (InterruptedException ex) {
        }...

以上代码已经将注释2解释清楚了,要去获取ContentProvider对象在本地的代理,如果ContentProvider已经启动,则可以得到IContentProvider对象,否则,需要启动ContentProvider所在进程以及ContentProvider本身。 AMS等待直到Provider的进程启动完毕。

ContentProvider进程的启动以及ContentProvider的启动 流程

应用进程启动后(对于应用进程启动不是很了解的可以自行查找),第一件大事就是调用AMS的attachApplication方法,ContentProvider进程自然也不例外, 然后会调用ActivityThread的bindApplication方法,最终会调用到handleBindApplication方法:

private void handleBindApplication(AppBindData data) {

    try {
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;

        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
				//注释 2.1, 这里会去启动当前进程的所有ContentProvider
                installContentProviders(app, data.providers);
                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
            }
        }

        try {//注释 2.2 这里会去调用当前进程Applciation的onCreate方法
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
         ...
        }

}

这个方法重点需要关注两点:

  • 注释2.1: 这里会去启动当前进程的所有ContentProvider,只要在manifest注册了的都会启动,然后依次回调每个Provider的onCreate方法;

  • 注释2.2: 这里会调用当前进程Application的onCreate 方法, 所以Applciation的onCreate方法调用时在ContentProvider的onCreate方法后面的;

像 为什么ContentProvider onCreate()在 Application onCreate()之前执行?为什么数据库的创建在Application onCreate()之前执行?等问题,在这处的源码里得到了解释。

而注释2.1调用的installContentProviders()方法,最终也是调用了installProvider()方法, 所以我们接着解释前面的注释3

注释3 installProvider()

再插入一下图片

在这里插入图片描述

前面解释了,调用getContentResolver方法所在的进程(以下称调用进程)也会调用installProvider()方法,而ContentProvider所在进程在启动时也会调用这个方法,就是通过包名信息来做的判断。

此方法中,如果是调用进程,则会等待ContentProvider初始化完成后,通过AMS得到本地的代理对象; 如果是ContentProvider所在进程,则会通过反射去完成初始化。

以下只贴出通过反射建立实例的代码:

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
	...
	if (context.getPackageName().equals(ai.packageName)) {
	...
	} else ...
	try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                Slog.e(TAG, "Failed to instantiate class " +
                      info.name + " from sourceDir " +
                      info.applicationInfo.sourceDir);
                return null;
            }
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
           ...
        }
}

发布ContentProvider: ActivityManagerService.publishContentProvider

最后,ContentProvider及所在进程初始化完毕后,借助AMS的publishContentProvider方法把ContentProvider信息发布出去, 这个方法就不分析了, 此时, 调用进程的getContentResolver最终得到了要找寻的 ContentProvider的本地代理对象, 和对应的ContentProvider建立了联系!

额外说明:

在这里插入图片描述

这里的 android:multiprocess=“false” 其实我们在manifest中通常是不会配置的,默认也是false, 这个配置的含义是, 是否建立ContentProvider的多个实例,如果为false, 只会有一个实例, 如果true,则当不同进程来调用同一个Provider时,则会建立多个实例。

ContentProvider的创建及启动流程到这里就分析完了。

喜欢的朋友麻烦点个赞吧~~

参考资料:
《深入理解Android 卷2》
https://www.jianshu.com/p/efff9a5d820f

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值