开篇

本篇以android-11.0.0_r25作为基础解析

在四大组件中,或许咱们平常用到最少的便是ContentProvider了,ContentProvider是用来帮助运用管理其本身和其他运用所存储数据的拜访,并提供与其他运用同享数据的办法,运用ContentProvider能够安全的在运用之间同享和修改数据,比如说拜访图库,通讯录等

在之前的文章中,咱们提到了ContentProvider的发动机遇,不妨顺水推舟,爽性把这一块剖析个明白,本篇文章并不会教咱们怎样运用ContentProvider,只将精力集中在ContentProvider在体系层面的发动与交互上

基础知识

ContentResolver

想要通过ContentProvider拜访运用数据,咱们一般需求借助ContentResolver的API,咱们能够通过Context.getContentResolver办法获取其实例方针

ContentResolver是一个抽象类,它的抽象办法由ContextImpl.ApplicationContentResolver承继完结,咱们实践上获取到的也是这个实例方针

Uri格式

ContentProvider的运用需求先取得提供者的Uri,它的格式如下:

  1. Scheme:固定为content://
  2. Authority:为提供者在AndroidManifest里设置的android:authorities特点
  3. 资源相对途径
  4. 资源ID

其中,资源相对途径和资源ID不是必须的,要看资源存储的数量及办法

举个栗子,外部存储中某张图片的Uri为:content://media/external/images/media/${id},其中media为Authority,/external/images/media为外部存储图片的相对途径,id为这张图片资源在数据库中存储的id

获取ContentProvider

ContentProvider作为同享数据的桥梁,最主要的几个功用无非是增、删、改、查,咱们就以查作为进口来剖析ContentProvider方针是怎样获取的

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

咱们能够将这个办法大致分红以下几个进程:

  1. 获取unstableProvider长途方针
  2. 调用unstableProvider方针的query办法,获取qCursor
  3. 假如query进程中长途方针逝世,测验获取stableProvider并调用query办法获取qCursor
  4. 获取stableProvider(假如之前没获取的话)
  5. qCursorstableProvider包装成CursorWrapperInner方针回来
  6. 开释资源

已然ContentProvider能够在运用之前同享数据,那它必定是支撑跨进程的,没错,用的仍是咱们熟悉的Binder通信,IContentProvider方针即是给调用方进程运用的长途Binder方针,回顾这个办法咱们发现,IContentProvider长途方针是通过acquireUnstableProvideracquireProvider获取的,咱们接下来看看这两个办法做了什么

这儿有一个关于unstablestable的概念,关于通过这两种办法获取的ContentProvider分别会有unstableCountstableCount两种引证计数,假如长途ContentProvider地点进程逝世,且其stableCount > 0的话,则会将其通过stable办法相关的调用方进程一同杀死,具体的流程咱们会在后边剖析

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

Android体系是通过Authority来区别不同的ContentProvider的,通过一些简略的判别处理后,终究调用了ActivityThread.acquireProvider办法去获取ContentProvider,而acquireUnstableProvideracquireProvider的区别仅仅终究一个布尔值入参不同算了

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

这个办法大约做了以下几件事:

  1. 首要从缓存中测验获取IContentProvider方针
  2. 运用AMS获取ContentProviderHolder方针
  3. 装置ContentProvider
  4. 回来IContentProvider方针

ActivityThread.acquireExistingProvider

咱们首要看通过acquireExistingProvider办法测验从缓存中获取IContentProvider方针

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

首要通过AuthorityuserId来从Map中查找是否已存在对应的ProviderClientRecord方针,然后从中取出IContentProvider方针,再查看其中的长途Binder方针是否已被杀死,终究悉数无误,添加ContentProvider的引证计数

AMS.getContentProvider

假如这一步没有获取到,程序会持续从AMS获取ContentProvider

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

通过一些查看后调用getContentProviderImpl办法,这个办法有点长,咱们分段来看

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;
    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;
            }
        }
        ...
    }
    ...
}

首要,榜首部分,查看方针ContentProvider是否已被发布并记载在了mProviderMap中,注意这儿的mProviderMapAMS中的一个成员变量,一系列Map的一个调集,和ActivityThread中的mProviderMap不是一个东西。假如在当时用户中找不到,且当时用户不是体系用户(UserHandle.USER_SYSTEM == 0),则测验从体系用户中查找合法可用的单例ContentProvider,符合以下任一一个条件的ContentProvider即可被视作单例ContentProvider

  • App是用户级运用程序(uid >= 10000)且ContentProvider组件设置了单例flag(android:singleUser)且App具有INTERACT_ACROSS_USERS权限
  • App运转在system进程中
  • ContentProvider组件设置了单例flag(android:singleUser)且是同一个App

