ContentProvider的启动过程源代码分析

ContentProvider的启动过程源代码分析

因为我们是通过ContentResolver来跟ContentProvider进行交互的,所以ContentProvider的启动的开始便从getContentResolver()开始分析。
1、获取ContentResolver并向ContentProvider插入一条数据

//获取ContentResolver并向ContentProvider插入一条数据
getContentResolver().insert(uri, values);

2、getContentResolver()具体实现是在ContextImpl中

//ContextImpl.java
class ContextImpl extends Context {
	......
	private ApplicationContentResolver mContentResolver;
	......
  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) {

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

可以看出通过getContentResolver获得的是一个ApplicationContentResolver类型的实例。ApplicationContentResolver是ContextImpl的一个内部类,它的父类是ContentResolver。

private static final class ApplicationContentResolver extends ContentResolver {
    private final ActivityThread mMainThread;
    private final UserHandle mUser;

    public ApplicationContentResolver(
            Context context, ActivityThread mainThread, UserHandle user) {
        super(context);
        mMainThread = Preconditions.checkNotNull(mainThread);
        mUser = Preconditions.checkNotNull(user);
    }
}

ContentResolver是Android提供的一个抽象类。所以getContentResolver获取的是一个ContentResolver。

/**
 * This class provides applications access to the content model.
 *
 * <div class=“special reference”>
 * <h3>Developer Guides</h3>
 * <p>For more information about using a ContentResolver with content providers, read the
 * <a href=“{@docRoot}guide/topics/providers/content-providers.html”>Content Providers</a>
 * developer guide.</p>
 */
public abstract class ContentResolver {
}

3、我们获取到ContentResolver之后便调用insert方法向ContentProvider插入一条数据。所以我们来看下insert方法,它是在ContentResolver中定义的

public abstract class ContentResolver {

public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
            @Nullable ContentValues values) {
    Preconditions.checkNotNull(url, “url”);
    IContentProvider provider = acquireProvider(url);//1
    if (provider == null) {
        throw new IllegalArgumentException("Unknown URL " + url);
    }
    try {
        long startTime = SystemClock.uptimeMillis();
        Uri createdRow = provider.insert(mPackageName, url, values);//2
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
        return createdRow;
    } catch (RemoteException e) {
        // Arbitrary and not worth documenting, as Activity
        // Manager will kill this process shortly anyway.
        return null;
    } finally {
        releaseProvider(provider);
    }
}
}

我们可以看到它在注释1处首先调用了acquireProvider来获取一个IContentProvider实例。然后在注释2处调用获取到的IContentProvider实例的insert方法进行插入数据操作。
IContentProvider是Android提供的用来跟ContentProvider进行进程间通信的Binder接口。

public interface IContentProvider extends IInterface {
    public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
            @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
            throws RemoteException;
    public String getType(Uri url) throws RemoteException;
    public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
            throws RemoteException;
    public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
            throws RemoteException;
    public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
            throws RemoteException;
    public int update(String callingPkg, Uri url, ContentValues values, String selection,
            String[] selectionArgs) throws RemoteException;
}

继续看acquireProvider方法,在注释1处它验证了传入的uri格式是否正确,即Scheme是否为content,之后在注释2处调用同名的抽象函数,因为我们通过getContentResolver()拿到的是一个ApplicationContentResolver实例所以这个抽象的acquireProvider具体实现是在ApplicationContentResolver类中。

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

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

可以看出它什么也没做只是调用了ActivityThread的acquireProvider方法。

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

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

4、ActivityThread的acquireProvider首先调用了acquireExistingProvider来查找是否已经存在要找的ContentProvider,这里我们是第一次调用所以返回为null,接着调用注释2处的AMS的getContentProvider获取一个ContentProviderHolder对象,之后还会调用installProvider函数来把这个ContentProviderHolder保存在本地,以便下次要使用这个ContentProvider接口时,直接就可以通过getExistingProvider函数获取。

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

5、ActivityManagerService.getContentProvider,它进一步调用getContentProviderImpl。

