ContentProvider源码解析(Android12)

1,510 阅读14分钟

Android的知识体系搭建

一 概述

ContentProvider 作为 Android 的四大组件之一,是出场最少的一个了,所以今天在我们介绍 ContentProvider 之前,先来回顾一下 ContentProvider 的使用方式。

二 使用方式

ContentProvider 作为内容提供者,一般涉及到两个进程,ContentProvider 的提供者所在的进程和使用者所在的进程(当然它也可以在一个进程内使用)。

2.1 提供者

作为提供者的一方,我们需要继承 ContentProvider,并实现它的抽象方法。下面就是 Google 提供的一个简单的 Demo

2.2 FileProvider

[development/samples/ApiDemos/src/com/example/android/apis/content/FileProvider.java]

public class FileProvider extends ContentProvider
        implements PipeDataWriter<InputStream> {
    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
            String sortOrder) {

        // content providers that support open and openAssetFile should support queries for all
        // android.provider.OpenableColumns.

        int displayNameIndex = -1;
        int sizeIndex = -1;

        // If projection is null, return all columns.
        if (projection == null) {
            projection = new String[] {
                    OpenableColumns.DISPLAY_NAME,
                    OpenableColumns.SIZE};
        }

        for (int i = 0; i < projection.length; i++) {
            if (OpenableColumns.DISPLAY_NAME.equals(projection[i])) {
                displayNameIndex = i;
            }
            if (OpenableColumns.SIZE.equals(projection[i])) {
                sizeIndex = i;
            }
        }

        MatrixCursor cursor = new MatrixCursor(projection);
        Object[] result = new Object[projection.length];

        for (int i = 0; i < result.length; i++) {
            if (i == displayNameIndex) {
                result[i] = uri.getPath();
            }
            if (i == sizeIndex) {
                result[i] = null; // Size is unknown, so null, if it was known, it would go here.
            }
        }

        cursor.addRow(result);
        return cursor;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // Don't support inserts.
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Don't support deletes.
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // Don't support updates.
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // For this sample, assume all files are .apks.
        return "application/vnd.android.package-archive";
    }

    ...
}

作为内容提供者,需要实现以下五个函数

  • onCreate :返回值表示当前内容提供者所在的进程是否加载完成
  • query : 返回一个 Cursor 用来查询数据,使用完需要关闭
  • insert :增
  • delete : 删
  • update :改

2.3 AndroidManifest.xml

<provider android:name=".content.FileProvider"
		  android:authorities="com.example.android.apis.content.FileProvider"
		  android:grantUriPermissions="true"
		  android:exported="false"
		  android:enabled="@bool/atLeastHoneycombMR2" />

在 AndroidManifest 中配置对应的 provider 节点,并配置以下属性

  • android:name : 文件路径
  • android:authorities :URI ,授权方用于标识 Content Provider 提供的数据
  • android:enabled :系统是否可以实例化 Content Provider。如果可以,则设为“true”;如果不能,则设为“false”。默认值为“true”。

接下来,就是使用方的示例。

2.4 使用者

ContentResolver cr = getContentResolver();
Uri uri = Uri.parse("uri ...");
//执行查询
Cursor c = cr.query(uri, null, null, null, null);

使用者的逻辑就相对比较简单了

  • 首先是获取 ContentResolver
  • 然后通过 Uri.parse 解析对应的 uri
  • 最后通过 ContentResolver 和 Uri 进行增删改查

ContentResolver 它是一个抽象类,它实现了 ContentInterface 接口。而 ContentInterface 中,定义了所有的增删改查,即我们需要使用的相关 api

接下来,我们就从使用者开始,看看 ContentResolver 具体的调用流程和原理。

三 Context

首先还是从 Context 出发,通过 getContentResolver 获取 ContentResolver。调用流程如下

  • Context.getContentResolver
  • ContextWrapper.getContentResolver
  • ContextImpl.getContentResolver

3.1 getContentResolver

[frameworks/base/core/java/android/app/ContextImpl.java]

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

和其他的四大组件不同,ContentResolver 在 ContextImpl 中直接就有一个成员对象 mContentResolver。

3.2 ContextImpl

[frameworks/base/core/java/android/app/ContextImpl.java]

@UnsupportedAppUsage
private final ApplicationContentResolver mContentResolver;

这个成员对象的类型是 ApplicationContentResolver。

