Android source code analysis-Full analysis of ContentProvider in the Framework layer

opening

This article takes android-11.0.0_r25 as the basic analysis

Among the four major components, the one we usually use the least is ContentProviderused ContentProviderto help the application manage the access to data stored by itself and other applications, and provide a method to share data with other applications. Using it ContentProvidercan safely share and modify data between applications, such as accessing the gallery, address book, etc.

In the previous article, we mentioned ContentProviderthe start-up timing, so we might as well follow the trend and analyze this piece clearly. This article will not teach you how to use it, but only ContentProviderfocus on ContentProviderthe start-up and interaction at the system level.

basic knowledge

ContentResolver

To ContentProvideraccess application data, we usually need to use ContentResolverthe API, we can Context.getContentResolverget its instance object through the method

ContentResolverIt is an abstract class whose abstract methods ContextImpl.ApplicationContentResolverare implemented by inheritance, and what we actually get is this instance object

Uri format

ContentProviderThe use of needs to obtain the Uri of the provider first, and its format is as follows:

  1. Scheme: Fixed tocontent://
  2. Authority: the attribute AndroidManifestset for the providerandroid:authorities
  3. resource relative path
  4. Resource ID

Among them, the resource relative path and resource ID are not necessary, it depends on the quantity and form of resource storage

For example, the Uri of a picture in the external storage is: content://media/external/images/media/${id}, where mediaAuthority /external/images/mediais the relative path of the picture in the external storage, and idit is the resource of this picture stored in the databaseid

GetContentProvider

ContentProviderAs a bridge for sharing data, the most important functions are nothing more than adding, deleting, modifying, and checking. We use checking as an entry point to analyze ContentProviderhow objects are obtained.

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

    //尝试获取unstableProvider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
    
    
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
    
    
        ...
        try {
    
    
            //调用远程对象query
            qCursor = unstableProvider.query(mPackageName, mAttributionTag, 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
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
    
    
                return null;
            }
            //调用远程对象query
            qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
                    queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
    
    
            return null;
        }

        // Force query execution.  Might fail and throw a runtime exception here.
        qCursor.getCount();
        ...

        // Wrap the cursor object into CursorWrapperInner object.
        //将qCursor和provider包装成CursorWrapperInner对象返回
        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);
        }
    }
}

We can roughly divide this method into the following steps:

  1. get unstableProviderremote object
  2. Call the method unstableProviderof the object query, getqCursor
  3. If querythe remote object dies in the process, try to get stableProviderand call querythe method getqCursor
  4. Get it stableProvider(if you didn't get it before)
  5. Wrap qCursorand stableProviderreturn CursorWrapperInneran object
  6. release resources

Since ContentProviderdata can be shared before the application, it must support cross-process. Yes, it still uses the Bindercommunication we are familiar with. The object is the remote object IContentProviderused by the calling process . Looking back at this method, we found that the remote object is passed or obtained. Let’s see what these two methods do next.BinderIContentProvideracquireUnstableProvideracquireProvider

Here is a concept about unstableand . There are two kinds of reference counts stablefor the obtained through these two methods . If the remote process dies, and if it is, it will kill the caller process associated with it through the method. The specific process will be analyzed later.ContentProviderunstableCountstableCountContentProviderstableCount > 0stable

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

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

public final IContentProvider acquireUnstableProvider(String name) {
    
    
    if (name == null) {
    
    
        return null;
    }
    return acquireUnstableProvider(mContext, name);
}

public final IContentProvider acquireProvider(String name) {
    
    
    if (name == null) {
    
    
        return null;
    }
    return acquireProvider(mContext, name);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
    
    
    return mMainThread.acquireProvider(c,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), false);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireProvider(Context context, String auth) {
    
    
    return mMainThread.acquireProvider(context,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), true);
}

ActivityThread.acquireProvider

The Android system Authoritydistinguishes the difference by means of ContentProvider. After some simple judgments and processing, ActivityThread.acquireProviderthe method is finally called to obtain it ContentProvider, and the difference between acquireUnstableProviderand acquireProvideris only the last boolean input parameter.

public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    
    
    //尝试从本地缓存中获取ContentProvider对象
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
    
    
        return provider;
    }

    // There is a possible race here.  Another thread may try to acquire
    // the same provider at the same time.  When this happens, we want to ensure
    // that the first one wins.
    // Note that we cannot hold the lock while acquiring and installing the
    // provider since it might take a long time to run and it could also potentially
    // be re-entrant in the case where the provider is in the same process.
    ContentProviderHolder holder = null;
    try {
    
    
        synchronized (getGetProviderLock(auth, userId)) {
    
    
            //使用AMS获取ContentProvider对象
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
        }
    } catch (RemoteException ex) {
    
    
        throw ex.rethrowFromSystemServer();
    }
    if (holder == null) {
    
    
        ...
        return null;
    }

    // Install provider will increment the reference count for us, and break
    // any ties in the race.
    //安装ContentProvider
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

This method roughly does the following things:

  1. IContentProviderFirst try to get the object from the cache
  2. Get the object AMSusingContentProviderHolder
  3. InstallContentProvider
  4. return IContentProviderobject

ActivityThread.acquireExistingProvider

Let's first look at the method to try to get the object acquireExistingProviderfrom the cacheIContentProvider

public final IContentProvider acquireExistingProvider(
        Context c, String auth, int userId, boolean stable) {
    
    
    synchronized (mProviderMap) {
    
    
        //从缓存Map中查找
        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()) {
    
    
            // The hosting process of the provider has died; we can't
            // use this one.
            //清理ContentProvider
            handleUnstableProviderDiedLocked(jBinder, true);
            return null;
        }

        // Only increment the ref count if we have one.  If we don't then the
        // provider is not reference counted and never needs to be released.
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
    
    
            //更新引用计数
            incProviderRefLocked(prc, stable);
        }
        return provider;
    }
}

First use Authorityand userIdto find out whether the corresponding ProviderClientRecordobject already exists in the Map, then take out IContentProviderthe object, and then check whether the remote Binderobject in it has been killed, and finally everything is correct, and ContentProviderthe reference count is increased

AMS.getContentProvider

If this step is not obtained, the program will continue to AMSobtainContentProvider

public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String callingPackage, String name, int userId,
        boolean stable) {
    
    
    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.
    final int callingUid = Binder.getCallingUid();
    if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
            != AppOpsManager.MODE_ALLOWED) {
    
    
        throw new SecurityException("Given calling package " + callingPackage
                + " does not match caller's uid " + callingUid);
    }
    return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
            null, stable, userId);
}

