zoukankan      html  css  js  c++  java
  • 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原理

  • 相关阅读:
    RocketMQ性能压测分析(转载)
    利用Fiddler或Charles进行mock数据
    Linux中buffer/cache,swap,虚拟内存和page ++
    AVA 8 :从永久区(PermGen)到元空间(Metaspace)
    jstat 监控调整GC很好用
    Jmeter常用函数
    关于Oracle新建表空间,添加用户及新建表数据
    关于Oracle增加表空间大小方法
    关于手动删除Oracle数据数据,导致Oracle无法连接处理过程
    解决jquery easyui combotree(下拉树)点击文字无法展开下级菜单的解决方法
  • 原文地址:https://www.cnblogs.com/Robin132929/p/13803239.html
Copyright © 2011-2022 走看看