[frameworks/base/core/java/android/app/ContextImpl.java]

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

并且这个对象还是在构造函数中初始化的。

3.3 ApplicationContentResolver

[frameworks/base/core/java/android/app/ContextImpl.java]

private static final class ApplicationContentResolver extends ContentResolver {
	@UnsupportedAppUsage
	private final ActivityThread mMainThread;

	...

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

ApplicationContentResolver 是 ContextImpl 的静态内部类,它继承自 ContentResolver,在 ApplicationContentResolver 中保存了一个 ActivityThread 的对象,外部调用它时,其实都会调用到 ActivityThread。

接下来,我们看看 ApplicationContentResolver 的增删改查都是如何实现的。

四 ContentResolver

4.1 query

接下来我们通过跟踪 query 的调用,来看一下 ContentProvider 的使用者和提供者是如何交互的。

[frameworks/base/core/java/android/content/ContentResolver.java]

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

	... // 省略代码

	// 通过 uri 获取 ContentProvider 的 BInder 对象
	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(mContext.getAttributionSource(), uri, projection,
					queryArgs, remoteCancellationSignal);
		} catch (DeadObjectException e) {
			// 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(mContext.getAttributionSource(), uri, projection,
					queryArgs, remoteCancellationSignal);
		}
		if (qCursor == null) {
			return null;
		}

		// 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;
	} 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);
		}
	}
}

首先,在 query 函数中,首先会 通过 acquireUnstableProvider 拿到一个 IContentProvider 对象。

4.2 IContentProvider

public interface IContentProvider extends IInterface {

这个 IContentProvider 对象是一个抽象类,它实现了 IInterface 接口,这个接口就是用于跨进程通信的。

public interface IInterface
{
    public IBinder asBinder();
}

所以 IContentProvider 这个对象,就是用于跨进程通信的。

4.3 acquireUnstableProvider

在 query 中,首先就会通过 acquireUnstableProvider 获取到一个 IContentProvider,而这个 acquireUnstableProvider 在之前我们已经说了,它定义在 ApplicationContentResolver 中,但是实际上调用的却是 ActivityThread 中的函数。

[frameworks/base/core/java/android/app/ActivityThread.java]

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;
}

4.3 ApplicationContentResolver.acquireUnstableProvider

[frameworks/base/core/java/android/app/ContextImpl.java]

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

最后会调用 mMainThread 中的 acquireProvider 函数。

五 ActivityThread

5.1 acquireProvider

[frameworks/base/core/java/android/app/ActivityThread.java]

@UnsupportedAppUsage
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;
	}

	// 如果不存在,就准备创建一个,但是为了避免多个线程同时获取
	// 这里会有一个锁
	ContentProviderHolder holder = null;
	final ProviderKey key = getGetProviderKey(auth, userId);
	try {
		synchronized (key) {
			// 通过 AMS 获取 IContentProvider
			holder = ActivityManager.getService().getContentProvider(
					getApplicationThread(), c.getOpPackageName(), auth, userId, stable);

			// 如果 holder 不为空,但是 provider 为空,那么久要等待
			// 提供者的进程发布 provider,这里的超时时间是 10 秒(不是ANR)
			if (holder != null && holder.provider == null && !holder.mLocal) {
				synchronized (key.mLock) {
					key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
					holder = key.mHolder;
				}
				if (holder != null && holder.provider == null) {
					// probably timed out
					holder = null;
				}
			}
		}
	} catch (RemoteException ex) {
		throw ex.rethrowFromSystemServer();
	} catch (InterruptedException e) {
		holder = null;
	} finally {

		synchronized (key.mLock) {
			key.mHolder = null;
		}
	}


	// 如果 holder 也没有,那么就会安装一个 IContentProvider
	holder = installProvider(c, holder, holder.info,
			true /*noisy*/, holder.noReleaseNeeded, stable);
	return holder.provider;
}

在 acquireProvider 中,主要就做了三件事

  1. 如果有可以复用的缓存,就直接使用 -> 调用 acquireExistingProvider [5.2]
  2. 如果没有可以复用的缓存,就新建一个 -> 调用 AMS.getContentProvider [6.1]
  3. 如果是新建的,就调用执行安装逻辑 -> 调用 installProvider [6.8]

5.2 acquireExistingProvider