Call the method after some checks getContentProviderImpl, this method is a bit long, let's look at it in sections

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;

    synchronized(this) {
    
    
        //获取调用方所在进程记录
        ProcessRecord r = null;
        if (caller != null) {
    
    
            r = getRecordForAppLocked(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;

        // First check if this content provider has been published...
        //检查需要的ContentProvider是否已被发布
        cpr = mProviderMap.getProviderByName(name, userId);
        // If that didn't work, check if it exists for user 0 and then
        // verify that it's a singleton provider before using it.
        //如果没找到,尝试从系统用户中查找已发布的ContentProvider
        //并确保它是可用的单例组件,条件如下:
        //是用户级应用程序且组件设置了单例flag且拥有INTERACT_ACROSS_USERS权限 或 App运行在system进程中 或 组件设置了单例flag且是同一个App
        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 == null ? callingUid : r.uid,
                                cpi.applicationInfo.uid)) {
    
    
                    userId = UserHandle.USER_SYSTEM;
                    checkCrossUser = false;
                } else {
    
    
                    cpr = null;
                    cpi = null;
                }
            }
        }

        //判断ContentProvider所在进程是否已死亡
        ProcessRecord dyingProc = null;
        if (cpr != null && cpr.proc != null) {
    
    
            providerRunning = !cpr.proc.killed;

            // Note if killedByAm is also set, this means the provider process has just been
            // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
            // yet. So we need to call appDiedLocked() here and let it clean up.
            // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
            // how to test this case.)
            if (cpr.proc.killed && cpr.proc.killedByAm) {
    
    
                Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
                // Now we are going to wait for the death before starting the new process.
                dyingProc = cpr.proc;
            }
        }
        ...
    }
    ...
}

First of all, the first part, check ContentProviderwhether the target has been published and recorded in mProviderMap, note that here mProviderMapis AMSa member variable in, a collection of a series of Maps, and ActivityThreadin mProviderMapis not a thing. If it cannot be found in the current user, and the current user is not a system user (UserHandle.USER_SYSTEM == 0), try to find a legally available singleton from the system user, and any one of the following ContentProviderconditions ContentProvidercan be regarded as a singleton ContentProvider:

  • App is a user-level application (uid >= 10000) and ContentProviderthe component sets the singleton flag ( android:singleUser) and App has INTERACT_ACROSS_USERSpermissions
  • App running in systemprocess
  • ContentProviderThe component sets the singleton flag ( android:singleUser) and is the same App

As for why cross-user access requires a singleton condition, this is related to multi-users, I am not very clear, and I will come back to supplement it if I analyze the multi-users in the future. At present, most of the application avatars and mobile phone avatars of domestic manufacturers use the multi-user technology

Then judge whether the target is running by ContentProviderRecordwhether the target exists and whether the process it is in is still aliveContentProvider

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;

    synchronized(this) {
    
    
        ...
        //ContentProvider正在运行中
        if (providerRunning) {
    
    
            cpi = cpr.info;

            //如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
            //直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
            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();

            // In this case the provider instance already exists, so we can
            // return it right away.
            //获取连接并更新引用计数
            conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                    stable);
            if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
    
    
                if (cpr.proc != null
                        && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_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.
                    //更新进程优先级
                    mProcessList.updateLruProcessLocked(cpr.proc, false, null);
                }
            }

            final int verifiedAdj = cpr.proc.verifiedAdj;
            //更新进程adj
            boolean success = updateOomAdjLocked(cpr.proc, true,
                    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.setAdj && !isProcessAliveLocked(cpr.proc)) {
    
    
                success = false;
            }
            maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
            // 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);
                //ContentProvider所在进程被杀了,更新引用计数
                boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                //仍有别的地方对这个ContentProvider有引用,直接返回null(需要等待进程清理干净才能重启)
                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
                //将运行状态标为false,使得重新启动ContentProvider所在进程
                providerRunning = false;
                conn = null;
                dyingProc = cpr.proc;
            } else {
    
    
                cpr.proc.verifiedAdj = cpr.proc.setAdj;
            }

            Binder.restoreCallingIdentity(origId);
        }
        ...
    }
    ...
}

In the second part, if the target ContentProvideris running, first check ContentProviderwhether the target can be run directly in the caller process, and one of the following conditions needs to be met:

  • The caller and the target ContentProviderare the same process in the same App
  • The caller and target ContentProviderbelong to the same App and ContentProviderthe component supports multi-process ( android:multiprocess)

In this case, just return a new one ContentProviderHolderand let the caller process process it by itself ContentProvider. The specific logic is in ActivityThread.installProviderthe method, which will be analyzed later

If this situation is not met, that is, the caller process and the target ContentProviderare not in the same process, a cross-process call is required to obtain ContentProviderConnectionthe connection and update the reference count

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;

    synchronized(this) {
    
    
        ...
        //ContentProvider未在运行
        if (!providerRunning) {
    
    
            //通过PMS获取ContentProvider信息
            try {
    
    
                cpi = AppGlobals.getPackageManager().
                    resolveContentProvider(name,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
            } 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 == null ? callingUid : r.uid,
                            cpi.applicationInfo.uid);
            if (singleton) {
    
    
                userId = UserHandle.USER_SYSTEM;
            }
            cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);

            ... //各项检查

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            //通过Class(android:name属性)获取ContentProviderRecord
            cpr = mProviderMap.getProviderByClass(comp, userId);
            //此ContentProvider是第一次运行
            boolean firstClass = cpr == null;
            if (firstClass) {
    
    
                final long ident = Binder.clearCallingIdentity();

                ... //权限处理

                try {
    
    
                    //获取应用信息
                    ApplicationInfo ai =
                        AppGlobals.getPackageManager().
                            getApplicationInfo(
                                    cpi.applicationInfo.packageName,
                                    STOCK_PM_FLAGS, userId);
                    if (ai == null) {
    
    
                        Slog.w(TAG, "No package info for content provider "
                                + cpi.name);
                        return null;
                    }
                    ai = getAppInfoForUser(ai, userId);
                    //新建ContentProvider记录
                    cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                } catch (RemoteException ex) {
    
    
                    // pm is in same process, this will never happen.
                } finally {
    
    
                    Binder.restoreCallingIdentity(ident);
                }
            } else if (dyingProc == cpr.proc && dyingProc != null) {
    
    
                // The old stable connection's client should be killed during proc cleaning up,
                // so do not re-use the old ContentProviderRecord, otherwise the new clients
                // could get killed unexpectedly.
                //旧的ContentProvider进程在死亡过程中,不要复用旧的ContentProviderRecord,避免出现预期之外的问题
                cpr = new ContentProviderRecord(cpr);
                // This is sort of "firstClass"
                firstClass = true;
            }

            //如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
            //直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
            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);
            }

            // This is single process, and our app is now connecting to it.
            // See if we are already in the process of launching this
            // provider.
            //查找正在启动中的ContentProvider
            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.
            //目标ContentProvider不在启动中
            if (i >= N) {
    
    
                final long origId = Binder.clearCallingIdentity();

                try {
    
    
                    // Content provider is now in use, its package can't be stopped.
                    //将App状态置为unstopped,设置休眠状态为false
                    try {
    
    
                        AppGlobals.getPackageManager().setPackageStoppedState(
                                cpr.appInfo.packageName, false, userId);
                    } catch (RemoteException e) {
    
    
                    } catch (IllegalArgumentException e) {
    
    
                        Slog.w(TAG, "Failed trying to unstop package "
                                + cpr.appInfo.packageName + ": " + e);
                    }

                    // Use existing process if already started
                    //获取目标ContentProvider所在进程记录
                    ProcessRecord proc = getProcessRecordLocked(
                            cpi.processName, cpr.appInfo.uid, false);
                    if (proc != null && proc.thread != null && !proc.killed) {
    
     //进程存活
                        if (!proc.pubProviders.containsKey(cpi.name)) {
    
    
                            //将ContentProviderRecord保存到进程已发布ContentProvider列表中
                            proc.pubProviders.put(cpi.name, cpr);
                            try {
    
    
                                //调度ActivityThread直接安装ContentProvider
                                proc.thread.scheduleInstallProvider(cpi);
                            } catch (RemoteException e) {
    
    
                            }
                        }
                    } else {
    
     //进程死亡
                        //启动App(App启动过程中会自动启动ContentProvider)
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0,
                                new HostingRecord("content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name)),
                                ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
                        if (proc == null) {
    
    
                            ...
                            return null;
                        }
                    }
                    cpr.launchingApp = proc;
                    //将目标ContentProvider添加到启动中列表
                    mLaunchingProviders.add(cpr);
                } finally {
    
    
                    Binder.restoreCallingIdentity(origId);
                }
            }

            // Make sure the provider is published (the same provider class
            // may be published under multiple names).
            if (firstClass) {
    
    
                mProviderMap.putProviderByClass(comp, cpr);
            }

            mProviderMap.putProviderByName(name, cpr);
            //获取连接并更新引用计数
            conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                    stable);
            if (conn != null) {
    
    
                conn.waiting = true;
            }
        }

        grantImplicitAccess(userId, null /*intent*/, callingUid,
                UserHandle.getAppId(cpi.applicationInfo.uid));
    }
    ...
}

