ContentProvider工作机制

来源:互联网 发布:买隐形眼镜必知常识 编辑:程序博客网 时间:2024/05/17 01:29

本章分监听机制、通知机制和数据库访问三个部分进行分析。

一、监听机制

在android中,我们可以通过ContentResolver监听数据库的变化,这一节我们来看监听是如何实现的。
我们首先会获取ContentResolver,在ContextImpl中调用getContentResolver()即可,它会返回一个内部类对象,如下:

public ContentResolver getContentResolver() {        return mContentResolver;}    public final void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,            @NonNull ContentObserver observer) {        Preconditions.checkNotNull(uri, "uri");        Preconditions.checkNotNull(observer, "observer");        registerContentObserver(                ContentProvider.getUriWithoutUserId(uri),                notifyForDescendants,                observer,                ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));    }

通过调用registerContentObserver进行注册动作,第一个参数为监听的uri,也就是我们需要监听的数据库字段;第二个参数为监听是否需要监听派生的uri还是只严格匹配该uri,在后面通知机制一节中我们再看该变量是如何控制的;第三个为重写的ContentObserver。注意到ContentResolver实际就是通过ContentService这个系统服务来操作的,这里其实容易理解,注册的信息应该交给系统服务来统一管理。接下来看ContentService的registerContentObserver方法:

   public void registerContentObserver(Uri uri, boolean notifyForDescendants,                                        IContentObserver observer, int userHandle) {        if (observer == null || uri == null) {            throw new IllegalArgumentException("You must pass a valid uri and observer");        }        final int uid = Binder.getCallingUid();        final int pid = Binder.getCallingPid();        final int callingUserHandle = UserHandle.getCallingUserId();        // Registering an observer for any user other than the calling user requires uri grant or        // cross user permission        if (callingUserHandle != userHandle) {            if (checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, userHandle)                    != PackageManager.PERMISSION_GRANTED) {                enforceCrossUserPermission(userHandle,                        "no permission to observe other users' provider view");            }        }        if (userHandle < 0) {            if (userHandle == UserHandle.USER_CURRENT) {                userHandle = ActivityManager.getCurrentUser();            } else if (userHandle != UserHandle.USER_ALL) {                throw new InvalidParameterException("Bad user handle for registerContentObserver: "                        + userHandle);            }        }        synchronized (mRootNode) {            mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,                    uid, pid, userHandle);            if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +                    " with notifyForDescendants " + notifyForDescendants);        }    }

mRootNode是一个根节点,它是一个ObserverNode对象,从它的初始化看到它的name是空字符串。来到它的addObserverLocked方法:

       private void addObserverLocked(Uri uri, int index, IContentObserver observer,                                       boolean notifyForDescendants, Object observersLock,                                       int uid, int pid, int userHandle) {            // If this is the leaf node add the observer            if (index == countUriSegments(uri)) {                mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,                        uid, pid, userHandle));                return;            }            // Look to see if the proper child already exists            String segment = getUriSegment(uri, index);            if (segment == null) {                throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");            }            int N = mChildren.size();            for (int i = 0; i < N; i++) {                ObserverNode node = mChildren.get(i);                if (node.mName.equals(segment)) {                    node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,                            observersLock, uid, pid, userHandle);                    return;                }            }            // No child found, create one            ObserverNode node = new ObserverNode(segment);            mChildren.add(node);            node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,                    observersLock, uid, pid, userHandle);        }

第一次传入的index为0,第一个判断是如果传入的uri的segment数量刚好和index一样,才会创建一个新的ObserverEntry放到该ObserverNode对象的容器mObservers中,我们传入的uri segment个数肯定是大于0的。比如:content://com.rhythmjay.providers/item/0的个数就是3。接下来取index为0的segemt,也就是com.rhythmjay.providers,遍历mRootNode的所有子节点,查找子节点中是否有name为com.rhythmjay.providers,有则调用该子节点的addObserverLocked方法且index+1,否则创建一个name为com.rhythmjay.providers的子节点调用其addObserverLocked方法且index+1。
第二次传入的index为1,第一个判断仍然不满足,获取index为1的segment为item。寻找或者创建name为item的子节点,调用其addObserverLocked并且index+1。
第三次传入的index为2,第一个判断仍然不满足,获取index为2的segment为0. 寻找或者创建name为0的子节点,调用其addObserverLocked并且index+1。
第四次传入的index为3,第一个判断满足,直接通过observer新建ObserverNode对象并放入到该子节点的容器mObservers中。
至此,我们可以看到ContentService中实际保存了一颗类似树状的信息,每个叶子节点都根据segment有命名,从根节点到叶子节点保存了一条uri的信息,且每个叶子节点都有保存这条路径uri的监听者observer。

二、通知机制

我们在自定义ContentProvider时,会将一些客户敏感的字段变化通知出去,而这也是通过ContentResolver完成的。首先我们确认好通知的条件,满足条件后我们就可以调用如下方法通知给感兴趣的客户端:
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
这里也会通过ContentService完成通知:

    public void notifyChange(Uri uri, IContentObserver observer,                             boolean observerWantsSelfNotifications, int flags,                             int userHandle) {        ......        try {            ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();            synchronized (mRootNode) {                mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,                        flags, userHandle, calls);            }            final int numCalls = calls.size();            for (int i=0; i<numCalls; i++) {                ObserverCall oc = calls.get(i);                try {                    oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);                    if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "                            + uri);                } catch (RemoteException ex) {                    synchronized (mRootNode) {                        Log.w(TAG, "Found dead observer, removing");                        IBinder binder = oc.mObserver.asBinder();                        final ArrayList<ObserverNode.ObserverEntry> list                                = oc.mNode.mObservers;                        int numList = list.size();                        for (int j=0; j<numList; j++) {                            ObserverNode.ObserverEntry oe = list.get(j);                            if (oe.observer.asBinder() == binder) {                                list.remove(j);                                j--;                                numList--;                            }                        }                    }                }            }            if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {                SyncManager syncManager = getSyncManager();                if (syncManager != null) {                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,                            uri.getAuthority());                }            }    ......    }

这里有个flags,通过上述方式通知的话默认为NOTIFY_SYNC_TO_NETWORK,意思就是会通知网络服务器。这里有创建一个ObserverCall的列表calls,它是一个输出参数,接下来就是从mRootNode来搜集所有的observer了。

        public void collectObserversLocked(Uri uri, int index, IContentObserver observer,                                           boolean observerWantsSelfNotifications, int flags,                                           int targetUserHandle, ArrayList<ObserverCall> calls) {            String segment = null;            int segmentCount = countUriSegments(uri);            if (index >= segmentCount) {                // This is the leaf node, notify all observers                if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);                collectMyObserversLocked(true, observer, observerWantsSelfNotifications,                        flags, targetUserHandle, calls);            } else if (index < segmentCount){                segment = getUriSegment(uri, index);                if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "                        + segment);                // Notify any observers at this level who are interested in descendants                collectMyObserversLocked(false, observer, observerWantsSelfNotifications,                        flags, targetUserHandle, calls);            }            int N = mChildren.size();            for (int i = 0; i < N; i++) {                ObserverNode node = mChildren.get(i);                if (segment == null || node.mName.equals(segment)) {                    // We found the child,                    node.collectObserversLocked(uri, index + 1, observer,                            observerWantsSelfNotifications, flags, targetUserHandle, calls);                    if (segment != null) {                        break;                    }                }            }        }

继续拿通知的uri为content://com.rhythmjay.providers/item/0作例子。
第一次传入的index为0,index小于segmentCount,segment为com.rhythmjay.providers,调用collectMyObserversLocked,注意这里的leaf为false。进入collectMyObserversLocked:

       private void collectMyObserversLocked(boolean leaf, IContentObserver observer,                                              boolean observerWantsSelfNotifications, int flags,                                              int targetUserHandle, ArrayList<ObserverCall> calls) {            int N = mObservers.size();            IBinder observerBinder = observer == null ? null : observer.asBinder();            for (int i = 0; i < N; i++) {                ObserverEntry entry = mObservers.get(i);                // Don't notify the observer if it sent the notification and isn't interested                // in self notifications                boolean selfChange = (entry.observer.asBinder() == observerBinder);                if (selfChange && !observerWantsSelfNotifications) {                    continue;                }                // Does this observer match the target user?                if (targetUserHandle == UserHandle.USER_ALL                        || entry.userHandle == UserHandle.USER_ALL                        || targetUserHandle == entry.userHandle) {                    // Make sure the observer is interested in the notification                    if (leaf) {                        // If we are at the leaf: we always report, unless the sender has asked                        // to skip observers that are notifying for descendants (since they will                        // be sending another more specific URI for them).                        if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0                                && entry.notifyForDescendants) {                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer                                    + ": skip notify for descendants");                            continue;                        }                    } else {                        // If we are not at the leaf: we report if the observer says it wants                        // to be notified for all descendants.                        if (!entry.notifyForDescendants) {                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer                                    + ": not monitor descendants");                            continue;                        }                    }                    if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf                            + " flags=" + Integer.toHexString(flags)                            + " desc=" + entry.notifyForDescendants);                    calls.add(new ObserverCall(this, entry.observer, selfChange,                            UserHandle.getUserId(entry.uid)));                }            }        }

由于mRootNode的mObservers.size()为0,所以这方法中我们什么都没做,接下来就是查找mRootNode的子节点中name为com.rhythmjay.providers的节点。然后调用它的collectObserversLocked方法且index+1。
第二次传入的index为1,index小于segmentCount,segment为item,调用collectMyObserversLocked,注意这里的leaf为false。然后查找是否有监听content://com.rhythmjay.providers的observer,一般我们都不会把authorities作为监听对象。
所以这里也是什么都没做。接下来就是查找mRootNode的子节点中name为item的节点。然后调用它的collectObserversLocked方法且index+1。
第三次传入的index为2,index小于segmentCount,segment为0,调用collectMyObserversLocked,注意这里的leaf为false。然后查找是否有监听content://com.rhythmjay.providers/item的observer。我们假设有这样的observer,则进入到循环中。这里注意到如果满足!entry.notifyForDescendants的条件,会过滤掉该observer,还记得我们在注册的时候registerContentObserver中的第二个参数notifyForDescendants吗,如果为false,则不会搜集该observer,如果为true,则搜集该observer,即如果为true的话,监听content://com.rhythmjay.providers/item的observer在content://com.rhythmjay.providers/item/0字段变化时,也会被通知到。所以创建一个ObserverCall对象增加到列表中。接下来就是查找mRootNode的子节点中name为0的节点。然后调用它的collectObserversLocked方法且index+1。
第四次传入的index为3,index等于segmentCount,然后调用collectMyObserversLocked,leaf为true,将监听content://com.rhythmjay.providers/item/0的observer增加到列表中。到这里,已经完成所有监听者的搜集。
接下来就是遍历列表并调用成员的回调onChange,通过binder的方式回调到各个进程中。然后有SyncManager的scheduleLocalSync方法,实现网络同步。

三、访问数据库

首先来到ContentResolver的query方法,通过acquireUnstableProvider方法和参数uri获取ContentProvider的Binder客户端,将uri转换为auth。ContextImpl中的ApplicationContentResolver实现了该方法,而最终实现的地方在ActivityThread中:

  public final IContentProvider acquireProvider(            Context c, String auth, int userId, boolean stable) {        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);        if (provider != null) {            return provider;        }        // 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.        IActivityManager.ContentProviderHolder holder = null;        try {            holder = ActivityManagerNative.getDefault().getContentProvider(                    getApplicationThread(), auth, userId, stable);        } catch (RemoteException ex) {            throw ex.rethrowFromSystemServer();        }        if (holder == null) {            Slog.e(TAG, "Failed to find provider info for " + auth);            return null;        }        // Install provider will increment the reference count for us, and break        // any ties in the race.        holder = installProvider(c, holder, holder.info,                true /*noisy*/, holder.noReleaseNeeded, stable);        return holder.provider;    }

首先调用acquireExistingProvider,查找本地应用是否已经保存了Provider的客户端记录ProviderClientRecord对象,如果没有返回null,如果有则不管是stable还是unstable均需要将其引用计数加1并通知AMS将ContentProviderConnection(用于维护一个客户端进程和ConntentProvider)的引用计数加1。
这里我们假设是第一次获取该ContentProvider客户端,所以继续往下走,接下来我们要去AMS去获取ContentProviderHolder对象,它是一个Parcelable对象,内部有IContentProvider和ContentProviderConnection的Binder客户端,接下来我们看下AMS的getContentProviderImpl的方法:

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,            String name, IBinder token, boolean stable, int userId) {        ContentProviderRecord cpr;        ContentProviderConnection conn = null;        ProviderInfo cpi = null;        synchronized(this) {            long startTime = SystemClock.uptimeMillis();            ProcessRecord r = null;            if (caller != null) {                r = getRecordForAppLocked(caller);                if (r == null) {                    throw new SecurityException(                            "Unable to find app for caller " + caller                          + " (pid=" + Binder.getCallingPid()                          + ") when getting content provider " + name);                }            }            boolean checkCrossUser = true;            checkTime(startTime, "getContentProviderImpl: getProviderByName");            // First check if this content provider has been published...            cpr = mProviderMap.getProviderByName(name, userId);            // If that didn't work, check if it exists for user 0 and then            // verify that it's a singleton provider before using it.            if (cpr == null && userId != UserHandle.USER_SYSTEM) {                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);                if (cpr != null) {                    cpi = cpr.info;                    if (isSingleton(cpi.processName, cpi.applicationInfo,                            cpi.name, cpi.flags)                            && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {                        userId = UserHandle.USER_SYSTEM;                        checkCrossUser = false;                    } else {                        cpr = null;                        cpi = null;                    }                }            }            boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;            if (providerRunning) {                cpi = cpr.info;                String msg;                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))                        != null) {                    throw new SecurityException(msg);                }                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");                if (r != null && cpr.canRunHere(r)) {                    // This provider has been published or is in the process                    // of being published...  but it is also allowed to run                    // in the caller's process, so don't make a connection                    // and just let the caller instantiate its own instance.                    ContentProviderHolder holder = cpr.newHolder(null);                    // don't give caller the provider object, it needs                    // to make its own.                    holder.provider = null;                    return holder;                }                final long origId = Binder.clearCallingIdentity();                checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");                // In this case the provider instance already exists, so we can                // return it right away.                conn = incProviderCountLocked(r, cpr, token, stable);                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {                        // If this is a perceptible app accessing the provider,                        // make sure to count it as being accessed and thus                        // back up on the LRU list.  This is good because                        // content providers are often expensive to start.                        checkTime(startTime, "getContentProviderImpl: before updateLruProcess");                        updateLruProcessLocked(cpr.proc, false, null);                        checkTime(startTime, "getContentProviderImpl: after updateLruProcess");                    }                }                checkTime(startTime, "getContentProviderImpl: before updateOomAdj");                final int verifiedAdj = cpr.proc.verifiedAdj;                boolean success = updateOomAdjLocked(cpr.proc);                // XXX things have changed so updateOomAdjLocked doesn't actually tell us                // if the process has been successfully adjusted.  So to reduce races with                // it, we will check whether the process still exists.  Note that this doesn't                // completely get rid of races with LMK killing the process, but should make                // them much smaller.                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {                    success = false;                }                maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);                checkTime(startTime, "getContentProviderImpl: after updateOomAdj");                if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);                // NOTE: there is still a race here where a signal could be                // pending on the process even though we managed to update its                // adj level.  Not sure what to do about this, but at least                // the race is now smaller.                if (!success) {                    // Uh oh...  it looks like the provider's process                    // has been killed on us.  We need to wait for a new                    // process to be started, and make sure its death                    // doesn't kill our process.                    Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()                            + " is crashing; detaching " + r);                    boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);                    checkTime(startTime, "getContentProviderImpl: before appDied");                    appDiedLocked(cpr.proc);                    checkTime(startTime, "getContentProviderImpl: after appDied");                    if (!lastRef) {                        // This wasn't the last ref our process had on                        // the provider...  we have now been killed, bail.                        return null;                    }                    providerRunning = false;                    conn = null;                } else {                    cpr.proc.verifiedAdj = cpr.proc.setAdj;                }                Binder.restoreCallingIdentity(origId);            }            if (!providerRunning) {                try {                    checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");                    cpi = AppGlobals.getPackageManager().                        resolveContentProvider(name,                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);                    checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");                } catch (RemoteException ex) {                }                if (cpi == null) {                    return null;                }                // If the provider is a singleton AND                // (it's a call within the same user || the provider is a                // privileged app)                // Then allow connecting to the singleton provider                boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,                        cpi.name, cpi.flags)                        && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);                if (singleton) {                    userId = UserHandle.USER_SYSTEM;                }                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);                checkTime(startTime, "getContentProviderImpl: got app info for user");                String msg;                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))                        != null) {                    throw new SecurityException(msg);                }                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");                if (!mProcessesReady                        && !cpi.processName.equals("system")) {                    // If this content provider does not run in the system                    // process, and the system is not yet ready to run other                    // processes, then fail fast instead of hanging.                    throw new IllegalArgumentException(                            "Attempt to launch content provider before system ready");                }                // Make sure that the user who owns this provider is running.  If not,                // we don't want to allow it to run.                if (!mUserController.isUserRunningLocked(userId, 0)) {                    Slog.w(TAG, "Unable to launch app "                            + cpi.applicationInfo.packageName + "/"                            + cpi.applicationInfo.uid + " for provider "                            + name + ": user " + userId + " is stopped");                    return null;                }                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);                checkTime(startTime, "getContentProviderImpl: before getProviderByClass");                cpr = mProviderMap.getProviderByClass(comp, userId);                checkTime(startTime, "getContentProviderImpl: after getProviderByClass");                final boolean firstClass = cpr == null;                if (firstClass) {                    final long ident = Binder.clearCallingIdentity();                    // If permissions need a review before any of the app components can run,                    // we return no provider and launch a review activity if the calling app                    // is in the foreground.                    if (Build.PERMISSIONS_REVIEW_REQUIRED) {                        if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {                            return null;                        }                    }                    try {                        checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");                        ApplicationInfo ai =                            AppGlobals.getPackageManager().                                getApplicationInfo(                                        cpi.applicationInfo.packageName,                                        STOCK_PM_FLAGS, userId);                        checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");                        if (ai == null) {                            Slog.w(TAG, "No package info for content provider "                                    + cpi.name);                            return null;                        }                        ai = getAppInfoForUser(ai, userId);                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);                    } catch (RemoteException ex) {                        // pm is in same process, this will never happen.                    } finally {                        Binder.restoreCallingIdentity(ident);                    }                }                checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");                if (r != null && cpr.canRunHere(r)) {                    // If this is a multiprocess provider, then just return its                    // info and allow the caller to instantiate it.  Only do                    // this if the provider is the same user as the caller's                    // process, or can run as root (so can be in any process).                    return cpr.newHolder(null);                }                if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "                            + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "                            + cpr.info.name + " callers=" + Debug.getCallers(6));                // This is single process, and our app is now connecting to it.                // See if we are already in the process of launching this                // provider.                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.                if (i >= N) {                    final long origId = Binder.clearCallingIdentity();                    try {                        // Content provider is now in use, its package can't be stopped.                        try {                            checkTime(startTime, "getContentProviderImpl: before set stopped state");                            AppGlobals.getPackageManager().setPackageStoppedState(                                    cpr.appInfo.packageName, false, userId);                            checkTime(startTime, "getContentProviderImpl: after set stopped state");                        } catch (RemoteException e) {                        } catch (IllegalArgumentException e) {                            Slog.w(TAG, "Failed trying to unstop package "                                    + cpr.appInfo.packageName + ": " + e);                        }                        // Use existing process if already started                        checkTime(startTime, "getContentProviderImpl: looking for process record");                        ProcessRecord proc = getProcessRecordLocked(                                cpi.processName, cpr.appInfo.uid, false);                        if (proc != null && proc.thread != null && !proc.killed) {                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,                                    "Installing in existing process " + proc);                            if (!proc.pubProviders.containsKey(cpi.name)) {                                checkTime(startTime, "getContentProviderImpl: scheduling install");                                proc.pubProviders.put(cpi.name, cpr);                                try {                                    proc.thread.scheduleInstallProvider(cpi);                                } catch (RemoteException e) {                                }                            }                        } else {                            checkTime(startTime, "getContentProviderImpl: before start process");                            proc = startProcessLocked(cpi.processName,                                    cpr.appInfo, false, 0, "content provider",                                    new ComponentName(cpi.applicationInfo.packageName,                                            cpi.name), false, false, false);                            checkTime(startTime, "getContentProviderImpl: after start process");                            if (proc == null) {                                Slog.w(TAG, "Unable to launch app "                                        + cpi.applicationInfo.packageName + "/"                                        + cpi.applicationInfo.uid + " for provider "                                        + name + ": process is bad");                                return null;                            }                        }                        cpr.launchingApp = proc;                        mLaunchingProviders.add(cpr);                    } finally {                        Binder.restoreCallingIdentity(origId);                    }                }                checkTime(startTime, "getContentProviderImpl: updating data structures");                // Make sure the provider is published (the same provider class                // may be published under multiple names).                if (firstClass) {                    mProviderMap.putProviderByClass(comp, cpr);                }                mProviderMap.putProviderByName(name, cpr);                conn = incProviderCountLocked(r, cpr, token, stable);                if (conn != null) {                    conn.waiting = true;                }            }            checkTime(startTime, "getContentProviderImpl: done!");        }        // Wait for the provider to be published...        synchronized (cpr) {            while (cpr.provider == null) {                if (cpr.launchingApp == null) {                    Slog.w(TAG, "Unable to launch app "                            + cpi.applicationInfo.packageName + "/"                            + cpi.applicationInfo.uid + " for provider "                            + name + ": launching app became null");                    EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,                            UserHandle.getUserId(cpi.applicationInfo.uid),                            cpi.applicationInfo.packageName,                            cpi.applicationInfo.uid, name);                    return null;                }                try {                    if (DEBUG_MU) Slog.v(TAG_MU,                            "Waiting to start provider " + cpr                            + " launchingApp=" + cpr.launchingApp);                    if (conn != null) {                        conn.waiting = true;                    }                    cpr.wait();                } catch (InterruptedException ex) {                } finally {                    if (conn != null) {                        conn.waiting = false;                    }                }            }        }        return cpr != null ? cpr.newHolder(conn) : null;    }

首先用getProviderByName判断获取ContentProvider的记录ContentProviderRecord是否存在,这里有单例或者针对每个userid有ContentProvider,也就说同一auth的ContentProvider可能只有一个或者也有多个。之后我们来看providerRunning的值如何获取,如果ContentProviderRecord对象存在且其进程存在,且进程未被kill掉,它的值为true,说明ContentProvider已经正常运转起来了。我们先看为true的情况,这里有cpr.canRunHere的判断,如下:

    public boolean canRunHere(ProcessRecord app) {        return (info.multiprocess || info.processName.equals(app.processName))                && uid == app.info.uid;    }

满足设置multiprocess为true或者获取ContentProvider的进程与ContentProvider同一进程,然后shareduid一致则返回true,意思就是我们可以为每个进程都提供一个ContentProvider实例,但必须shareduid一致。在这里直接创建ContentProviderHolder实例,且其IContentProvider为null并返回。
然后再来看下providerRunning为false的情况,首先查询ContentProvider的信息ProviderInfo,通过它保存的包名和类名创建ComponentName对象,最后通过getProviderByClass查询ContentProviderRecord对象,这里与之前getProviderByName类似。如果cpr为null,这种情况只有在Singleton时满足,首先会创建一个新的ContentProviderRecord对象,如果是multiprocess则直接创建ContentProviderHolder返回。接下来判断该ContentProvider是否正在启动,如果未在启动,i >= N为true,进入判断语句内部。接下来根据ContentProvider所在进程是否已经存在,如未存在,则首先启动该进程,我们在之前的Activity启动中已经分析过,这里就直接看进程已经起来的场景,调用ActivityThread的scheduleInstallProvider方法。直接进入installContentProviders方法,如下:

    private void installContentProviders(            Context context, List<ProviderInfo> providers) {        final ArrayList<IActivityManager.ContentProviderHolder> results =            new ArrayList<IActivityManager.ContentProviderHolder>();        for (ProviderInfo cpi : providers) {            if (DEBUG_PROVIDER) {                StringBuilder buf = new StringBuilder(128);                buf.append("Pub ");                buf.append(cpi.authority);                buf.append(": ");                buf.append(cpi.name);                Log.i(TAG, buf.toString());            }            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);            if (cph != null) {                cph.noReleaseNeeded = true;                results.add(cph);            }        }        try {            ActivityManagerNative.getDefault().publishContentProviders(                getApplicationThread(), results);        } catch (RemoteException ex) {            throw ex.rethrowFromSystemServer();        }    }

这里有两个关键调用,installProvider及AMS的publishContentProviders,我们先看installProvider方法:

   private IActivityManager.ContentProviderHolder installProvider(Context context,            IActivityManager.ContentProviderHolder holder, ProviderInfo info,            boolean noisy, boolean noReleaseNeeded, boolean stable) {        ContentProvider localProvider = null;        IContentProvider provider;        if (holder == null || holder.provider == null) {            if (DEBUG_PROVIDER || noisy) {                Slog.d(TAG, "Loading provider " + info.authority + ": "                        + info.name);            }            Context c = null;            ApplicationInfo ai = info.applicationInfo;            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) {                Slog.w(TAG, "Unable to get context for package " +                      ai.packageName +                      " while loading content provider " +                      info.name);                return null;            }            try {                final java.lang.ClassLoader cl = c.getClassLoader();                localProvider = (ContentProvider)cl.                    loadClass(info.name).newInstance();                provider = localProvider.getIContentProvider();                if (provider == null) {                    Slog.e(TAG, "Failed to instantiate class " +                          info.name + " from sourceDir " +                          info.applicationInfo.sourceDir);                    return null;                }                if (DEBUG_PROVIDER) Slog.v(                    TAG, "Instantiating local provider " + info.name);                // XXX Need to create the correct context for this provider.                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 {            provider = holder.provider;            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "                    + info.name);        }        IActivityManager.ContentProviderHolder retHolder;        synchronized (mProviderMap) {            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider                    + " / " + info.name);            IBinder jBinder = provider.asBinder();            if (localProvider != null) {                ComponentName cname = new ComponentName(info.packageName, info.name);                ProviderClientRecord pr = mLocalProvidersByName.get(cname);                if (pr != null) {                    if (DEBUG_PROVIDER) {                        Slog.v(TAG, "installProvider: lost the race, "                                + "using existing local provider");                    }                    provider = pr.mProvider;                } else {                    holder = new IActivityManager.ContentProviderHolder(info);                    holder.provider = provider;                    holder.noReleaseNeeded = true;                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);                    mLocalProviders.put(jBinder, pr);                    mLocalProvidersByName.put(cname, pr);                }                retHolder = pr.mHolder;            } else {                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);                if (prc != null) {                    if (DEBUG_PROVIDER) {                        Slog.v(TAG, "installProvider: lost the race, updating ref count");                    }                    // 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).                    if (!noReleaseNeeded) {                        incProviderRefLocked(prc, stable);                        try {                            ActivityManagerNative.getDefault().removeContentProvider(                                    holder.connection, stable);                        } catch (RemoteException e) {                            //do nothing content provider object is dead any way                        }                    }                } else {                    ProviderClientRecord client = installProviderAuthoritiesLocked(                            provider, localProvider, holder);                    if (noReleaseNeeded) {                        prc = new ProviderRefCount(holder, client, 1000, 1000);                    } else {                        prc = stable                                ? new ProviderRefCount(holder, client, 1, 0)                                : new ProviderRefCount(holder, client, 0, 1);                    }                    mProviderRefCountMap.put(jBinder, prc);                }                retHolder = prc.holder;            }        }        return retHolder;    }

由于我们传入的ContentProviderHolder为null,所以在这里通过反射创建了对应的ContentProvider实例localProvider,并通过attachInfo设置其权限和回调onCreate方法,同时通过getIContentProvider取出IContentProvider。如果查询到的ProviderClientRecord为null,则会创建ContentProviderHolder对象,并将IContentProvider的引用放到ContentProviderHolder对象中。同时也会创建ProviderClientRecord对象,保存到容器中。最终该函数返回ContentProviderHolder对象。
接下来看publishContentProviders,如下

    public final void publishContentProviders(IApplicationThread caller,            List<ContentProviderHolder> providers) {        if (providers == null) {            return;        }        enforceNotIsolatedCaller("publishContentProviders");        synchronized (this) {            final ProcessRecord r = getRecordForAppLocked(caller);            if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);            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;                }                ContentProviderRecord dst = r.pubProviders.get(src.info.name);                if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);                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);                    }                    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--;                        }                    }                    if (wasInLaunchingProviders) {                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);                    }                    synchronized (dst) {                        dst.provider = src.provider;                        dst.proc = r;                        dst.notifyAll();                    }                    updateOomAdjLocked(r);                    maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,                            src.info.authority);                }            }            Binder.restoreCallingIdentity(origId);        }    }