acquireExistingProvider 就是从 ActivityThread 的成员变量 mProviderMap 中,取出对应的 IContentProvider。

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;
		IBinder jBinder = provider.asBinder();
		if (!jBinder.isBinderAlive()) {
			// Binder 所在的进程死亡,就不使用缓存
			handleUnstableProviderDiedLocked(jBinder, true);
			return null;
		}

		//
		ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
		if (prc != null) {
			// 增加引用计数
			incProviderRefLocked(prc, stable);
		}
		return provider;
	}
}

缓存的逻辑很简单,就是从 mProviderRefCountMap 中取出。

5.3 小结

到此我们可以简单小结一下,目前有两个关键点,一个通过 AMS.getContentProvider 获取 IContentProvider,另一个是 ActivityThread 的 installProvider。

六 AMS

首先我们来看看 AMS 获取 IContentProvider 的逻辑。

6.1 getContentProvider

[frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java]

final ContentProviderHelper mCpHelper;

public final ContentProviderHolder getContentProvider(
		IApplicationThread caller, String callingPackage, String name, int userId,
		boolean stable) {

	try {
		return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
	} finally {
	}
}

在 AMS 中,存在一个 ContentProviderHelper 类型的对象 mCpHelper,通过调用 mCpHelper.getContentProvider 获得了 ContentProviderHolder。

6.2 ContentProviderHelper.getContentProvider

ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
		String name, int userId, boolean stable) {
	... // 省略代码

	return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
			null, stable, userId);
}

6.3 getContentProviderImpl 中的数据结构

getContentProviderImpl 这个函数很长,所以这里我们分几个部分进行分析。

  1. 获取缓存的 ContentProviderRecord,并获取 ContentProvider 对应所在的进程
  2. ContentProvider 所在进程存活的情况
  3. ContentProvider 所在进程死亡的情况

为了方便理解,我们先看一下 ContentProviderRecord 这个对象的定义

6.3.1 ContentProviderRecord

final class ContentProviderRecord implements ComponentName.WithComponentName {
    //
    static final int MAX_RETRY_COUNT = 3;

	// AMS
    final ActivityManagerService service;
    // 持有者的信息?
    public final ProviderInfo info;
    final int uid;
    // 进程信息
    final ApplicationInfo appInfo;
    // 包名
    final ComponentName name;
    final boolean singleton;
    // 它的 Binder 对象
    public IContentProvider provider;

    public boolean noReleaseNeeded;
    // 所有连接的客户端
    final ArrayList<ContentProviderConnection> connections
            = new ArrayList<ContentProviderConnection>();
    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
    // Handles for non-framework processes supported by this provider
    ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
    // Count for external process for which we have no handles.
    int externalProcessNoHandleCount;
    int mRestartCount; // number of times we tried before bringing up it successfully.
    ProcessRecord proc; // if non-null, hosting process.
    ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
    String stringName;
    String shortStringName;

ContentProviderRecord 是 AMS 用来记录 ContentProvider 的结构,它里面记录了有关 ContentProvider 的相关信息