In the third part, if the target ContentProvideris not running, first PMSobtain ContentProviderthe information, and then try to obtain it through the Class ( android:nameattribute) ContentProviderRecord. If it cannot be obtained, it means that this ContentProvideris the first time to run (after booting). In this case, a new one needs to be created ContentProviderRecord. If it is obtained, but the process it is in is marked as dying, it also needs to be created at this time . ContentProviderRecordDo not reuse the old one ContentProviderRecordto avoid unexpected problems.

Next, check ContentProviderwhether the target can run directly in the caller process. If it can directly return a new one, ContentProviderHolderlet the caller process start and getContentProvider

Then check the list that is being started ContentProvider. If it is not in the list, we may need to start it manually. At this time, there are two situations:

  1. ContentProviderThe process has been started: if the process does not include this in the published ContentProviderlist ContentProviderRecord, add it to the list, and then call ApplicationThread.scheduleInstallProviderthe method in the target process to install and startContentProvider
  2. ContentProviderThe process is not started: start the target process, and it will be automatically installed and started during the start of the target process ContentProvider( ActivityThread.handleBindApplicationin the method)

Last update mProviderMap, get ContentProviderConnectionthe connection and update the reference count

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;

    // Wait for the provider to be published...
    final long timeout =
            SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
    boolean timedOut = false;
    synchronized (cpr) {
    
    
        while (cpr.provider == null) {
    
    
            //ContentProvider启动过程中进程死亡,返回null
            if (cpr.launchingApp == null) {
    
    
                ...
                return null;
            }
            try {
    
    
                //计算最大等待时间
                final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
                if (conn != null) {
    
    
                    conn.waiting = true;
                }
                //释放锁,等待ContentProvider启动完成
                cpr.wait(wait);
                //等待时间已过,ContentProvider还是没能启动完成并发布,超时
                if (cpr.provider == null) {
    
    
                    timedOut = true;
                    break;
                }
            } catch (InterruptedException ex) {
    
    
            } finally {
    
    
                if (conn != null) {
    
    
                    conn.waiting = false;
                }
            }
        }
    }
    if (timedOut) {
    
    
        ... //超时处理
        return null;
    }

    //返回新的ContentProviderHolder
    return cpr.newHolder(conn);
}

In the fourth part, if ContentProviderit already exists, create a new one ContentProviderHolderand return it directly. If it ContentProviderdoes not exist before and is now being started, calculate CONTENT_PROVIDER_READY_TIMEOUT_MILLISa timeout period based on the current time ContentProviderRecord. After locking the target, call waitthe method to wait until the status is released after successful release (in the ContentProvidermethod , it will be analyzed later), or wait until timeout. After the state is released, judge whether the internal value has been assigned, if not, it can be concluded that the timeout is over, and return at this time , if so, return a new onenotifywaitAMS.publishContentProviderswaitContentProvidernullContentProviderHolder

ActivityThread.installProvider

Since this method includes the logic of starting the local installation ContentProviderand obtaining the remote installation at the same time, it will be analyzed together in the ContentProviderfollowing chapters启动ContentProvider

Start ContentProvider

From the previous chapters 获取ContentProvider, we have concluded that the startup is divided into two situations, and then we will analyze the startup path ContentProviderin these two situationsContentProvider

process started

In the case of the process started, if the process ContentProviderdoes not contain this in the published list ContentProviderRecord, add it to the list, and then call ApplicationThread.scheduleInstallProviderthe method install start in the target processContentProvider

ApplicationThread.scheduleInstallProviderBy Handersending a message with a whatvalue of , we search H.INSTALL_PROVIDERbased on this value, and find that we will go to the method, and in this method, the method will be called to install and startwhatActivityThread.handleInstallProviderinstallContentProvidersContentProvider

process not started

In the case that the process is not started, start the target process directly. In the previous article Android source code analysis - Activity startup process (middle) , we analyzed the startup process of the App, and there are two places that are ContentProvidercrucial for startup.

AMS.attachApplicationLocked

In this method generateApplicationProvidersLockedthe method will be called

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    
    
    ...
    //normalMode一般情况下均为true
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    ...
}

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    
    
    List<ProviderInfo> providers = null;
    try {
    
    
        //通过PMS获取App中同一个进程内的所有的ContentProvider组件信息
        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) {
    
    
    }
    int userId = app.userId;
    if (providers != null) {
    
    
        int N = providers.size();
        //有必要的情况下进行Map扩容
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
    
    
            // TODO: keep logic in sync with installEncryptionUnawareProviders
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            //对于单例ContentProvider,需要在默认用户中启动,如果不是默认用户的话则直接将其丢弃掉,不启动
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
    
    
                // This is a singleton provider, but a user besides the
                // default user is asking to initialize a process it runs
                // in...  well, no, it doesn't actually run in this process,
                // it runs in the process of the default user.  Get rid of it.
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
    
    
                //新建ContentProviderRecord并将其加入到缓存中
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            //将ContentProviderRecord保存到进程已发布ContentProvider列表中
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
    
    
                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                //将App添加至进程中运行的包列表中
                app.addPackage(cpi.applicationInfo.packageName,
                        cpi.applicationInfo.longVersionCode, mProcessStats);
            }
            notifyPackageUse(cpi.applicationInfo.packageName,
                                PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
        }
    }
    return providers;
}