@Override
public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String name, int userId, boolean stable) {
    enforceNotIsolatedCaller("getContentProvider");
    if (caller == null) {
        String msg = “null IApplicationThread when getting content provider “
                + name;
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
    }
    // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
    // with cross-user grant.
    return getContentProviderImpl(caller, name, null, stable, userId);
}

6、ActivityManagerService.getContentProviderImpl,该函数很长我们分段来看。

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;

    synchronized(this) {
        long startTime = SystemClock.uptimeMillis();

        ProcessRecord r = null;
        if (caller != null) {
            r = getRecordForAppLocked(caller);//1、获取调用进程的进程记录块
            if (r == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                      + " (pid=" + Binder.getCallingPid()
                      + ") when getting content provider " + name);
            }
        }

        boolean checkCrossUser = true;

        checkTime(startTime, "getContentProviderImpl: getProviderByName");

        // First check if this content provider has been published…
        cpr = mProviderMap.getProviderByName(name, userId);//2、检查要获取的provider是否已存在
        // If that didn’t work, check if it exists for user 0 and then
        // verify that it's a singleton provider before using it.
        if (cpr == null && userId != UserHandle.USER_SYSTEM) {
            cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
            if (cpr != null) {
                cpi = cpr.info;
                if (isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags)
                        && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
                    userId = UserHandle.USER_SYSTEM;
                    checkCrossUser = false;
                } else {
                    cpr = null;
                    cpi = null;
                }
            }
        }

        boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已经在运行
        if (providerRunning) {//4、provider已经在运行了
            cpi = cpr.info;
            String msg;
            checkTime(startTime, “getContentProviderImpl: before checkContentProviderPermission");
            if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                    != null) {
                throw new SecurityException(msg);
            }
            checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");

            if (r != null && cpr.canRunHere(r)) {
                // This provider has been published or is in the process
                // of being published...  but it is also allowed to run
                // in the caller's process, so don't make a connection
                // and just let the caller instantiate its own instance.
                ContentProviderHolder holder = cpr.newHolder(null);
                // don't give caller the provider object, it needs
                // to make its own.
                holder.provider = null;
                return holder;
            }
            // Don’t expose providers between normal apps and instant apps
            try {
                if (AppGlobals.getPackageManager()
                        .resolveContentProvider(name, 0 /*flags*/, userId) == null) {
                    return null;
                }
            } catch (RemoteException e) {
            }

            final long origId = Binder.clearCallingIdentity();

            checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");

            // In this case the provider instance already exists, so we can
            // return it right away.
            conn = incProviderCountLocked(r, cpr, token, stable);
            if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                    // If this is a perceptible app accessing the provider,
                    // make sure to count it as being accessed and thus
                    // back up on the LRU list.  This is good because
                    // content providers are often expensive to start.
                    checkTime(startTime, “getContentProviderImpl: before updateLruProcess");
                    updateLruProcessLocked(cpr.proc, false, null);
                    checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                }
            }

            checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
            final int verifiedAdj = cpr.proc.verifiedAdj;
            boolean success = updateOomAdjLocked(cpr.proc, true);
            // 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.setAdj && !isProcessAliveLocked(cpr.proc)) {
                success = false;
            }
            maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
            checkTime(startTime, “getContentProviderImpl: after updateOomAdj");
            if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "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.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
                        + " is crashing; detaching " + r);
                boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                checkTime(startTime, “getContentProviderImpl: before appDied”);
                appDiedLocked(cpr.proc);
                checkTime(startTime, "getContentProviderImpl: after appDied");
                if (!lastRef) {
                    // This wasn't the last ref our process had on
                    // the provider...  we have now been killed, bail.
                    return null;
                }
                providerRunning = false;
                conn = null;
            } else {
                cpr.proc.verifiedAdj = cpr.proc.setAdj;
            }

            Binder.restoreCallingIdentity(origId);
        }

        if (!providerRunning) {//5、provider还未运行
            try {
                checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
                cpi = AppGlobals.getPackageManager().
                    resolveContentProvider(name,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
            } catch (RemoteException ex) {
            }
            if (cpi == null) {
                return null;
            }
            // If the provider is a singleton AND
            // (it's a call within the same user || the provider is a
            // privileged app)
            // Then allow connecting to the singleton provider
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags)
                    && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
            if (singleton) {
                userId = UserHandle.USER_SYSTEM;
            }
            cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
            checkTime(startTime, "getContentProviderImpl: got app info for user");

            String msg;
            checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
            if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                    != null) {
                throw new SecurityException(msg);
            }
            checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");

            if (!mProcessesReady
                    && !cpi.processName.equals("system")) {
                // If this content provider does not run in the system
                // process, and the system is not yet ready to run other
                // processes, then fail fast instead of hanging.
                throw new IllegalArgumentException(
                        "Attempt to launch content provider before system ready");
            }

            // Make sure that the user who owns this provider is running.  If not,
            // we don’t want to allow it to run.
            if (!mUserController.isUserRunningLocked(userId, 0)) {
                Slog.w(TAG, “Unable to launch app “
                        + cpi.applicationInfo.packageName + "/"
                        + cpi.applicationInfo.uid + " for provider "
                        + name + ": user " + userId + " is stopped");
                return null;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
            cpr = mProviderMap.getProviderByClass(comp, userId);//6、通过Classname 查找是否存在对应的provider
            checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
            final boolean firstClass = cpr == null;//7、没有找到对应的provider说明这是首次启动
            if (firstClass) {
                final long ident = Binder.clearCallingIdentity();

                // If permissions need a review before any of the app components can run,
                // we return no provider and launch a review activity if the calling app
                // is in the foreground.
                if (mPermissionReviewRequired) {
                    if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
                        return null;
                    }
                }

                try {
                    checkTime(startTime, “getContentProviderImpl: before getApplicationInfo”);
                    ApplicationInfo ai =
                        AppGlobals.getPackageManager().
                            getApplicationInfo(
                                    cpi.applicationInfo.packageName,
                                    STOCK_PM_FLAGS, userId);
                    checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
                    if (ai == null) {
                        Slog.w(TAG, "No package info for content provider "
                                + cpi.name);
                        return null;
                    }
                    ai = getAppInfoForUser(ai, userId);
                    cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、创建对应的ContentProviderRecord
                } catch (RemoteException ex) {
                    // pm is in same process, this will never happen.
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }

            checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");

            if (r != null && cpr.canRunHere(r)) {
                // If this is a multiprocess provider, then just return its
                // info and allow the caller to instantiate it.  Only do
                // this if the provider is the same user as the caller's
                // process, or can run as root (so can be in any process).
                return cpr.newHolder(null);
            }

            if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
                        + (r != null ? r.uid : null) + “ pruid “ + cpr.appInfo.uid + “): “
                        + cpr.info.name + “ callers=“ + Debug.getCallers(6));

            // This is single process, and our app is now connecting to it.
            // See if we are already in the process of launching this
            // provider.
            //9、系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。
            // 在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了
            final int N = mLaunchingProviders.size();
            int i;
            for (i = 0; i < N; i++) {
                if (mLaunchingProviders.get(i) == cpr) {
                    break;
                }
            }

            // If the provider is not already being launched, then get it
            // started.
            //10、该ContentProvider没有被其它应用程序加载
            if (i >= N) {
                final long origId = Binder.clearCallingIdentity();

                try {
                    // Content provider is now in use, its package can’t be stopped.
                    try {
                        checkTime(startTime, "getContentProviderImpl: before set stopped state");
                        AppGlobals.getPackageManager().setPackageStoppedState(
                                cpr.appInfo.packageName, false, userId);
                        checkTime(startTime, "getContentProviderImpl: after set stopped state");
                    } catch (RemoteException e) {
                    } catch (IllegalArgumentException e) {
                        Slog.w(TAG, “Failed trying to unstop package “
                                + cpr.appInfo.packageName + “: “ + e);
                    }

                    // Use existing process if already started
                    checkTime(startTime, “getContentProviderImpl: looking for process record");
                    ProcessRecord proc = getProcessRecordLocked(
                            cpi.processName, cpr.appInfo.uid, false);
                    if (proc != null && proc.thread != null && !proc.killed) {
                        if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                "Installing in existing process " + proc);
                        if (!proc.pubProviders.containsKey(cpi.name)) {
                            checkTime(startTime, "getContentProviderImpl: scheduling install");
                            proc.pubProviders.put(cpi.name, cpr);
                            try {
                                proc.thread.scheduleInstallProvider(cpi);
                            } catch (RemoteException e) {
                            }
                        }
                    } else {
                        checkTime(startTime, "getContentProviderImpl: before start process");
                        //11、启动一个新的进程来加载这个ContentProvider对应的类
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0, "content provider",
                                new ComponentName(cpi.applicationInfo.packageName,
                                        cpi.name), false, false, false);
                        checkTime(startTime, "getContentProviderImpl: after start process");
                        if (proc == null) {
                            Slog.w(TAG, "Unable to launch app "
                                    + cpi.applicationInfo.packageName + "/"
                                    + cpi.applicationInfo.uid + “ for provider “
                                    + name + “: process is bad”);
                            return null;
                        }
                    }
                    cpr.launchingApp = proc;
                    //12、把加载的ContentProvider加到mLaunchingProviders中
                    mLaunchingProviders.add(cpr);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }

            checkTime(startTime, "getContentProviderImpl: updating data structures");

            // Make sure the provider is published (the same provider class
            // may be published under multiple names).
            //13、把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询
            if (firstClass) {
                mProviderMap.putProviderByClass(comp, cpr);
            }

            mProviderMap.putProviderByName(name, cpr);
            conn = incProviderCountLocked(r, cpr, token, stable);
            if (conn != null) {
                conn.waiting = true;
            }
        }
        checkTime(startTime, “getContentProviderImpl: done!”);

        grantEphemeralAccessLocked(userId, null /*intent*/,
                cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));
    }

    // Wait for the provider to be published…
    //14、因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,
    // 它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。
    // 这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
    // 它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,
    // 这时候函数getContentProviderImpl就可以返回了
    
    synchronized (cpr) {
        while (cpr.provider == null) {
            if (cpr.launchingApp == null) {
                Slog.w(TAG, "Unable to launch app "
                        + cpi.applicationInfo.packageName + "/"
                        + cpi.applicationInfo.uid + " for provider "
                        + name + ": launching app became null");
                EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
                        UserHandle.getUserId(cpi.applicationInfo.uid),
                        cpi.applicationInfo.packageName,
                        cpi.applicationInfo.uid, name);
                return null;
            }
            try {
                if (DEBUG_MU) Slog.v(TAG_MU,
                        "Waiting to start provider " + cpr
                        + " launchingApp=" + cpr.launchingApp);
                if (conn != null) {
                    conn.waiting = true;
                }
                cpr.wait();
            } catch (InterruptedException ex) {
            } finally {
                if (conn != null) {
                    conn.waiting = false;
                }
            }
        }
    }
    return cpr != null ? cpr.newHolder(conn) : null;
}