  • ProviderInfo:此 ContentProvider 在 AndroidManifest.xml 中的信息
  • ComponentName:ContentProvider 的名称
  • singleton:此 ContentProvider 是否是单例
  • provider:此 ContentProvider 对应的 Binder 对象
  • connections:当前 ContentProvider 所有的连接信息
  • proc:此 proc 所在的进程信息
  • launchingApp:等待 ContentProvider 启动的进程

6.3.2 ContentProviderConnection

public final class ContentProviderConnection extends Binder {
    public final ContentProviderRecord provider;
    // 请求使用 ContentProvider 的客户端
    public final ProcessRecord client;
    // 客户端包名
    public final String clientPackage;
    ...

ContentProviderConnection 是 ContentProvider 和对应客户端的连接对象,它里面保存了请求 ContentProvider 的客户端信息,和对应的记录 ContentProviderRecord。

6.4 getContentProviderImpl

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, int callingUid, String callingPackage, String callingTag,
            boolean stable, int userId) {
	ContentProviderRecord cpr;
	ContentProviderConnection conn = null;
	ProviderInfo cpi = null;
	boolean providerRunning = false;
	final int expectedUserId = userId;
	synchronized (mService) {
		long startTime = SystemClock.uptimeMillis();

	// 先检查调用者所在进程
	ProcessRecord r = null;
	if (caller != null) {
	// 如果调用者所在的进程不存在(异常情况,直接抛出)
		r = mService.getRecordForAppLOSP(caller);
		if (r == null) {
			throw new SecurityException("Unable to find app for caller " + caller
					+ " (pid=" + Binder.getCallingPid() + ") when getting content provider "
					+ name);
		}
	}

	boolean checkCrossUser = true;

	// 检查 ContentProviderRecord 缓存是否存在(内容提供者是否已经发布)
	cpr = mProviderMap.getProviderByName(name, userId);
	// 如果没发布,并且当前用户不是系统用户
	if (cpr == null && userId != UserHandle.USER_SYSTEM) {
		// 那么就从系统用户中获取
		cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
		if (cpr != null) {
			cpi = cpr.info;
			if (mService.isSingleton(
					cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
						&& mService.isValidSingletonCall(
								r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
				userId = UserHandle.USER_SYSTEM;
				checkCrossUser = false;
			} else {
				cpr = null;
				cpi = null;
			}
		}
	}

	ProcessRecord dyingProc = null;
	if (cpr != null && cpr.proc != null) {
		// ContentProvider 所在的进程是否存活,保存到 providerRunning 中
		providerRunning = !cpr.proc.isKilled();

		// 如果 ContentProviderRecord 所在的进程已经被杀了,
		// 但是 appDiedLocked 还没用被调用,那么就保存到 dyingProc
		// 后续需要对 dyingProc 做清理工作
		if (cpr.proc.isKilled() && cpr.proc.isKilledByAm()) {
			dyingProc = cpr.proc;
		}
	}

	// 如果 ContentProviderRecord 的进程在运行
	if (providerRunning) {
		...
	}

	// 如果 ContentProvider 进程没用运行
	if (!providerRunning) {
		...
	}

	// 因为需要等待提供者发布,所以这里有一个死循环
	synchronized (cpr) {
		while (cpr.provider == null) {
			...
		}
	}
}

第一部分的逻辑比较简单,就是先判断有没有缓存的 ContentProvider。

  • 如果有,就看这个 ContentProvider 的进程是否存活,如果是则 providerRunning 为 true,否则为 false。
  • 如果没有,providerRunning 就为 false

接下来,就会根据 providerRuning 的值为 true 或者 false 执行不同的逻辑。

6.5 进程存活

// 如果 ContentProviderRecord 的进程在运行
if (providerRunning) {
	cpi = cpr.info;

	// 如果调用者进程已启动
	// 并且 ContentProviderRecord 可以在调用者进程中运行
	// 如果 multiprocess 的设置为 true,那么这里 canRunHere 就为 true
	// 那么就会【直接返回】ContentProviderHolder
	if (r != null && cpr.canRunHere(r)) {
		checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
				cpr.name.flattenToShortString(), startTime);

		ContentProviderHolder holder = cpr.newHolder(null, true);
		// 【直接返回】不要给调用者提供对象
		holder.provider = null;
		return holder;
	}

	// 免安装应用不提供 ContentProvider
	try {
		if (AppGlobals.getPackageManager()
				.resolveContentProvider(name, /*flags=*/ 0, userId) == null) {
			return null;
		}
	} catch (RemoteException e) {
	}

	checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
			cpr.name.flattenToShortString(), startTime);

	final long origId = Binder.clearCallingIdentity();
	try {
		checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");

		// Return the provider instance right away since it already exists.
		conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
				callingTag, stable, true, startTime, mService.mProcessList,
				expectedUserId);

		checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
		final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
		boolean success = mService.updateOomAdjLocked(cpr.proc,
				OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
		// XXX things have changed so updateOomAdjLocked doesn't actually tell us
		// if the process has been successfully adjusted.  So to reduce races with
		// it, we will check whether the process still exists.  Note that this doesn't
		// completely get rid of races with LMK killing the process, but should make
		// them much smaller.
		if (success && verifiedAdj != cpr.proc.mState.getSetAdj()
				&& !isProcessAliveLocked(cpr.proc)) {
			success = false;
		}
		maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
		checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
		if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
			Slog.i(TAG, "Adjust success: " + success);
		}
		// NOTE: there is still a race here where a signal could be
		// pending on the process even though we managed to update its
		// adj level.  Not sure what to do about this, but at least
		// the race is now smaller.
		if (!success) {
			// Uh oh...  it looks like the provider's process
			// has been killed on us.  We need to wait for a new
			// process to be started, and make sure its death
			// doesn't kill our process.
			Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
					+ " is crashing; detaching " + r);
			boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
					false, false);
			if (!lastRef) {
				// This wasn't the last ref our process had on
				// the provider...  we will be killed during cleaning up, bail.
				return null;
			}
			// We'll just start a new process to host the content provider
			providerRunning = false;
			conn = null;
			dyingProc = cpr.proc;
		} else {
			cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
		}
	} finally {
		Binder.restoreCallingIdentity(origId);
	}
}