This method is mainly to obtain what needs to be started ContentProvider. ContentProviderRecordIf it is the first time to start this, ContentProvideryou need to create a new one ContentProviderRecordand store it in the cache, and then save it to the process published ContentProviderlist for later use. At the same time, this method returns ProviderInfoa list that needs to be started. AMS.attachApplicationLockedThe method can judge whether there is a need to start based on this list ContentProviderand set ContentProviderthe start timeout detection

ActivityThread.handleBindApplication

private void handleBindApplication(AppBindData data) {
    
    
    ...
    try {
    
    
        // If the app is being launched for full backup or restore, bring it up in
        // a restricted environment with the base application class.
        //创建Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
    
        ...

        mInitialApplication = app;

        // don't bring up providers in restricted mode; they may depend on the
        // app's custom Application class
        //在非受限模式下启动ContentProvider
        if (!data.restrictedBackupMode) {
    
    
            if (!ArrayUtils.isEmpty(data.providers)) {
    
    
                installContentProviders(app, data.providers);
            }
        }

        ...

        //执行Application的onCreate方法
        try {
    
    
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
    
    
            ...
        }
    } finally {
    
    
        // If the app targets < O-MR1, or doesn't change the thread policy
        // during startup, clobber the policy to maintain behavior of b/36951662
        if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
    
    
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }
    ...
}

As you can see, in this method, installContentProvidersthe method installation and startup is directly calledContentProvider

Another point, why do I want to Applicationcreate and onCreateput in it? Now there are many libraries on the market, including many tutorials that tell us that we can ContentProviderinitialize the SDK through registration to obtain the overall situation. ContextFor example, the new version of the famous memory leak detection tool LeakCanary, if you want to use it, just add its dependencies directly. You don’t need to make even a little change to the code. The principle is that the startup time is after creation and before calling, and the member variables in it are most likely to be. You can also use this in the ContentProviderdevelopment Applicationprocess Application.onCreatein ContentProviderthe Contextfuture Application.

ActivityThread.installContentProviders

Well, now that these two situations have finally come to ActivityThread.installContentProvidersthe method, let's analyze ContentProviderthe actual startup and installation process next.

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

    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 {
    
    
        //发布ContentProvider
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
    
    
        throw ex.rethrowFromSystemServer();
    }
}

ContentProviderThis method is very simple, it is convenient to list all the information to be started , start the installation one by one ContentProvider, and finally publish together

ActivityThread.installProvider

Let's look at installProviderthe method first. We analyzed in the previous chapter that ContentProviderthis method will also be called when we get it. This time we will combine it and analyze it together.

Through the above code, we found that there are two places where installProviderthe method will be called, and the input parameters of the method have three forms, namely:

  • holderNot for null, infonot for null, holder.providerfor null: called in ActivityThread.acquireProviderthe method, the path is not to get the existing ContentProvider-> AMS.getContentProvider-> AMS.getContentProviderImpl-> found that the target ContentProvidercan be run directly in the caller process -> directly returns a new ContentProviderHolder(including ProviderInfo) -> ActivityThread.installProvider, in this case installProviderthe method will start the installation locallyContentProvider

  • holderFor null, infonot for null: ActivityThread.installContentProviderscalled in the method, two paths, one is automatically executed after the App process is started, and the other is AMS.getContentProviderfound in the method that the target process has started but ContentProvidernot started, and the calling ActivityThread.scheduleInstallProvidermethod is executed. In this case, installProviderthe method will start the installation locallyContentProvider

  • holderNot for null, holder.providernot for null: ActivityThread.acquireProvidercalled in the method, the path is not to get the existing ContentProvider-> AMS.getContentProvider-> AMS.getContentProviderImpl-> get the remote ContentProviderreference of the target process -> wrap it into ContentProviderHolderreturn -> ActivityThread.installProvider, in this case, installProviderthe method can directly get the remote ContentProviderreference and then process it

We divide these three situations into two cases and analyze them separately.

Start the ContentProvider locally

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    
    
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
    
     //启动本地ContentProvider
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        //首先获取Context,一般情况下就是Application
        if (context.getPackageName().equals(ai.packageName)) {
    
    
            c = context;
        } else if (mInitialApplication != null &&
                mInitialApplication.getPackageName().equals(ai.packageName)) {
    
    
            c = mInitialApplication;
        } else {
    
    
            try {
    
    
                c = context.createPackageContext(ai.packageName,
                        Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
    
    
                // Ignore
            }
        }
        if (c == null) {
    
    
            return null;
        }

        //Split Apks动态加载相关
        if (info.splitName != null) {
    
    
            try {
    
    
                c = c.createContextForSplit(info.splitName);
            } catch (NameNotFoundException e) {
    
    
                throw new RuntimeException(e);
            }
        }

        try {
    
    
            final java.lang.ClassLoader cl = c.getClassLoader();
            //获取应用信息
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
    
    
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            //通过AppComponentFactory实例化ContentProvider
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            //Transport类,继承自ContentProviderNative(Binder服务端)
            provider = localProvider.getIContentProvider();
            if (provider == null) {
    
    
                return null;
            }
            // XXX Need to create the correct context for this provider.
            //初始化ContentProvider,调用其onCreate方法
            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 {
    
     //获取外部ContentProvider
        ...
    }

    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
    
    
        //对于本地ContentProvider来说,这里的实际类型是Transport,继承自ContentProviderNative(Binder服务端)
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
    
     //本地启动ContentProvider的情况
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
    
    
                //如果已经存在相应的ContentProvider记录,使用其内部已存在的ContentProvider
                provider = pr.mProvider;
            } else {
    
    
                //否则使用新创建的ContentProvider
                holder = new ContentProviderHolder(info);
                holder.provider = provider;
                //对于本地ContentProvider来说,不存在释放引用这种情况
                holder.noReleaseNeeded = true;
                //创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                //保存ProviderClientRecord到本地缓存中
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else {
    
     //获取远程ContentProvider的情况
            ...
        }
    }
    return retHolder;
}

We found ContentProviderthe entry to create and start here, first judge and determine what is created and used by passing in Context(actually ) (in general ), then get the application information , and then get it through it (Introduced in the previous article, if the property is not set in it , the default is used ), and then instantiate the object through the methodApplicationContentProviderContextApplicationLoadedApkAppComponentFactoryAndroidManifestandroid:appComponentFactoryAppComponentFactoryAppComponentFactory.instantiateProviderContentProvider

public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
        @NonNull String className)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    
    
    return (ContentProvider) cl.loadClass(className).newInstance();
}

The default is to ClassNamecall the default constructor to instantiate ContentProviderthe object through reflection, and finally call ContentProvider.attachInfothe method to initialize

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

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    
    
    ...
    if (mContext == null) {
    
    
        mContext = context;
        ...
        ContentProvider.this.onCreate();
    }
}

