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的工作机制已经分析完成了。
- ContentProvider工作机制
- ContentProvider的工作过程
- contentProvider的工作原理
- ContentProvider的工作过程
- ContentProvider 工作过程
- Android Contentprovider机制详解
- contentprovider数据共享机制
- contentprovider数据共享机制
- IPC机制--使用ContentProvider
- IPC机制--利用ContentProvider
- Replugin ContentProvider实现机制
- Android数据共享机制ContentProvider
- Android数据共享机制ContentProvider
- IPC机制之使用ContentProvider
- Android 四大组件之ContentProvider工作原理
- 四大组件ContentProvider的工作过程
- 利用ContentProvider机制读写联系人信息。
- IPC机制第三篇,ContentProvider实现
- 算法系列——Perfect Squares
- GDOI7.6~7.15模拟总结
- Android常用控件(Widget)
- [bzoj3064]Tyvj 1518 CPU监控 线段树&排行榜垫底留念
- Tensorflow Save
- ContentProvider工作机制
- BZOJ 1145: [CTSC2008]图腾totem 数据结构维护,思维题
- 【in_array和array_search】PHP中的in_array和array_search【原创】
- HDU 1001
- HTML(进阶)
- 服务器内部跳转(请求转发)和请求重定向的区别
- 我的文章被推荐到CSDN首页
- java单例模式
- python正则表达式二:literal、re1|re2 和 .