如果 ContentProvider 所在的进程存活,那么执行 providerRunning 为 true 的逻辑。

  • 注意,如果调用者的进程不为空,并且 ContentProvider 可以运行在调用者所在的进程中(即 AndroidManifest.xml 中的 multiprocess 为 true)。那么就会直接通过 ContentProviderRecord 创建出一个 ContentProviderHolder 对象,并且这个 holder 对象里的 provider 为空。直接返回此 holder。
  • 这里 provider 为空是为了在调用者的进程中创建出对应的 ContentProvider,而不是提供者所在的进程。因为如果你在一个 App 中使用支付宝的付款功能,结果付款成功之后,进入了支付宝应用的界面,而不是你所在使用 App 的付款结果页,必然会导致你使用上的误解,所以这里需要在使用者进程中创建 ContentProvider,而不是提供者进程。

6.6 进程死亡

// 如果 ContentProvider 进程没用运行
if (!providerRunning) {
	... // 省略代码

			// 获取 ContentProvider 的进程,如果存在就直接使用
			ProcessRecord proc = mService.getProcessRecordLocked(
					cpi.processName, cpr.appInfo.uid);
			IApplicationThread thread;
			if (proc != null && (thread = proc.getThread()) != null
					&& !proc.isKilled()) {
				if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
					Slog.d(TAG, "Installing in existing process " + proc);
				}
				final ProcessProviderRecord pr = proc.mProviders;
				if (!pr.hasProvider(cpi.name)) {
					checkTime(startTime, "getContentProviderImpl: scheduling install");
					pr.installProvider(cpi.name, cpr);
					try {
						thread.scheduleInstallProvider(cpi);
					} catch (RemoteException e) {
					}
				}
			} else {

				// 如果不存在,就走启动进程的逻辑
				proc = mService.startProcessLocked(
						cpi.processName, cpr.appInfo, false, 0,
						new HostingRecord("content provider",
							new ComponentName(
									cpi.applicationInfo.packageName, cpi.name)),
						Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
				if (proc == null) {
// 如果启动失败,就返回空
					return null;
				}
			}
			cpr.launchingApp = proc;
			mLaunchingProviders.add(cpr);
		} finally {
			Binder.restoreCallingIdentity(origId);
		}
	}

	... // 省略代码

}

如果 ContextProvider 的进程没有运行,那么就会走启动进程的逻辑。

6.7 等待发布

final long timeout =
		SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
	// 这里是个死循环
	while (cpr.provider == null) {
		// 处理异常情况,如果启动的进程失败就直接返回
		if (cpr.launchingApp == null) {
			return null;
		}
		try {
			final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());

			if (conn != null) {
				conn.waiting = true;
			}
			// 调用 ContentProviderRecord 的 wait 进入等待
			cpr.wait(wait);
			if (cpr.provider == null) {
				timedOut = true;
				break;
			}
		} catch (InterruptedException ex) {
		} finally {
			if (conn != null) {
				conn.waiting = false;
			}
		}
	}
}
if (timedOut) {
	// 如果超时,就返回为 null
	String callerName = "unknown";
	if (caller != null) {
		synchronized (mService.mProcLock) {
			final ProcessRecord record =
					mService.mProcessList.getLRURecordForAppLOSP(caller);
			if (record != null) {
				callerName = record.processName;
			}
		}
	}
	return null;
}

// 正常返回,通过 ContentProviderConnection 创建一个 Holder 对象
return cpr.newHolder(conn, false);

最后,如果 ContentProviderRecord 里的 provider 为空,即没有发布,就会在一个死循环里等待发布。发布流程可以看 [7.3]。

我们再回到 [5.1],在经历了 AMS 的 getContentProvider 之后,会调用 installProvider 函数。

6.8 installProvider

[frameworks/base/core/java/android/app/ActivityThread.java]