We will not analyze the details, we only need to know that mContexta value is assigned here, and then ContentProvider.onCreatethe method is called.

At this point, ContentProvidereven if the startup is complete, some installation steps need to be performed next, which is actually some processing on the cache. After ContentProviderinstantiation, its getIContentProvidermethod will be called to providerassign a value to the variable. The object obtained here is actually an Transportobject, inherited from , ContentProviderNativeis a Binderserver object. After initialization ContentProvider, the method will be Transportcalled on the object to asBinderobtain Binderthe object. The object obtained here is actually itself. Then try to obtain the object from the cache. If it ProviderClientRecordis obtained, it means that the corresponding one already exists ContentProvider. Use ProviderClientRecordthe internal one ContentProvider. The newly created one can be discarded.ContentProviderHolderProviderClientRecordContentProvider

Get handle remote ContentProvider

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    
    
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
    
     //启动本地ContentProvider
        ...
    } else {
    
     //获取外部ContentProvider
        //实际类型为ContentProviderProxy
        provider = holder.provider;
    }

    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
    
    
        //对于外部ContentProvider来说,这里的实际类型是BinderProxy
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
    
     //本地启动ContentProvider的情况
            ...
        } else {
    
     //获取远程ContentProvider的情况
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
    
     //如果ContentProvider引用已存在
                // We need to transfer our new reference to the existing
                // ref count, releasing the old one...  but only if
                // release is needed (that is, it is not running in the
                // system process).
                //对于远程ContentProvider来说,如果目标App为system应用(UID为ROOT_UID或SYSTEM_UID)
                //并且目标App不为设置(包名不为com.android.settings),则noReleaseNeeded为true
                if (!noReleaseNeeded) {
    
    
                    //增加已存在的ContentProvider引用的引用计数
                    incProviderRefLocked(prc, stable);
                    try {
    
    
                        //释放传入的引用,移除ContentProviderConnection相关信息,更新引用计数
                        ActivityManager.getService().removeContentProvider(
                                holder.connection, stable);
                    } catch (RemoteException e) {
    
    
                        //do nothing content provider object is dead any way
                    }
                }
            } else {
    
    
                //创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                        provider, localProvider, holder);
                if (noReleaseNeeded) {
    
     //同上,目标App为system应用,不需要释放引用
                    //新建一个ProviderRefCount,但引用计数初始化为一个较大的数值
                    //这样后续无论调用方进程的ContentProvider引用计数如何变动都不会影响到AMS
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else {
    
     //需要释放引用的情况下
                    //正常的新建初始化一个ProviderRefCount
                    prc = stable
                            ? new ProviderRefCount(holder, client, 1, 0)
                            : new ProviderRefCount(holder, client, 0, 1);
                }
                //保存至缓存
                mProviderRefCountMap.put(jBinder, prc);
            }
            retHolder = prc.holder;
        }
    }
    return retHolder;
}

For the case of holder.providernot doing so null, directly obtain the remote ContentProviderreference and then process it. IContentProviderThe actual type obtained here is ContentProviderProxy, and then call asBinderthe method on it to obtain BinderProxythe object, and then try to obtain the object from the cache. ProviderRefCountIf there is already a corresponding reference object in the cache, !noReleaseNeededuse the original reference when the reference needs to be released ( ), and release the reference passed in by the ContentProviderparameter

Here it is assigned noReleaseNeededat the time of construction, the condition for is that the target App is a system application ( for or ) and the target App is not set (the package name is not )ContentProviderRecordtrueUIDROOT_UIDSYSTEM_UIDcom.android.settings

ProviderRefCountIf the corresponding object is not found in the cache , create a ProviderClientRecordnew ProviderRefCountobject and save them in the cache. As for why the initial value noReleaseNeededof the newly created ProviderRefCountreference count is 1000, I guess it is because it means that noReleaseNeededthere is no need to release the reference, so simply set a relatively large value here, so that no matter how the reference count of the calling process changes, the method will not be called to handle the change of the reference ContentProvider. AMSIn very early Androidversions ( Android 4.0.1), this value was set to10000

At this point, the remote ContentProviderinstallation is also over

ActivityThread.installProviderAuthoritiesLocked

Next, let's take a brief look at installProviderAuthoritiesLockedthe methods that both cases will go to.

private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
        ContentProvider localProvider, ContentProviderHolder holder) {
    
    
    final String auths[] = holder.info.authority.split(";");
    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
    ...
    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);
        }
    }
    return pcr;
}

This method is very simple. Create a new one ProviderClientRecordand add it to mProviderMapthe cache. The operation here corresponds to the first acquireExistingProvidermethod. With this cache, you can get it directly in the future without going through a series of complicated AMScross-process operations.

AMS.publishContentProviders

ContentProviderAfter all the startup and installation are completed, it is necessary to call AMS.publishContentProvidersto publish them for external use

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

    synchronized (this) {
    
    
        final ProcessRecord r = getRecordForAppLocked(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();

        final int N = providers.size();
        for (int i = 0; i < N; i++) {
    
    
            ContentProviderHolder src = providers.get(i);
            if (src == null || src.info == null || src.provider == null) {
    
    
                continue;
            }
            //App进程启动时或AMS.getContentProvider中已经将相应ContentProviderRecord添加到了pubProviders中
            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);
                String names[] = dst.info.authority.split(";");
                for (int j = 0; j < names.length; j++) {
    
    
                    mProviderMap.putProviderByName(names[j], dst);
                }

                //ContentProvider已经启动完毕,将其从正在启动的ContentProvider列表中移除
                int launchingCount = mLaunchingProviders.size();
                int j;
                boolean wasInLaunchingProviders = false;
                for (j = 0; j < launchingCount; j++) {
    
    
                    if (mLaunchingProviders.get(j) == dst) {
    
    
                        mLaunchingProviders.remove(j);
                        wasInLaunchingProviders = true;
                        j--;
                        launchingCount--;
                    }
                }
                //移除ContentProvider启动超时监听
                if (wasInLaunchingProviders) {
    
    
                    mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                // Make sure the package is associated with the process.
                // XXX We shouldn't need to do this, since we have added the package
                // when we generated the providers in generateApplicationProvidersLocked().
                // But for some reason in some cases we get here with the package no longer
                // added...  for now just patch it in to make things happy.
                r.addPackage(dst.info.applicationInfo.packageName,
                        dst.info.applicationInfo.longVersionCode, mProcessStats);
                synchronized (dst) {
    
    
                    dst.provider = src.provider;
                    dst.setProcess(r);
                    //让出锁,通知其他wait的地方
                    //对应着AMS.getContentProvider的第四部分:等待ContentProvider启动完成
                    dst.notifyAll();
                }
                dst.mRestartCount = 0;
                updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
                maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                        src.info.authority);
            }
        }

        Binder.restoreCallingIdentity(origId);
    }
}