首先看注释1处获取了调用进程的进程记录块,方便后面使用

ProcessRecord r = null;
if (caller != null) {
    r = getRecordForAppLocked(caller);//1、获取调用进程的进程记录块
    if (r == null) {
        throw new SecurityException(
                "Unable to find app for caller " + caller
              + " (pid=" + Binder.getCallingPid()
              + “) when getting content provider “ + name);
    }
}

在注释2处检查是否已经存在想要获取的provider

cpr = mProviderMap.getProviderByName(name, userId);//2、检查要获取的provider是否已存在

在注释3处根据上边的查找情况判断provider是否已经在运行了,因为这是首次启动所以providerRunning为false。

boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已经在运行

如果还未运行那么会进入注释5的分支,之后在注释6处通过classname 查找是否存在对应的provider,在注释2处也查找过否存在对应的provider。这里解释下因为有两个成员变量用来保存系统中的Content Provider信息的,一个是mProvidersByName,一个是mProvidersByClass,前者是以Content Provider的authoriry值为键值来保存的,后者是以Content Provider的类名为键值来保存的。一个Content Provider可以有多个authority,而只有一个类来和它对应,因此,这里要用两个Map来保存,为了方便根据不同条件来快速查找而设计的。如果仍未找到那么就说明这是首次加载,在注释8处创建对应的ContentProviderRecord。