@UnsupportedAppUsage
private ContentProviderHolder installProvider(Context context,
		ContentProviderHolder holder, ProviderInfo info,
		boolean noisy, boolean noReleaseNeeded, boolean stable) {

	// localProvider 是一个 ContentProvider 对象,
	// 而不是 IContentProvider 这个 Binder 对象
	// 说明使用 localProvider 时是同一个进程间通信
	ContentProvider localProvider = null;
	IContentProvider provider;

	... // 省略代码
	if (holder == null || holder.provider == null) {

		// 如果 holder 为空或者 holder.provider 为空,
		// 就走同进程通信
		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) {
				return null;
			}
			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;
		}
	} else {
		// 否则,就从 ContentProviderHolder 中取出 IContentProvider
		provider = holder.provider;
	}

	ContentProviderHolder retHolder;


	synchronized (mProviderMap) {
		IBinder jBinder = provider.asBinder();
		// localProvider 是否为空,即当前的 ContextProvider 和调用者是否是同一个进程
		if (localProvider != null) {
			// 同进程通信,直接给参数赋值
			ComponentName cname = new ComponentName(info.packageName, info.name);
			ProviderClientRecord pr = mLocalProvidersByName.get(cname);
			if (pr != null) {
				provider = pr.mProvider;
			} else {
				holder = new ContentProviderHolder(info);
				holder.provider = provider;
				holder.noReleaseNeeded = true;
				pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
				mLocalProviders.put(jBinder, pr);
				mLocalProvidersByName.put(cname, pr);
			}
			retHolder = pr.mHolder;
		} else {
			// 跨进程通信,增加引用计数,
			// 并且将 Binder 对象缓存到 mProviderRefCountMap 中
			ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
			if (prc != null) {
				if (!noReleaseNeeded) {
					incProviderRefLocked(prc, stable);
					try {
						ActivityManager.getService().removeContentProvider(
								holder.connection, stable);
					} catch (RemoteException e) {
						//do nothing content provider object is dead any way
					}
				}
			} else {
				ProviderClientRecord client = installProviderAuthoritiesLocked(
						provider, localProvider, holder);
				if (noReleaseNeeded) {
					prc = new ProviderRefCount(holder, client, 1000, 1000);
				} else {
					prc = stable
							? new ProviderRefCount(holder, client, 1, 0)
							: new ProviderRefCount(holder, client, 0, 1);
				}
				mProviderRefCountMap.put(jBinder, prc);
			}
			retHolder = prc.holder;
		}
	}
	return retHolder;
}

installProvider 函数,看它名字久知道它的目的是安装 ContextProvider。在这个函数中,会根据调用者来源的不同来区分处理。

  • 如果调用者和 ContextProvider 的提供者是同一个进程,那么就直接返回。
  • 如果调用者和 ContextProvider 的提供者是不同进程,那么就需要增加计数器

七 ActivityThread

说完了 ContextProvider 的获取,再来说说 ContextProvider 的发布。之前在 [6.7] 我们看到了 ContextProvider 发布了就可以直接获取,而 ContextProvider 没有发布的时候,会有一个死循环。

7.1 handleBindApplication

接下来,我们就来看看 ContextProvider 是如何发布的。首先是进程的启动流程,在应用启动完毕后,会调用 handleBindApplication 创建 Application 并绑定上下午。

private void handleBindApplication(AppBindData data) {

	if (!data.restrictedBackupMode) {
		// 如果 App 的 providers 不为空,就先安装 providers
		if (!ArrayUtils.isEmpty(data.providers)) {
			installContentProviders(app, data.providers);
		}
	}

	// 调用 Application 的 OnCreate 函数
	try {
		mInstrumentation.callApplicationOnCreate(app);
	} catch (Exception e) {
		if (!mInstrumentation.onException(app, e)) {
			throw new RuntimeException(
			  "Unable to create application " + app.getClass().getName()
			  + ": " + e.toString(), e);
		}
	}
}

在 handleBindApplication 中,我们居然发现 ContentProvider 的发布居然是在调用 Application 的 OnCreate 函数之前。所以之前有一些应用或者三方库会在 ContentProvider 进行初始化。

当然,现在建议不要这样做,因为 ContentProvider 从发布和创建也是需要消耗性能的,谷歌现在在 Jetpack 中已经提供了用于初始化的 App Startup。不要再用 ContentProvider 初始化了。

7.2 installContentProviders