至于为什么跨用户拜访需求单例这个条件,这个和多用户相关,我也不是很清楚,以后假如剖析到了多用户这块再回来弥补。目前国内厂商的运用分身、手机分身功用大部分用的便是多用户技能

接着通过方针ContentProviderRecord是否存在和其地点进程是否还存活判别方针ContentProvider是否在运转中

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

第二部分,假如方针ContentProvider正在运转中,首要查看方针ContentProvider是否能够在调用者进程中直接运转,需求满足以下任一一个条件:

  • 调用者和方针ContentProvider是同一个App中的同进程
  • 调用者和方针ContentProvider属同一个App且ContentProvider组件支撑多进程(android:multiprocess

在这种状况下,直接回来一个新的ContentProviderHolder让调用者进程自己处理取得ContentProvider即可,具体逻辑在ActivityThread.installProvider办法中,后边会剖析

假如不满足这种状况,即调用方进程和方针ContentProvider不在一个进程中,需求跨进程调用,获取ContentProviderConnection衔接并更新引证计数

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

第三部分,假如方针ContentProvider未在运转,先通过PMS获取ContentProvider信息,接着测验通过Class(android:name特点)获取ContentProviderRecord,假如获取不到,说明这个ContentProvider是榜首次运转(开机后),这种状况下需求新建ContentProviderRecord,假如获取到了,可是其地点进程被标记为正在逝世,此刻同样需求新建ContentProviderRecord,不要复用旧的ContentProviderRecord,避免呈现预期之外的问题

接下来同样查看方针ContentProvider是否能够在调用者进程中直接运转,假如能够直接回来一个新的ContentProviderHolder让调用者进程自己发动获取ContentProvider

接着查看正在发动中的ContentProvider列表,假如不在列表中,咱们或许需求手动发动它,此刻又有两种状况:

  1. ContentProvider地点进程已发动:假如进程已发布ContentProvider列表中不包括这个ContentProviderRecord,则将其添加到列表中,然后调用方针进程中的ApplicationThread.scheduleInstallProvider办法装置发动ContentProvider
  2. ContentProvider地点进程未发动:发动方针进程,方针进程发动进程中会主动装置发动ContentProviderActivityThread.handleBindApplication办法中)

终究更新mProviderMap,获取ContentProviderConnection衔接并更新引证计数

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

第四部分,假如ContentProvider已存在,直接新建一个ContentProviderHolder回来,假如ContentProvider之前不存在,现在正在发动中,则以当时时刻加上CONTENT_PROVIDER_READY_TIMEOUT_MILLIS推算出一个超时时刻,给方针ContentProviderRecord上锁后,调用wait办法等候,直到ContentProvider成功发布后notify免除wait状况(在AMS.publishContentProviders办法中,之后会剖析到),或一直等候直到超时。wait状况免除后,判别内部ContentProvider是否已被赋值,假如没有,则能够判定超时,此刻回来null,如有,则回来一个新的ContentProviderHolder

ActivityThread.installProvider

由于这个办法一同包括了发动装置本地ContentProvider和获取装置长途ContentProvider的逻辑,所以放到后边发动ContentProvider章节里一同剖析

发动ContentProvider

从前面的章节获取ContentProvider中,咱们现已归纳出ContentProvider的发动分为两种状况,接着咱们就来剖析在这两种状况下,ContentProvider的发动途径

进程已发动

在进程已发动的状况下,假如进程已发布ContentProvider列表中不包括这个ContentProviderRecord,则将其添加到列表中,然后调用方针进程中的ApplicationThread.scheduleInstallProvider办法装置发动ContentProvider

ApplicationThread.scheduleInstallProvider会通过Hander发送一条what值为H.INSTALL_PROVIDER的音讯,咱们依据这个what值查找,发现会走到ActivityThread.handleInstallProvider办法中,在这个办法内又会调用installContentProviders办法装置发动ContentProvider

进程未发动