if (!providerRunning) {//5、provider还未运行
//. . .
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, “getContentProviderImpl: before getProviderByClass”);
cpr = mProviderMap.getProviderByClass(comp, userId);//6、通过Classname 查找是否存在对应的provider
checkTime(startTime, “getContentProviderImpl: after getProviderByClass”);
final boolean firstClass = cpr == null;//7、没有找到对应的provider说明这是首次启动
if (firstClass) {
    final long ident = Binder.clearCallingIdentity();
//. . .
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、创建对应的ContentProviderRecord
}

注释9系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了。

// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//9、系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。
// 在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
    if (mLaunchingProviders.get(i) == cpr) {
        break;
    }
}

下面就是如果ContentProvider没有被其它应用程序加载那么会启动一个新的进程去加载,完成后把其加到mLaunchingProviders中

//10、该ContentProvider没有被其它应用程序加载
if (I >= N) {
//. . .
//11、启动一个新的进程来加载这个ContentProvider对应的类
proc = startProcessLocked(cpi.processName,
        cpr.appInfo, false, 0, “content provider”,
        new ComponentName(cpi.applicationInfo.packageName,
                cpi.name), false, false, false);
//. . .
//12、把加载的ContentProvider加到mLaunchingProviders中
mLaunchingProviders.add(cpr);
}