@UnsupportedAppUsage
private void installContentProviders(
		Context context, List<ProviderInfo> providers) {
	final ArrayList<ContentProviderHolder> results = new ArrayList<>();

	for (ProviderInfo cpi : providers) {

		// 线调用 installProvider 安装 ContentProvider
		ContentProviderHolder cph = installProvider(context, null, cpi,
				false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
		if (cph != null) {
			cph.noReleaseNeeded = true;
			results.add(cph);
		}
	}

	try {
		// 然后再调用 AMS 的 publishContentProviders
		ActivityManager.getService().publishContentProviders(
			getApplicationThread(), results);
	} catch (RemoteException ex) {
		throw ex.rethrowFromSystemServer();
	}
}

在 ActivityThread 的 installContentProviders 中,会先调用 installProvider 安装 ContentProvider。然后再调用 AMS 的 来发布 publishContentProviders。

安装的逻辑在 [6.8] 中已经说了,我们接下来看看发布的逻辑。

7.3 ContentProviderHelper.publishContentProviders

AMS 的发布逻辑,其实也是调用它的 mCpHelper 对象的 publishContentProviders 函数,和之前的 [6.1] 一样。

void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
	if (providers == null) {
		return;
	}

	synchronized (mService) {
		final ProcessRecord r = mService.getRecordForAppLOSP(caller);

		if (r == null) {
			throw new SecurityException("Unable to find app for caller " + caller
					+ " (pid=" + Binder.getCallingPid()
					+ ") when publishing content providers");
		}

		final long origId = Binder.clearCallingIdentity();
		boolean providersPublished = false;
		for (int i = 0, size = providers.size(); i < size; i++) {
			ContentProviderHolder src = providers.get(i);
			if (src == null || src.info == null || src.provider == null) {
				continue;
			}
			ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
			if (dst == null) {
				continue;
			}

			providersPublished = true;

			ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);

			//
			mProviderMap.putProviderByClass(comp, dst);
			String[] names = dst.info.authority.split(";");
			for (int j = 0; j < names.length; j++) {
				mProviderMap.putProviderByName(names[j], dst);
			}

			boolean wasInLaunchingProviders = false;
			for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
				if (mLaunchingProviders.get(j) == dst) {
					mLaunchingProviders.remove(j);
					wasInLaunchingProviders = true;
					j--;
					numLaunching--;
				}
			}

			// 移除超时消息
			if (wasInLaunchingProviders) {
				mService.mHandler.removeMessages(
						ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
				mService.mHandler.removeMessages(
						ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
			}

			r.addPackage(dst.info.applicationInfo.packageName,
					dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
			synchronized (dst) {
				// 注意,这里给 ContentProviderRecord 的 provider 赋值
				// 还记得 [6.7] 里的死循环吗
				dst.provider = src.provider;
				dst.setProcess(r);
				// dst 是一个 ContentProviderRecord 对象,这里调用 notifyAll 通知之前 cpr.wait 的对象。
				dst.notifyAll();
				dst.onProviderPublishStatusLocked(true);
			}
			dst.mRestartCount = 0;
		}

		// update the app's oom adj value and each provider's usage stats
		if (providersPublished) {
			mService.updateOomAdjLocked(r, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
			for (int i = 0, size = providers.size(); i < size; i++) {
				ContentProviderHolder src = providers.get(i);
				if (src == null || src.info == null || src.provider == null) {
					continue;
				}
				maybeUpdateProviderUsageStatsLocked(r,
						src.info.packageName, src.info.authority);
			}
		}

		Binder.restoreCallingIdentity(origId);
	}
}

总结

最后还是来做一个简单的总结,先用一幅图来展示整个流程

ContentProvider~1.png

  • ContentProvider 的使用首先需要获取 ContentResolver,它实现了 ContentInterface 接口,这个接口里定义所有我们需要使用到的增删改查的 api。
  • 在 ContentResolver 中,实际上调用的是 IContentProvider 对象,它继承自 IInterface,用于跨进程通信。
  • getContentResolver.query 实际真正调用的是支持 binder 通信的代理对象 IContentProvider
  • 在获取 IContentProvider 的过程中,会触发提供者进程的启动(如果需要的话)
  • ContentProvider 启动支持在当前进程启动,也支持新建一个进程启动
  • ContentProvider 启动过程的 OnCreate 方法调用会先于 Application 方法的 OnCreate 方法调用