这里会根据之前返回的ContentProviderHolder对象中的信息查询ContentProviderRecord,由于我们之前创建了一个ContentProviderRecord对象,所以它不会为null。将该ContentProviderRecord对象保存到mProviderMap中,实际也是宣告了ContentProviderRecord已经是启动状态了。接着将其中真正启动的ContentProvider记录中移除。接下来比较关键的一点,在这里调用了ContentProviderRecord对象的notify方法,为什么要notify呢,等下我们就知道了。
再回到之前的AMS getContentProviderImpl流程,我们看到这里有wait动作,原来是唤醒的这里,确保getContentProviderImpl不会因为内部的异步处理,导致获取不到正确结果,该接口最终返回ContentProviderHolder对象。
回到客户端进程,再一次调用installProvider,注意与前一次的installProvider的区别,两个进程不一样,一个为ContentProvider所在进程,一个为客户端所在进程。
在这里也是区别multiprocess和普通获取ContentProvider的地方,如果multiprocess为true,则holder.provider == null满足条件,就会重新创建ContentProvider实例,这个实例也就属于客户端的进程中。如果是普通获取ContentProvider,则只是累计计数加1。
我们拿到IContentProvider以后,就可以通过它来访问ContentProvider的内部数据了,处理完后会调用releaseProvider将引用计数减1。
到这里,ContentProvider的工作机制已经分析完成了。

原创粉丝点击