在进程未发动的状况下,直接发动方针进程,在之前的文章 Android源码剖析 – Activity发动流程(中) 里,咱们剖析了App的发动流程,其中有两个当地对发动ContentProvider至关重要

AMS.attachApplicationLocked

在这个办法中会调用generateApplicationProvidersLocked办法

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

这个办法主要是获取需求发动的ContentProviderContentProviderRecord,假如是榜首次发动这个ContentProvider则需求新建一个ContentProviderRecord并将其存入缓存,然后将其保存到进程已发布ContentProvider列表中,以供后边运用。一同这个办法回来了需求发动的ProviderInfo列表,AMS.attachApplicationLocked办法能够依据这个列表判别是否有需求发动的ContentProvider并设置ContentProvider发动超时检测

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

能够看到,在这个办法中直接调用了installContentProviders办法装置发动ContentProvider

另外提一点,为什么我要把Application的创立和onCreate也放进来呢?现在市面上有许多库,包括许多教程告知咱们,能够通过注册ContentProvider的办法初始化SDK,获取大局Context,比如说著名的内存走漏检测工具LeakCanary的新版别,想要运用它,直接添加它的依赖就行了,不需求对代码做哪怕一点的改动,究其原理,便是由于ContentProvider的发动机遇是在Application创立后,Application.onCreate调用前,而且ContentProvider内的Context成员变量大约率便是Application,咱们以后在开发进程中也能够妙用这一点

ActivityThread.installContentProviders

好了,现在这两种状况终究都走到了ActivityThread.installContentProviders办法中,那咱们接下来就好好剖析ContentProvider实践的发动装置流程

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

这个办法很简略,便利一切待发动的ContentProvider信息列表,逐一发动装置ContentProvider,终究一同发布

ActivityThread.installProvider

咱们先看installProvider办法,咱们在上一章中剖析到,获取ContentProvider的时分也会调用这个办法,这次咱们就结合起来一同剖析

通过上文的代码,咱们发现,有两处当地会调用installProvider办法,办法的入参有三种办法,分别为:

  • holder不为nullinfo不为nullholder.providernull:在ActivityThread.acquireProvider办法中被调用,途径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 发现方针ContentProvider能够在调用者进程中直接运转 -> 直接回来一个新的ContentProviderHolder(包括ProviderInfo) -> ActivityThread.installProvider,在这种状况下installProvider办法会在本地发动装置ContentProvider

  • holdernullinfo不为null:在ActivityThread.installContentProviders办法中被调用,两条途径,一是App进程发动后主动履行,二是在AMS.getContentProvider办法中发现方针进程已发动可是ContentProvider未发动,调用ActivityThread.scheduleInstallProvider办法履行,在这种状况下installProvider办法会在本地发动装置ContentProvider

  • holder不为nullholder.provider不为null:在ActivityThread.acquireProvider办法中被调用,途径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 获取到方针进程的长途ContentProvider引证 -> 包装成ContentProviderHolder回来 -> ActivityThread.installProvider,在这种状况下installProvider办法直接能够获取到长途ContentProvider引证,然后进行处理

咱们将这三种状况分红两种case分别剖析

本地发动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
        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;
}

咱们在这儿找到了ContentProvider创立并发动的进口,首要通过传入的Context(实践上便是Application)判别并确认创立并给ContentProvider运用的的Context是什么(一般状况下也是Application),然后获取到运用信息LoadedApk,再通过它得到AppComponentFactory(前面的文章中介绍过,假如没有在AndroidManifest中设置android:appComponentFactory特点,运用的便是默许的AppComponentFactory),接着通过AppComponentFactory.instantiateProvider办法实例化ContentProvider方针

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

默许的话便是通过ClassName反射调用默许结构函数实例化ContentProvider方针,终究再调用ContentProvider.attachInfo办法初始化

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

详细内容咱们就不剖析了,只需求知道这儿给mContext赋了值,然后调用了ContentProvider.onCreate办法就能够了

到了这一步,ContentProvider就算是发动完结了,接下来需求履行一些装置进程,其实也便是对缓存等进行一些处理。在ContentProvider实例化后,会调用其getIContentProvider办法给provider变量赋值,这儿取得的方针其实是一个Transport方针,承继自ContentProviderNative,是一个Binder服务端方针,在ContentProvider初始化后,会对Transport方针调用asBinder办法取得Binder方针,这儿取得的其实仍是自己本身,接着从缓存中测验获取ProviderClientRecord方针,假如获取到了,说明现已存在了相应的ContentProvider,运用ProviderClientRecord内部的ContentProvider,刚刚新创立的那个就能够丢掉了,假如没获取到,就去新建ContentProviderHolder以及ProviderClientRecord,然后将他们添加到各种缓存中,至此,ContentProvider的装置进程也到此完毕