接着把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询

//13、把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询
if (firstClass) {
    mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);

因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,这时候函数getContentProviderImpl就可以返回了

// Wait for the provider to be published…
//14、因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,
// 它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。
// 这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
// 它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,
// 这时候函数getContentProviderImpl就可以返回了

synchronized (cpr) {
    while (cpr.provider == null) {
try {
    if (DEBUG_MU) Slog.v(TAG_MU,
            “Waiting to start provider “ + cpr
            + “ launchingApp=“ + cpr.launchingApp);
    if (conn != null) {
        conn.waiting = true;
    }
    cpr.wait();
} catch (InterruptedException ex) {
}
}
}

以上就是整个getContentProviderImpl的操作。
7、上面我们知道要获取的ContentProvider还未加载,需要启动一个新的进程去加载。新进程的启动时通过startProcessLocked来完成的,具体的启动过程这里就不具体展开了。当新进程启动完毕之后会依次调用这几个函数ActivityThread.main——> ActivityThread.attach——>ActivityManagerService.attachApplication。我们从attachApplication开始分析。

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {
//此处我们只看跟ContentProvider有关的内容

boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;//1、调用generateApplicationProvidersLocked获得需要在这个过程中加载的Content Provider列表

//. .
//2、调用bindApplication执行一些应用程序初始化工作
thread.bindApplication(processName, appInfo, providers,
        app.instr.mClass,
        profilerInfo, app.instr.mArguments,
        app.instr.mWatcher,
        app.instr.mUiAutomationConnection, testMode,
        mBinderTransactionTrackingEnabled, enableTrackAllocation,
        isRestrictedBackupMode || !normalMode, app.persistent,
        new Configuration(getGlobalConfiguration()), app.compat,
        getCommonServicesLocked(app.isolated),
        mCoreSettingsObserver.getCoreSettingsLocked(),
        buildSerial);
}

8、我们先看generateApplicationProvidersLocked

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    List<ProviderInfo> providers = null;
    try {
        providers = AppGlobals.getPackageManager()
                .queryContentProviders(app.processName, app.uid,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
                                | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
                .getList();
    } catch (RemoteException ex) {
    }
}