Traverse the entire listContentProvider to be released and find the corresponding ones . We have analyzed in the previous chapters. When the App process is started or in, it has been added to it , and then it is saved in each cache. Since it has been started , it needs to be removed from the list that is starting . boot completeProcessRecord.pubProvidersContentProviderRecordAMS.getContentProviderContentProviderRecordpubProvidersContentProviderContentProviderContentProviderContentProviderContentProviderRecordContentProviderContentProviderRecordnotifyAllwaitAMS.getContentProviderContentProvider

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    
    
    ...
    //这里的cpr和在publishContentProviders获得的dst是一个对象
    synchronized (cpr) {
    
    
        while (cpr.provider == null) {
    
    
            ...
            //释放锁,等待ContentProvider启动完成
            cpr.wait(wait);
            ...
        }
    }
    ...
}

In this way, ContentProvideras soon as it is released, a notification will be received here, waitthe status will be released, obtained ContentProvider, and returned. Does it feel like everything is connected?

ContentProvider reference count

ContentProviderThe acquisition and startup analysis is over, let's talk about its reference count, and ContentProviderprepare for the next section to analyze the process of killing the caller process by death

ActivityThreadThe reference count of the layer is AMSseparate from the reference count of the layer. ActivityThreadIt records ContentProviderhow many places the target is in use in this process, and AMSrecords ContentProviderhow many processes the target is using.

ActivityThread layer reference counting

increment reference count

Let's ActivityThreadstart with increasing the reference count of the layer. When ActivityThreadacquiring ContentProvider, the method will be called incProviderRefLockedto increase the reference count. The specific timing is acquireExistingProvideror installProvider, I will not repeat the code. Just read the previous sections (the same below)

private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
    
    
    if (stable) {
    
    
        //增加ActivityThread的stable引用计数
        prc.stableCount += 1;
        //本进程对目标ContentProvider产生了stable引用关系
        if (prc.stableCount == 1) {
    
    
            // We are acquiring a new stable reference on the provider.
            int unstableDelta;
            //正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
            if (prc.removePending) {
    
    
                // We have a pending remove operation, which is holding the
                // last unstable reference.  At this point we are converting
                // that unstable reference to our new stable reference.
                //当ActivityThread释放一个stable的ContentProvider时,如果释放完后,
                //发现stable和unstable引用计数均为0,则会暂时保留一个unstable引用
                //所以这里需要为 -1 ,将这个unstable引用移除
                unstableDelta = -1;
                // Cancel the removal of the provider.
                prc.removePending = false;
                // There is a race! It fails to remove the message, which
                // will be handled in completeRemoveProvider().
                //取消移除ContentProvider引用
                mH.removeMessages(H.REMOVE_PROVIDER, prc);
            } else {
    
    
                //对于正常情况,只需要增加stable引用计数,不需要动unstable引用计数
                unstableDelta = 0;
            }
            try {
    
    
                //AMS层修改引用计数
                ActivityManager.getService().refContentProvider(
                        prc.holder.connection, 1, unstableDelta);
            } catch (RemoteException e) {
    
    
                //do nothing content provider object is dead any way
            }
        }
    } else {
    
    
        //增加ActivityThread的unstable引用计数
        prc.unstableCount += 1;
        //本进程对目标ContentProvider产生了unstable引用关系
        if (prc.unstableCount == 1) {
    
    
            // We are acquiring a new unstable reference on the provider.
            //正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
            if (prc.removePending) {
    
    
                // Oh look, we actually have a remove pending for the
                // provider, which is still holding the last unstable
                // reference.  We just need to cancel that to take new
                // ownership of the reference.
                //取消移除ContentProvider引用
                prc.removePending = false;
                mH.removeMessages(H.REMOVE_PROVIDER, prc);
            } else {
    
    
                // First unstable ref, increment our count in the
                // activity manager.
                try {
    
    
                    //增加AMS层的unstable引用计数
                    ActivityManager.getService().refContentProvider(
                            prc.holder.connection, 0, 1);
                } catch (RemoteException e) {
    
    
                    //do nothing content provider object is dead any way
                }
            }
        }
    }
}

The logic here needs to ContentProviderbe read together with the release reference to understand. Let me explain in advance

First of all, removePendingthis variable indicates that this variable ContentProvideris being removed. When the reference count is reduced, it is assigned a value after ActivityThreadchecking that the value is equal to the reference count , stableand a delay message with the value will be sent to the server. After a certain period of time, the removal operation will be triggered , the local cache will be cleaned, and then it will be reset . So when it is here, it means that this variable has not been completely removed.unstable0trueHandlerwhatREMOVE_PROVIDERContentProviderremovePendingfalseremovePendingtrueContentProviderContentProvider

In stablethe case of references, when ActivityThreadthe reference count is reduced, after checking that the reference count is equal stableto the reference count , a reference will be temporarily reserved , and the reference will be removed when the removal is actually triggered later , so this needs to be considered when increasing the reference count. In this case, the reference count of the layer should be reduced by one.unstable0unstableContentProviderunstableAMSunstable

For other cases, it is normal to increase ActivityThreadthe reference count of the layer, and then call the reference count of AMS.refContentProviderthe method operation AMSlayer

Decrease the reference count

ContentProviderThe method will be called after use ActivityThread.releaseProvider. Taking the querymethod as an example, the method releaseUnstableProviderand releaseProvidermethod will be called at the end, and they will all come here

public final boolean releaseProvider(IContentProvider provider, boolean stable) {
    
    
    if (provider == null) {
    
    
        return false;
    }

    IBinder jBinder = provider.asBinder();
    synchronized (mProviderMap) {
    
    
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc == null) {
    
    
            // The provider has no ref count, no release is needed.
            return false;
        }

        boolean lastRef = false;
        if (stable) {
    
    
            //引用计数已经为0,无法再减了
            if (prc.stableCount == 0) {
    
    
                return false;
            }
            //减少ActivityThread的stable引用计数
            prc.stableCount -= 1;
            if (prc.stableCount == 0) {
    
    
                // What we do at this point depends on whether there are
                // any unstable refs left: if there are, we just tell the
                // activity manager to decrement its stable count; if there
                // aren't, we need to enqueue this provider to be removed,
                // and convert to holding a single unstable ref while
                // doing so.
                lastRef = prc.unstableCount == 0;
                try {
    
    
                    //如果是最后的引用,则暂时保留一个unstable引用
                    ActivityManager.getService().refContentProvider(
                            prc.holder.connection, -1, lastRef ? 1 : 0);
                } catch (RemoteException e) {
    
    
                    //do nothing content provider object is dead any way
                }
            }
        } else {
    
    
            //引用计数已经为0,无法再减了
            if (prc.unstableCount == 0) {
    
    
                return false;
            }
            //减少ActivityThread的unstable引用计数
            prc.unstableCount -= 1;
            if (prc.unstableCount == 0) {
    
    
                // If this is the last reference, we need to enqueue
                // this provider to be removed instead of telling the
                // activity manager to remove it at this point.
                lastRef = prc.stableCount == 0;
                //如果是最后的引用,则不进入到这里,暂时保留一个unstable引用
                if (!lastRef) {
    
    
                    try {
    
    
                        //减少AMS引用计数
                        ActivityManager.getService().refContentProvider(
                                prc.holder.connection, 0, -1);
                    } catch (RemoteException e) {
    
    
                        //do nothing content provider object is dead any way
                    }
                }
            }
        }

        if (lastRef) {
    
    
            if (!prc.removePending) {
    
    
                // Schedule the actual remove asynchronously, since we don't know the context
                // this will be called in.
                //表面此ContentProvider正在移除中
                prc.removePending = true;
                //发送延时消息,等待一定时间后移除ContentProvider
                Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
                mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME);
            } else {
    
    
                Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
            }
        }
        return true;
    }
}