获取处理长途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;
}

关于holder.provider不为null的状况,直接获取长途ContentProvider引证,然后进行处理就能够了。这儿获取到的IContentProvider的实践类型是ContentProviderProxy,然后对其调用asBinder办法,获取到的是BinderProxy方针,接着从缓存中测验获取ProviderRefCount方针,假如缓存中现已有相应的引证方针了,则在需求开释引证(!noReleaseNeeded)的状况下运用原有的引证,开释参数传入进来的ContentProvider引证

这儿noReleaseNeeded是在ContentProviderRecord结构时赋值的,为true的条件是方针App为system运用(UIDROOT_UIDSYSTEM_UID)而且方针App不为设置(包名不为com.android.settings

假如缓存中没有查找到相应的ProviderRefCount方针,新建ProviderClientRecordProviderRefCount方针,并将他们保存到缓存中,至于为什么在noReleaseNeeded的状况下,新建的ProviderRefCount的引证计数初始值为1000,我猜想是由于noReleaseNeeded代表了不需求开释引证,所以这儿爽性设置一个比较大的值,这样不管调用方进程的ContentProvider引证计数怎样变动,都不会再调用到AMS的办法中去处理引证的变化,在非常前期的Android版别中(Android 4.0.1),这个值曾被设置为10000

至此,长途ContentProvider的装置也完毕了

ActivityThread.installProviderAuthoritiesLocked

接下来咱们再简略的看一下两种case都会走到的installProviderAuthoritiesLocked办法吧

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

这个办法很简略,新建了一个ProviderClientRecord并将其添加到mProviderMap缓存中,这儿的操作对应着最前面的acquireExistingProvider办法,有了这个缓存,以后就能够直接拿,而不用再复杂的通过一系列的AMS跨进程操作了

AMS.publishContentProviders

ContentProvider悉数发动装置完后,便要调用AMS.publishContentProviders将他们发布出去,供外部运用了

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

遍历整个待发布的ContentProvider列表,从ProcessRecord.pubProviders中查找相对应的ContentProviderRecord,咱们在之前的章节中现已剖析过了,App进程发动时或AMS.getContentProvider中现已将相应ContentProviderRecord添加到了pubProviders中,然后便是将其保存到各个缓存中,由于ContentProvider现已发动完毕,所以需求将其从正在发动的ContentProvider列表中移除,在ContentProvider正常发动的状况下,咱们需求将ContentProvider的发动超时监听移除,终究,获取ContentProviderRecord同步锁,将准备好的ContentProvider赋值到ContentProviderRecord中,接着调用notifyAll办法告诉其他调用过wait的当地,将锁让出,这儿对应的便是AMS.getContentProvider的第四部分:等候ContentProvider发动完结

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

这样,ContentProvider一发布,这儿就会收到告诉,免除wait状况,取得到ContentProvider,回来出去,是不是感觉悉数都串起来了?

ContentProvider引证计数

ContentProvider的获取与发动剖析完了,接下来咱们聊聊它的引证计数,为下一小节剖析ContentProvider逝世杀死调用方进程的进程做准备

ActivityThread层的引证计数是和AMS层的引证计数分开的,ActivityThread记载的是方针ContentProvider在本进程中有多少处正在运用,而AMS记载的是方针ContentProvider正在被多少个进程运用

ActivityThread层的引证计数

添加引证计数

咱们先从ActivityThread层添加引证计数开端说起,在ActivityThread获取ContentProvider时,便会调用incProviderRefLocked办法来添加引证计数,具体的机遇为acquireExistingProviderinstallProvider时,代码我就不重复放了,咱们看前面几个小节就行(后同)

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

这儿的逻辑需求配合着ContentProvider开释引证那里一同看才好理解,我先提前解释一下

首要,removePending这个变量表示此ContentProvider正在移除中,当ActivityThread削减引证计数,查看到stableunstable引证计数均为0后被赋值为true,而且会向Handler发送一条what值为REMOVE_PROVIDER的延时音讯,在必定时刻后便会触发ContentProvider移除操作,整理本地缓存,再将removePending重新置为false,所以当这儿removePendingtrue则说明此ContentProvider还没完全被移除,咱们把这个音讯撤销掉持续运用这个ContentProvider

关于stable引证的状况下,当ActivityThread削减引证计数,查看到stableunstable引证计数均为0后,会暂时保存一个unstable引证,等到后边真实触发到了移除ContentProvider的时分再将这个unstable引证移除,所以在添加引证计数的时分需求考虑到这一点,在这种状况下要将AMS层的unstable引证计数减一

关于其他的状况便是正常的添加ActivityThread层引证计数,然后调用AMS.refContentProvider办法操作AMS层的引证计数

削减引证计数

ContentProvider运用完后会调用ActivityThread.releaseProvider办法,以query办法为例,终究会调用releaseUnstableProviderreleaseProvider办法,终究都会走到这儿来

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

能够看到,在减完引证计数后,假如发现是终究一个引证,即stableunstable引证计数均为0,此刻不管是stable仍是unstable都会让AMS暂时保存一个unstable引证,然后发送一条what值为REMOVE_PROVIDER的延时音讯,等候必定时刻后移除ContentProvider,当时刻到了触发这条音讯时,会调用到ActivityThread.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
    }
}