它是通过AppGlobals.getPackageManager().queryContentProviders().getList()来获取要加载的ContentProvider。
9、然后我们接着看bindApplication,它的第三个参数就是我们在generateApplicationProvidersLocked中获取的要加载的ContentProvider list。

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> 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,
        String buildSerial) {

    if (services != null) {
        // Setup the service cache in the ServiceManager
        ServiceManager.initServiceCache(services);
    }

    setCoreSettings(coreSettings);

    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    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;
    sendMessage(H.BIND_APPLICATION, data);
}

它的操作就是把相关的信息都封装成一个AppBindData对象,然后以一个消息的形式发送到主线程的消息队列中等待处理。
10、ActivityThread中处理BIND_APPLICATION消息的是handleBindApplication函数,该函数很长 我们只关注跟ContentProvider有关的操作,它取出了AppBindData中的provider然后调用installContentProviders。

private void handleBindApplication(AppBindData data)
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);
}
}

11、installContentProviders

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();
//1、installProvider来在本地安装每一个ContentProivder的信息
    for (ProviderInfo cpi : providers) {
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
      //2、调用ActivityManagerService服务的publishContentProviders函数来通知ActivityManagerService服务,这个进程中所要加载的Content Provider,都已经准备完毕
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

我们看到在注释1处调用installProvider来在本地安装每一个Content Proivder的信息,并且为每一个Content Provider创建一个ContentProviderHolder对象来保存相关的信息。ContentProviderHolder对象是一个Binder对象,是用来把Content Provider的信息传递给ActivityManagerService服务的。然后在注释2处通过publishContentProviders函数来通知ActivityManagerService服务,这个进程中所要加载的Content Provider,都已经准备完毕。publishContentProviders函数的作用就是用来唤醒在前面步骤6中等待的线程
12、我们先来看installProvider

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
//. . .
try {
 //1、加载ContentProvider
    final java.lang.ClassLoader cl = c.getClassLoader();
    localProvider = (ContentProvider)cl.
        loadClass(info.name).newInstance();
    provider = localProvider.getIContentProvider();
    localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
}
//. . .
//2、保存ContentProvider
synchronized (mProviderMap) {
    IBinder jBinder = provider.asBinder();
    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;
//. . .
}

installProvider主要做了两件事在注释1处加载ContentProvider并调用getIContentProvider函数来获得一个Binder对象,这个Binder对象返回给installContentProviders函数之后,就会传到ActivityManagerService中去,后续其它应用程序就是通过获得这个Binder对象来和相应的Content Provider进行通信,然后又调用attachInfo

final java.lang.ClassLoader cl = c.getClassLoader();
    localProvider = (ContentProvider)cl.
        loadClass(info.name).newInstance();
    provider = localProvider.getIContentProvider();
    localProvider.attachInfo(c, info);

attachInfo最终会调用ContentProvider的onCreate函数,这样ContentProvider就完成了创建加载。

public void attachInfo(Context context, ProviderInfo info) {
    attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    mNoPerms = testing;

    /*
     * Only allow it to be set once, so after the content service gives
     * this to us clients can't change it.
     */
    if (mContext == null) {
        mContext = context;
        if (context != null) {
            mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                    Context.APP_OPS_SERVICE);
        }
        mMyUid = Process.myUid();
        if (info != null) {
            setReadPermission(info.readPermission);
            setWritePermission(info.writePermission);
            setPathPermissions(info.pathPermissions);
            mExported = info.exported;
            mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
            setAuthorities(info.authority);
        }
        ContentProvider.this.onCreate();//回调onCreate
    }
}

ContentProvider加载完成之后,会在注释2处把创建的ContentProvider保存起来。