It can be seen that after decrementing the reference count, if it is found to be the last reference, that is, stableequal to unstablethe reference count , a reference will be temporarily reserved regardless 0of whether it is or not stable, and then a delay message with a value of , will be sent and removed after a certain period of time . When the time is up to trigger this message, the method will be calledunstableAMSunstablewhatREMOVE_PROVIDERContentProviderActivityThread.completeRemoveProvider

final void completeRemoveProvider(ProviderRefCount prc) {
    
    
    synchronized (mProviderMap) {
    
    
        if (!prc.removePending) {
    
    
            // There was a race!  Some other client managed to acquire
            // the provider before the removal was completed.
            // Abort the removal.  We will do it later.
            return;
        }

        // More complicated race!! Some client managed to acquire the
        // provider and release it before the removal was completed.
        // Continue the removal, and abort the next remove message.
        prc.removePending = false;

        //移除缓存
        final IBinder jBinder = prc.holder.provider.asBinder();
        ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
        if (existingPrc == prc) {
    
    
            mProviderRefCountMap.remove(jBinder);
        }

        //移除缓存
        for (int i=mProviderMap.size()-1; i>=0; i--) {
    
    
            ProviderClientRecord pr = mProviderMap.valueAt(i);
            IBinder myBinder = pr.mProvider.asBinder();
            if (myBinder == jBinder) {
    
    
                mProviderMap.removeAt(i);
            }
        }
    }

    try {
    
    
        //处理AMS层引用计数
        ActivityManager.getService().removeContentProvider(
                prc.holder.connection, false);
    } catch (RemoteException e) {
    
    
        //do nothing content provider object is dead any way
    }
}

This method clears the relevant cache held in the process ContentProvider, and then calls AMS.removeContentProviderthe method to notify AMSthe removal ContentProviderand handle the corresponding reference count. Here we find that AMS.removeContentProviderthe last parameter passed in by the calling method stableis , because we have retained a reference falsebefore stableand unstablethe reference count is equal , so the reference removed at this time is also a reference0unstableContentProviderunstable

Reference counting for the AMS layer

Next, let's look at AMSthe reference count of the layer

AMS.refContentProvider

ActivityThreadLet's start with the reference count modification of the layer we just analyzed : refContentProviderit looks like

public boolean refContentProvider(IBinder connection, int stable, int unstable) {
    
    
    ContentProviderConnection conn;
    ...
    conn = (ContentProviderConnection)connection;
    ...

    synchronized (this) {
    
    
        if (stable > 0) {
    
    
            conn.numStableIncs += stable;
        }
        stable = conn.stableCount + stable;
        if (stable < 0) {
    
    
            throw new IllegalStateException("stableCount < 0: " + stable);
        }

        if (unstable > 0) {
    
    
            conn.numUnstableIncs += unstable;
        }
        unstable = conn.unstableCount + unstable;
        if (unstable < 0) {
    
    
            throw new IllegalStateException("unstableCount < 0: " + unstable);
        }

        if ((stable+unstable) <= 0) {
    
    
            throw new IllegalStateException("ref counts can't go to zero here: stable="
                    + stable + " unstable=" + unstable);
        }
        conn.stableCount = stable;
        conn.unstableCount = unstable;
        return !conn.dead;
    }
}

This method is very simple, there should be no need for further analysis, right? It is a simple modification ContentProviderConnectionof the reference count value

AMS.incProviderCountLocked

Next, let's look at AMSthe increase of the layer reference count. AMS.incProviderCountLockedThe trigger time of this method is in AMS.getContentProviderImplthe method

ContentProviderConnection incProviderCountLocked(ProcessRecord r,
        final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
        String callingPackage, String callingTag, boolean stable) {
    
    
    if (r != null) {
    
    
        for (int i=0; i<r.conProviders.size(); i++) {
    
    
            ContentProviderConnection conn = r.conProviders.get(i);
            //如果连接已存在,在其基础上增加引用计数
            if (conn.provider == cpr) {
    
    
                if (stable) {
    
    
                    conn.stableCount++;
                    conn.numStableIncs++;
                } else {
    
    
                    conn.unstableCount++;
                    conn.numUnstableIncs++;
                }
                return conn;
            }
        }
        //新建ContentProviderConnection连接
        ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
        //建立关联
        conn.startAssociationIfNeeded();
        if (stable) {
    
    
            conn.stableCount = 1;
            conn.numStableIncs = 1;
        } else {
    
    
            conn.unstableCount = 1;
            conn.numUnstableIncs = 1;
        }
        //添加连接
        cpr.connections.add(conn);
        r.conProviders.add(conn);
        //建立关联
        startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
                cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
        return conn;
    }
    cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
    return null;
}

If the corresponding connection already exists in the calling process ContentProviderConnection, increase the reference count on its basis, otherwise create a new connection, and then initialize the reference count value

AMS.decProviderCountLocked

Then reduce the reference count. Before ActivityThreadreducing the reference to 0, ActivityThread.completeRemoveProviderthe method will be called with a delay. In this method, AMS.removeContentProviderthe method will be called

public void removeContentProvider(IBinder connection, boolean stable) {
    
    
    long ident = Binder.clearCallingIdentity();
    try {
    
    
        synchronized (this) {
    
    
            ContentProviderConnection conn = (ContentProviderConnection)connection;
            ...
            //减少引用计数
            if (decProviderCountLocked(conn, null, null, stable)) {
    
    
                //更新进程优先级
                updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
            }
        }
    } finally {
    
    
        Binder.restoreCallingIdentity(ident);
    }
}

In this method, AMS.decProviderCountLockedthe reference count will be reduced, and then the process priority will be updated

boolean decProviderCountLocked(ContentProviderConnection conn,
        ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
    
    
    if (conn != null) {
    
    
        cpr = conn.provider;
        //减少引用计数值
        if (stable) {
    
    
            conn.stableCount--;
        } else {
    
    
            conn.unstableCount--;
        }
        if (conn.stableCount == 0 && conn.unstableCount == 0) {
    
    
            //停止关联
            conn.stopAssociation();
            //移除连接
            cpr.connections.remove(conn);
            conn.client.conProviders.remove(conn);
            if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
    
    
                // The client is more important than last activity -- note the time this
                // is happening, so we keep the old provider process around a bit as last
                // activity to avoid thrashing it.
                if (cpr.proc != null) {
    
    
                    cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
                }
            }
            //停止关联
            stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
                    cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            return true;
        }
        return false;
    }
    cpr.removeExternalProcessHandleLocked(externalProcessToken);
    return false;
}