这个办法将进程内所持有的ContentProvider相关缓存铲除,然后调用AMS.removeContentProvider办法告诉AMS移除ContentProvider,处理相应的引证计数。这儿咱们发现,调用AMS.removeContentProvider办法传入的终究一个参数stablefalse,由于咱们之前在stableunstable引证计数均为0的状况下,保存了一个unstable引证,所以这时移除的ContentProvider引证也是unstable引证

AMS层的引证计数

接着咱们来看AMS层的引证计数

AMS.refContentProvider

咱们就先从咱们刚刚剖析的ActivityThread层的引证计数修改后续:refContentProvider 看起

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

这个办法很简略,应该不需求再多做剖析了吧?便是简略的修改ContentProviderConnection的引证计数值

AMS.incProviderCountLocked

接下来咱们看AMS层引证计数的添加,AMS.incProviderCountLocked这个办法的触发机遇是在AMS.getContentProviderImpl办法中

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

假如调用方进程已存在对应ContentProviderConnection衔接,则在其基础上添加引证计数,不然新建衔接,然后初始化引证计数值

AMS.decProviderCountLocked

然后是削减引证计数,之前在ActivityThread减引证到0后,会延时调用ActivityThread.completeRemoveProvider办法,在这个办法中会调用到AMS.removeContentProvider办法

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

在这个办法中便会调用AMS.decProviderCountLocked削减引证计数,然后更新进程优先级

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

削减引证计数值,假如stableunstable引证计数均为0,则将这个衔接移除

ContentProvider逝世杀死调用方进程的进程

咱们前面提到过,ContentProvider地点进程逝世会将与其一切有stable相关的调用方进程杀死,这是怎样做到的呢?在之前的文章中,咱们介绍过进程发动时,在调用AMS.attachApplicationLocked时会注册一个App进程逝世回调,咱们就从进程逝世,触发逝世回调开端剖析

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

注册了逝世回调后,假如对应binder进程逝世,便会回调IBinder.DeathRecipient.binderDied办法,咱们来看一下AppDeathRecipient对这个办法的完结

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

直接易手调用AMS.appDiedLocked办法,然后通过handleAppDiedLocked调用到cleanUpApplicationRecordLocked办法中

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();
    }
    ...
}

能够看到,这个办法中遍历了ProcessRecord.pubProviders,逐一对发布的ContentProvider调用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;
}

能够看到,在这个办法中遍历了ContentProvider下的一切衔接,当发现有其他进程与自己建立了stable衔接(conn.stableCount > 0),且调用方进程不是persistent进程(常驻进程,只要具有体系签名的App设置这个特点才收效),也不是运转在system_server进程,调用ProcessRecord.kill办法直接杀死进程,关于没有建立stable衔接的调用方进程,调用IApplicationThread.unstableProviderDied办法告诉调用方进程移除相应的ContentProvider

所以,运用ContentProvider是有必定危险的,咱们要注意规避

总结

到这儿,整个Framework层关于ContentProvider的内容应该都剖析完了,期望咱们看完后能取得一些收获,接下来的文章应该会去剖析Service相关源码,敬请期待~