synchronized (mProviderMap) {
    IBinder jBinder = provider.asBinder();
    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;

可以看分别保存到了mLocalProviders(以ContentProvider对应的Binder对象provider为键值来保存,表明这是一个在本地加载的ContentProvider)、 mLocalProvidersByName(以ContentProvider类名为键值保存)和mProviderMap(通过installProviderAuthoritiesLocked保存到mProviderMap,以ContentProvider的author为键值保存)
下面贴下installProviderAuthoritiesLocked方法

        ContentProvider localProvider, ContentProviderHolder holder) {
    final String auths[] = holder.info.authority.split(“;”);
    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);

    if (provider != null) {
        // If this provider is hosted by the core OS and cannot be upgraded,
        // then I guess we're okay doing blocking calls to it.
        for (String auth : auths) {
            switch (auth) {
                case ContactsContract.AUTHORITY:
                case CallLog.AUTHORITY:
                case CallLog.SHADOW_AUTHORITY:
                case BlockedNumberContract.AUTHORITY:
                case CalendarContract.AUTHORITY:
                case Downloads.Impl.AUTHORITY:
                case "telephony":
                    Binder.allowBlocking(provider.asBinder());
            }
        }
    }

    final ProviderClientRecord pcr = new ProviderClientRecord(
            auths, provider, localProvider, holder);
    for (String auth : auths) {
        final ProviderKey key = new ProviderKey(auth, userId);
        final ProviderClientRecord existing = mProviderMap.get(key);
        if (existing != null) {
            Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
                    + " already published as " + auth);
        } else {
            mProviderMap.put(key, pcr);//以ContentProvider的author为键值保存该ContentProvider,因为可能存在多个author所以这里采用for循环
        }
    }
    return pcr;
}

至此 installProvider执行完毕,回到步骤11然后执行其注释2处逻辑
13、步骤11注释2执行了publishContentProviders来通知ActivityManagerService服务,进程中所要加载的ContentProvider,都已经准备完毕。

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
for (int I = 0; I < N; I++) {
    ContentProviderHolder src = providers.get(i);
    if (src == null || src.info == null || src.provider == null) {
        continue;
    }
    ContentProviderRecord dst = r.pubProviders.get(src.info.name);
    if (dst != null) {
        ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
        mProviderMap.putProviderByClass(comp, dst);//1保存ContentProvider
        String names[] = dst.info.authority.split(";");
        for (int j = 0; j < names.length; j++) {
            mProviderMap.putProviderByName(names[j], dst);//2保存ContentProvider
        }

        int launchingCount = mLaunchingProviders.size();
        int j;
        boolean wasInLaunchingProviders = false;
        for (j = 0; j < launchingCount; j++) {
            if (mLaunchingProviders.get(j) == dst) {//3把刚刚加载的ContentProvider从mLaunchingProviders移除
                mLaunchingProviders.remove(j);
                wasInLaunchingProviders = true;
                j--;
                launchingCount--;
            }
        }
        if (wasInLaunchingProviders) {
            mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
        }
        synchronized (dst) {//4唤醒等待线程
            dst.provider = src.provider;
            dst.proc = r;
            dst.notifyAll();
        }
        updateOomAdjLocked(r, true);
        maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                src.info.authority);
    }
}
}

首先在注释1和2处把ContentProvider保存,有人可能疑惑了上一步不是保存了,这里要注意的的上一步的保存是在启动ContentProvider的进程中进行的,这里保存是在AMS进程中。之后在注释3处把刚刚加载的ContentProvider从mLaunchingProviders移除。最后在注释4处唤醒等待的线程,这里指的是步骤6中注释14处等待的线程。唤醒之后,它检查本地ContentProviderRecord变量cpr的provider域不为null,于是就返回了。最终返回到步骤4的注释3处继续执行。
14、步骤4的注释3处调用了ActivityThread的installProvider,此次调用是在AMS请求调用ContentProvider的进程中。

//Step 4
holder = installProvider(c, holder, holder.info,
        true /*noisy*/, holder.noReleaseNeeded, stable);//3

因为这次holder.provider不为空所以不需要再次加载ContentProvider

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

if (holder == null || holder.provider == null){
}else {
    provider = holder.provider;
}
}

14、之后一路返回最终返回到步骤3的注释1处,然后继续执行在注释2处利用返回的provider调用其insert完成数据插入操作。至此整个ContentProvider启动过程分析完毕。

参考链接:

理解ContentProvider原理

posted @ 2020-10-12 15:38  Robin132929  阅读(283)  评论(0编辑  收藏  举报