Decrease the reference count value, if it is equal stableto unstablethe reference count 0, remove the connection

ContentProvider death kills the process of the caller process

We mentioned earlier that ContentProviderthe death of the process will stablekill all the caller processes associated with it. How is this done? In the previous article, we introduced that when the process starts, AMS.attachApplicationLockedan App process death callback will be registered when it is called. We will start the analysis from the process death and trigger the death callback

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    
    
    ...
    //注册App进程死亡回调
    AppDeathRecipient adr = new AppDeathRecipient(
            app, pid, thread);
    thread.asBinder().linkToDeath(adr, 0);
    app.deathRecipient = adr;
    ...
}

After registering the death callback, if the corresponding binderprocess dies, IBinder.DeathRecipient.binderDiedthe method will be called back. Let's take a look AppDeathRecipientat the implementation of this method

private final class AppDeathRecipient implements IBinder.DeathRecipient {
    
    
    final ProcessRecord mApp;
    final int mPid;
    final IApplicationThread mAppThread;

    AppDeathRecipient(ProcessRecord app, int pid,
            IApplicationThread thread) {
    
    
        mApp = app;
        mPid = pid;
        mAppThread = thread;
    }

    @Override
    public void binderDied() {
    
    
        synchronized(ActivityManagerService.this) {
    
    
            appDiedLocked(mApp, mPid, mAppThread, true, null);
        }
    }
}

Call AMS.appDiedLockedthe method directly by hand, and then pass handleAppDiedLockedthe call into cleanUpApplicationRecordLockedthe method

final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
    
    
    ...

    boolean restart = false;

    // Remove published content providers.
    //清除已发布的ContentProvider
    for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
    
    
        ContentProviderRecord cpr = app.pubProviders.valueAt(i);
        if (cpr.proc != app) {
    
    
            // If the hosting process record isn't really us, bail out
            continue;
        }
        final boolean alwaysRemove = app.bad || !allowRestart;
        final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
        if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
    
    
            // We left the provider in the launching list, need to
            // restart it.
            restart = true;
        }

        cpr.provider = null;
        cpr.setProcess(null);
    }
    app.pubProviders.clear();

    // Take care of any launching providers waiting for this process.
    //清除正在启动中的ContentProvider
    if (cleanupAppInLaunchingProvidersLocked(app, false)) {
    
    
        mProcessList.noteProcessDiedLocked(app);
        restart = true;
    }

    // Unregister from connected content providers.
    //清除已连接的ContentProvider
    if (!app.conProviders.isEmpty()) {
    
    
        for (int i = app.conProviders.size() - 1; i >= 0; i--) {
    
    
            ContentProviderConnection conn = app.conProviders.get(i);
            conn.provider.connections.remove(conn);
            stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
                    conn.provider.appInfo.longVersionCode, conn.provider.name,
                    conn.provider.info.processName);
        }
        app.conProviders.clear();
    }
    ...
}

As you can see, this method traverses and removes the ProcessRecord.pubProviderspublished ContentProvidercalling methods one by one.removeDyingProviderLocked

private final boolean removeDyingProviderLocked(ProcessRecord proc,
        ContentProviderRecord cpr, boolean always) {
    
    
    boolean inLaunching = mLaunchingProviders.contains(cpr);
    if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
    
    
        // It's being launched but we've reached maximum attempts, force the removal
        always = true;
    }

    if (!inLaunching || always) {
    
    
        synchronized (cpr) {
    
    
            cpr.launchingApp = null;
            cpr.notifyAll();
        }
        final int userId = UserHandle.getUserId(cpr.uid);
        // Don't remove from provider map if it doesn't match
        // could be a new content provider is starting
        //移除缓存
        if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
    
    
            mProviderMap.removeProviderByClass(cpr.name, userId);
        }
        String names[] = cpr.info.authority.split(";");
        for (int j = 0; j < names.length; j++) {
    
    
            // Don't remove from provider map if it doesn't match
            // could be a new content provider is starting
            //移除缓存
            if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
    
    
                mProviderMap.removeProviderByName(names[j], userId);
            }
        }
    }

    for (int i = cpr.connections.size() - 1; i >= 0; i--) {
    
    
        ContentProviderConnection conn = cpr.connections.get(i);
        if (conn.waiting) {
    
    
            // If this connection is waiting for the provider, then we don't
            // need to mess with its process unless we are always removing
            // or for some reason the provider is not currently launching.
            if (inLaunching && !always) {
    
    
                continue;
            }
        }
        ProcessRecord capp = conn.client;
        conn.dead = true;
        if (conn.stableCount > 0) {
    
    
            if (!capp.isPersistent() && capp.thread != null
                    && capp.pid != 0
                    && capp.pid != MY_PID) {
    
    
                //当调用方与被杀死的目标ContentProvider进程间有stable连接
                //并且调用方App进程非persistent进程并且非system_server进程中的情况下
                //杀死调用方进程
                capp.kill("depends on provider "
                        + cpr.name.flattenToShortString()
                        + " in dying proc " + (proc != null ? proc.processName : "??")
                        + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
                        ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                        ApplicationExitInfo.SUBREASON_UNKNOWN,
                        true);
            }
        } else if (capp.thread != null && conn.provider.provider != null) {
    
    
            try {
    
    
                //通知调用方移除ContentProvider
                capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
            } catch (RemoteException e) {
    
    
            }
            // In the protocol here, we don't expect the client to correctly
            // clean up this connection, we'll just remove it.
            //移除连接
            cpr.connections.remove(i);
            if (conn.client.conProviders.remove(conn)) {
    
    
                stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
                        cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            }
        }
    }

    if (inLaunching && always) {
    
    
        mLaunchingProviders.remove(cpr);
        cpr.mRestartCount = 0;
        inLaunching = false;
    }
    return inLaunching;
}

It can be seen that in this method ContentProvider, all the following connections are traversed. When it is found that other processes have established stableconnections with itself ( conn.stableCount > 0), and the calling process is not persistenta process (a resident process, only Apps with a system signature set this property to take effect), nor is it running in a process, the system_servercalling ProcessRecord.killmethod directly kills the process. For the stablecalling process that has not established a connection, the calling IApplicationThread.unstableProviderDiedmethod notifies the calling process to remove the correspondingContentProvider

Therefore, ContentProviderthere is a certain risk in using it, and everyone should pay attention to avoiding it.

Summarize

At this point, the content of the entire Frameworklayer ContentProvidershould be analyzed. I hope you can gain something after reading it. The next article should analyze the Servicerelevant source code, so stay tuned~

Guess you like

Origin blog.csdn.net/qq_34231329/article/details/131530985