概述 ContentProvider用于提供数据的统一访问格式,封装了底层的具体实现。对于数据的使用者来说,无需知晓数据的来源是数据库、本地文件或者网络等,使用者只需简单地使用ContentProvider提供的数据增删查改操作接口即可,基本使用可参考Android四大组件之ContentProvider 。
注:本文基于Android 10源码,为了文章的简洁性,引用源码的地方可能有所删减。
注:下面使用CR代表ContentResolver,CP代表ContentProvider,CPR代表ContentProviderRecord,AT代表ActivityThread,AMS代表ActivityManagerService。
ContextImpl.getContentResolver 1 2 3 4 5 6 7 8 9 10 11 12 class ContextImpl extends Context { private final ApplicationContentResolver mContentResolver; private ContextImpl (@NonNull ActivityThread mainThread) { mContentResolver = new ApplicationContentResolver(this , mainThread); } @Override public ContentResolver getContentResolver () { return mContentResolver; } }
CR.insert/delete/query/update CR.insert 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public final @Nullable Uri insert (@RequiresPermission .Write @NonNull Uri url, @Nullable ContentValues values) { Preconditions.checkNotNull(url, "url" ); IContentProvider provider = acquireProvider(url); if (provider == null ) { throw new IllegalArgumentException("Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; return createdRow; } catch (RemoteException e) { return null ; } finally { releaseProvider(provider); } }
通过调用acquireProvider()方法尝试获取provider,然后执行insert操作。
CR.delete 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public final int delete (@RequiresPermission .Write @NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs) { Preconditions.checkNotNull(url, "url" ); IContentProvider provider = acquireProvider(url); if (provider == null ) { throw new IllegalArgumentException("Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; return rowsDeleted; } catch (RemoteException e) { return -1 ; } finally { releaseProvider(provider); } }
通过调用acquireProvider()方法尝试获取provider,然后执行delete操作。
CR.query 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(uri, null , null , null , null ); public abstract class ContentResolver { public final @Nullable Cursor query (@RequiresPermission .Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return query(uri, projection, selection, selectionArgs, sortOrder, null ); } public final @Nullable Cursor query (@RequiresPermission .Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder); return query(uri, projection, queryArgs, cancellationSignal); } public final @Nullable Cursor query (final @RequiresPermission .Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri" ); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null ) { return null ; } IContentProvider stableProvider = null ; Cursor qCursor = null ; try { long startTime = SystemClock.uptimeMillis(); try { qCursor = unstableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null ) { return null ; } qCursor = stableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } if (qCursor == null ) { return null ; } qCursor.getCount(); final IContentProvider provider = (stableProvider != null ) ? stableProvider : acquireProvider(uri); final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider); stableProvider = null ; qCursor = null ; return wrapper; } catch (RemoteException e) { return null ; } finally { if (qCursor != null ) { qCursor.close(); } if (unstableProvider != null ) { releaseUnstableProvider(unstableProvider); } if (stableProvider != null ) { releaseProvider(stableProvider); } } } }
首先调用acquireUnstableProvider()方法尝试获取unstable的ContentProvider,然后执行query操作,如果query过程抛出DeadObjectException异常,即代表ContentProvider所在进程死亡,则通过acquireProvider()方法尝试获取stable的ContentProvider,然后接着执行query操作。
CR.update 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public final int update (@RequiresPermission .Write @NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs) { Preconditions.checkNotNull(uri, "uri" ); IContentProvider provider = acquireProvider(uri); if (provider == null ) { throw new IllegalArgumentException("Unknown URI " + uri); } try { long startTime = SystemClock.uptimeMillis(); int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs); long durationMillis = SystemClock.uptimeMillis() - startTime; return rowsUpdated; } catch (RemoteException e) { return -1 ; } finally { releaseProvider(provider); } }
通过调用acquireProvider()方法尝试获取provider,然后执行update操作。
CR.acquire(Unstable)Provider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 ; } protected abstract IContentProvider acquireUnstableProvider (Context c, String name) ;protected abstract IContentProvider acquireProvider (Context c, String name) ;
acquireProvider和acquireUnstableProvider都是抽象方法,具体看看ApplicationContentResolver中的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static final class ApplicationContentResolver extends ContentResolver { @Override protected IContentProvider acquireProvider (Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true ); } @Override protected IContentProvider acquireUnstableProvider (Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false ); } }
可以看到不论是acquireUnstableProvider还是acquireProvider方法,最终都会调用ActivityThread的acquireProvider()方法。
AT.acquireProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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; } ContentProviderHolder holder = null ; try { synchronized (getGetProviderLock(auth, userId)) { holder = ActivityManager.getService().getContentProvider(getApplicationThread(), auth, userId, stable); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } if (holder == null ) { return null ; } holder = installProvider(c, holder, holder.info, true , holder.noReleaseNeeded, stable); return holder.provider; }
该方法过程:
首先通过acquireExistingProvider()方法尝试获取已存储的provider,获取成功则直接返回,否则继续执行;
通过AMS.getContentProvider()方法来获取provider,当无法获取对应的provider则直接返回,否则继续执行;
采用installProvider()安装provider,并该provider的增加引用计数。
AT.acquireExistingProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();public final IContentProvider acquireExistingProvider ( Context c, String auth, int userId, boolean stable) { synchronized (mProviderMap) { 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()) { handleUnstableProviderDiedLocked(jBinder, true ); return null ; } ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null ) { incProviderRefLocked(prc, stable); } return provider; } }
AMS.getContentProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public final ContentProviderHolder getContentProvider ( IApplicationThread caller, String name, int userId, boolean stable) { enforceNotIsolatedCaller("getContentProvider" ); if (caller == null ) { throw new SecurityException(msg); } return getContentProviderImpl(caller, name, null , stable, userId); } private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; synchronized (this ) { ProcessRecord r = getRecordForAppLocked(caller); cpr = mProviderMap.getProviderByName(name, userId); boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; if (providerRunning) { } if (!providerRunning) { } } synchronized (cpr) { while (cpr.provider == null ) { } } return cpr != null ? cpr.newHolder(conn) : null ; }
该方法是获取provider的核心代码,可以分成以下3部分:
目标provider进程已存在;
目标provider进程不存在;
循环等待provider发布完成。
该方法的name参数就是前面的auth,即provider标签里面的android:authorities
值。
目标provider已存在 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; synchronized (this ) { ProcessRecord r = getRecordForAppLocked(caller); cpr = mProviderMap.getProviderByName(name, userId); boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; if (providerRunning) { cpi = cpr.info; String msg; if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) != null ) { throw new SecurityException(msg); } if (r != null && cpr.canRunHere(r)) { ContentProviderHolder holder = cpr.newHolder(null ); holder.provider = null ; return holder; } if (AppGlobals.getPackageManager().resolveContentProvider(name, 0 , userId) == null ) { return null ; } final long origId = Binder.clearCallingIdentity(); conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null && (conn.stableCount+conn.unstableCount) == 1 ) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { updateLruProcessLocked(cpr.proc, false , null ); } } final int verifiedAdj = cpr.proc.verifiedAdj; boolean success = updateOomAdjLocked(cpr.proc, true ); if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) { success = false ; } if (!success) { boolean lastRef = decProviderCountLocked(conn, cpr, token, stable); appDiedLocked(cpr.proc); if (!lastRef) { return null ; } providerRunning = false ; conn = null ; } else { cpr.proc.verifiedAdj = cpr.proc.setAdj; } Binder.restoreCallingIdentity(origId); } } return cpr != null ? cpr.newHolder(conn) : null ; }
目标provider不存在 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; synchronized (this ) { ProcessRecord r = getRecordForAppLocked(caller); cpr = mProviderMap.getProviderByName(name, userId); boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; if (!providerRunning) { cpi = AppGlobals.getPackageManager().resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); if (cpi == null ) { return null ; } 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); String msg; if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) != null ) { throw new SecurityException(msg); } if (!mProcessesReady && !cpi.processName.equals("system" )) { throw new IllegalArgumentException("Attempt to launch content provider before system ready" ); } if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() && "system" .equals(cpi.processName)) { throw new IllegalStateException("Cannot access system provider: '" + cpi.authority + "' before system providers are installed!" ); } if (!mUserController.isUserRunning(userId, 0 )) { return null ; } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr = mProviderMap.getProviderByClass(comp, userId); final 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 ) { return null ; } ai = getAppInfoForUser(ai, userId); cpr = new ContentProviderRecord(this , cpi, ai, comp, singleton); } catch (RemoteException ex) { } finally { Binder.restoreCallingIdentity(ident); } } if (r != null && cpr.canRunHere(r)) { return cpr.newHolder(null ); } final int N = mLaunchingProviders.size(); int i; for (i = 0 ; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break ; } } if (i >= N) { final long origId = Binder.clearCallingIdentity(); try { AppGlobals.getPackageManager().setPackageStoppedState(cpr.appInfo.packageName, false , userId); ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false ); if (proc != null && proc.thread != null && !proc.killed) { if (!proc.pubProviders.containsKey(cpi.name)) { proc.pubProviders.put(cpi.name, cpr); proc.thread.scheduleInstallProvider(cpi); } } else { proc = startProcessLocked(cpi.processName, cpr.appInfo, false , 0 , "content provider" , new ComponentName(cpi.applicationInfo.packageName, cpi.name), false , false , false ); if (proc == null ) { return null ; } } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); } finally { Binder.restoreCallingIdentity(origId); } } if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null ) { conn.waiting = true ; } } } return cpr != null ? cpr.newHolder(conn) : null ; }
当进程没有启动时,调用startProcessLocked方法启动目标provider进程,由Android-Application启动原理 可知,AMS的attachApplicationLocked方法会调用IApplicationThread.bindApplication方法,即通过Binder的方式调用到目标进程的ActivityThread会执行handleBindApplication方法:
1 2 3 4 5 6 7 private void handleBindApplication (AppBindData data) { if (!ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); } }
当目标进程已经启动时,则调用proc.thread.scheduleInstallProvider
方法通过Binder调用到目标进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private class ApplicationThread extends IApplicationThread .Stub { @Override public void scheduleInstallProvider (ProviderInfo provider) { sendMessage(H.INSTALL_PROVIDER, provider); } } case INSTALL_PROVIDER: handleInstallProvider((ProviderInfo) msg.obj); break ; public void handleInstallProvider (ProviderInfo info) { final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { installContentProviders(mInitialApplication, Arrays.asList(info)); } finally { StrictMode.setThreadPolicy(oldPolicy); } }
可知无论目标进程启动与否,最终都会调用到installContentProviders方法。
等待目标provider发布 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; synchronized (cpr) { while (cpr.provider == null ) { if (cpr.launchingApp == null ) { return null ; } try { 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 ; }
CPR.newHolder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public ContentProviderHolder newHolder (ContentProviderConnection conn) { ContentProviderHolder holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = noReleaseNeeded; holder.connection = conn; return holder; } public class ContentProviderHolder implements Parcelable { public final ProviderInfo info; public IContentProvider provider; public IBinder connection; public boolean noReleaseNeeded; }
CPR.canRunHere 1 2 3 4 5 public boolean canRunHere (ProcessRecord app) { return (info.multiprocess || info.processName.equals(app.processName)) && uid == app.info.uid; }
该ContentProvider是否能运行在调用者所在进程需要满足以下条件:
ContentProvider在AndroidManifest.xml文件配置multiprocess=true或调用者进程与ContentProvider在同一个进程
ContentProvider进程跟调用者所在进程是同一个uid
AT.installContentProviders 该方法会先调用installProvider函数安装provider,然后通过Binder调用AMS的publishContentProviders方法来发布provider。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 , true , true ); if (cph != null ) { cph.noReleaseNeeded = true ; results.add(cph); } } try { ActivityManager.getService().publishContentProviders(getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }
AMS.publishContentProviders 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public final void publishContentProviders (IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null ) { return ; } enforceNotIsolatedCaller("publishContentProviders" ); synchronized (this ) { final ProcessRecord r = getRecordForAppLocked(caller); if (r == null ) { throw new SecurityException(); } 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 (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, true ); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }
AT.installProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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 ) { 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 { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } if (c == null ) { return null ; } 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 ) { packageInfo = getSystemContext().mPackageInfo; } localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name); provider = localProvider.getIContentProvider(); if (provider == null ) { return null ; } localProvider.attachInfo(c, info); } catch (java.lang.Exception e) { return null ; } } else { provider = holder.provider; } ContentProviderHolder retHolder; synchronized (mProviderMap) { IBinder jBinder = provider.asBinder(); if (localProvider != null ) { ComponentName cname = new ComponentName(info.packageName, info.name); ProviderClientRecord pr = mLocalProvidersByName.get(cname); if (pr != null ) { provider = pr.mProvider; } else { holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = true ; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null ) { if (!noReleaseNeeded) { incProviderRefLocked(prc, stable); ActivityManager.getService().removeContentProvider(holder.connection, stable); } } 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; }
CP.getIContentProvider 1 2 3 4 5 6 7 8 9 10 11 public abstract class ContentProvider implements ComponentCallbacks2 { public IContentProvider getIContentProvider () { return mTransport; } class Transport extends ContentProviderNative { } } abstract public class ContentProviderNative extends Binder implements IContentProvider {}
AMS.removeContentProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void removeContentProvider (IBinder connection, boolean stable) { enforceNotIsolatedCaller("removeContentProvider" ); long ident = Binder.clearCallingIdentity(); try { synchronized (this ) { ContentProviderConnection conn; try { conn = (ContentProviderConnection)connection; } catch (ClassCastException e) { throw new IllegalArgumentException(msg); } if (conn == null ) { throw new NullPointerException("connection is null" ); } if (decProviderCountLocked(conn, null , null , stable)) { updateOomAdjLocked(); } } } finally { Binder.restoreCallingIdentity(ident); } }
AT.installProviderAuthoritiesLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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); if (provider != null ) { for (String auth : auths) { switch (auth) { case ContactsContract.AUTHORITY: case CallLog.AUTHORITY: case CallLog.SHADOW_AUTHORITY: case BlockedNumberContract.AUTHORITY: case CalendarContract.AUTHORITY: case Downloads.Impl.AUTHORITY: case "telephony" : Binder.allowBlocking(provider.asBinder()); } } } final ProviderClientRecord pcr = new ProviderClientRecord( auths, provider, localProvider, holder); for (String auth : auths) { final ProviderKey key = new ProviderKey(auth, userId); final ProviderClientRecord existing = mProviderMap.get(key); if (existing != null ) { Slog.w(TAG, "Content provider " + pcr.mHolder.info.name + " already published as " + auth); } else { mProviderMap.put(key, pcr); } } return pcr; }
调用进程: insert/delete/query/update 在获取了ContentProvider后,调用进程(客户端)接着开始远程调用目标进程的ContentProvider(服务端)相对应的接口。由上面AMS的代码可知,服务端的Binder实例是Transport对象,Transport继承自ContentProviderNative:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 abstract public class ContentProviderNative extends Binder implements IContentProvider { public ContentProviderNative () { attachInterface(this , descriptor); } static public IContentProvider asInterface (IBinder obj) { if (obj == null ) { return null ; } IContentProvider in = (IContentProvider)obj.queryLocalInterface(descriptor); if (in != null ) { return in; } return new ContentProviderProxy(obj); } }
因此客户端的Binder代理是ContentProviderProxy对象,调用进程调用增删查改方法时,会通过Binder远程调用到目标ContentProvider进程的onTransact方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 final class ContentProviderProxy implements IContentProvider { public ContentProviderProxy (IBinder remote) { mRemote = remote; } @Override public IBinder asBinder () { return mRemote; } @Override public Uri insert (String callingPkg, Uri url, ContentValues values) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IContentProvider.descriptor); data.writeString(callingPkg); url.writeToParcel(data, 0 ); values.writeToParcel(data, 0 ); mRemote.transact(IContentProvider.INSERT_TRANSACTION, data, reply, 0 ); DatabaseUtils.readExceptionFromParcel(reply); Uri out = Uri.CREATOR.createFromParcel(reply); return out; } finally { data.recycle(); reply.recycle(); } } }
目标进程: insert/delete/query/update 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 abstract public class ContentProviderNative extends Binder implements IContentProvider { @Override public IBinder asBinder () { return this ; } @Override public boolean onTransact (int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { switch (code) { case QUERY_TRANSACTION: { Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal); if (cursor != null ) { CursorToBulkCursorAdaptor adaptor = null ; try { adaptor = new CursorToBulkCursorAdaptor(cursor, observer, getProviderName()); cursor = null ; BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor(); adaptor = null ; reply.writeNoException(); reply.writeInt(1 ); d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } finally { if (adaptor != null ) { adaptor.close(); } if (cursor != null ) { cursor.close(); } } } else { reply.writeNoException(); reply.writeInt(0 ); } return true ; } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); return true ; } return super .onTransact(code, data, reply, flags); } }
上面onTransact方法中都是调用的子类Transport中对应的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Transport extends ContentProviderNative { @Override public Cursor query (String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null ) != AppOpsManager.MODE_ALLOWED) { if (projection != null ) { return new MatrixCursor(projection, 0 ); } Cursor cursor = ContentProvider.this .query(uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); if (cursor == null ) { return null ; } return new MatrixCursor(cursor.getColumnNames(), 0 ); } final String original = setCallingPackage(callingPkg); try { return ContentProvider.this .query(uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } finally { setCallingPackage(original); } } }
ContentProvider引用计数 这里将ContentProvider引用计数单独作为一节进行解析。
相关类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public final class ContentProviderConnection extends Binder { public final ContentProviderRecord provider; public final ProcessRecord client; public final long createTime; public int stableCount; public int unstableCount; public boolean waiting; public boolean dead; public int numStableIncs; public int numUnstableIncs; } private static final class ProviderRefCount { public final ContentProviderHolder holder; public final ProviderClientRecord client; public int stableCount; public int unstableCount; public boolean removePending; }
AMS.incProviderCountLocked incProviderCountLocked方法运行在system_server进程,增加引用成功后,返回一个表示与客户端连接的ContentProviderConnection对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ContentProviderConnection incProviderCountLocked (ProcessRecord r, final ContentProviderRecord cpr, IBinder externalProcessToken, 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 conn = new ContentProviderConnection(cpr, r); 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.curProcState, cpr.uid, cpr.name, cpr.info.processName); return conn; } cpr.addExternalProcessHandleLocked(externalProcessToken); return null ; }
AMS.decProviderCountLocked decProviderCountLocked运行在system_server进程,引用减小成功后,当provider连接的stable和unstable引用次数都为0时移除该连接对象信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 ) { cpr.connections.remove(conn); conn.client.conProviders.remove(conn); if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) { if (cpr.proc != null ) { cpr.proc.lastProviderTime = SystemClock.uptimeMillis(); } } stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name); return true ; } return false ; } cpr.removeExternalProcessHandleLocked(externalProcessToken); return false ; }
AT.incProviderRefLocked incProviderRefLocked运行在客户端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private final void incProviderRefLocked (ProviderRefCount prc, boolean stable) { if (stable) { prc.stableCount += 1 ; if (prc.stableCount == 1 ) { int unstableDelta; if (prc.removePending) { unstableDelta = -1 ; prc.removePending = false ; mH.removeMessages(H.REMOVE_PROVIDER, prc); } else { unstableDelta = 0 ; } ActivityManager.getService().refContentProvider(prc.holder.connection, 1 , unstableDelta); } } else { prc.unstableCount += 1 ; if (prc.unstableCount == 1 ) { if (prc.removePending) { prc.removePending = false ; mH.removeMessages(H.REMOVE_PROVIDER, prc); } else { ActivityManager.getService().refContentProvider(prc.holder.connection, 0 , 1 ); } } } }
removePending是指当前正在处于移除引用计数的过程,当removePending=true代表正处于移除最后引用的情况。而此时有需要增加引用计数,所以会抵消一次unstableCount加1的操作。
AT.releaseProvider CR.releaseProvider跟CR.releaseUnstableProvider都会调用到AT.releaseProvider方法,releaseProvider运行在客户端,会减小引用计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public final boolean releaseProvider (IContentProvider provider, boolean stable) { IBinder jBinder = provider.asBinder(); synchronized (mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc == null ) { return false ; } boolean lastRef = false ; if (stable) { if (prc.stableCount == 0 ) { return false ; } prc.stableCount -= 1 ; if (prc.stableCount == 0 ) { lastRef = prc.unstableCount == 0 ; ActivityManager.getService().refContentProvider(prc.holder.connection, -1 , lastRef ? 1 : 0 ); } } else { if (prc.unstableCount == 0 ) { return false ; } prc.unstableCount -= 1 ; if (prc.unstableCount == 0 ) { lastRef = prc.stableCount == 0 ; if (!lastRef) { ActivityManager.getService().refContentProvider(prc.holder.connection, 0 , -1 ); } } } if (lastRef) { if (!prc.removePending) { prc.removePending = true ; Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc); mH.sendMessage(msg); } } return true ; } }
该方法先减小ProviderRefCount对象的引用,再通过Binder调用AMS.refContentProvider来减小ContentProviderConnection的引用。
主线程收到REMOVE_PROVIDER消息后会调用AT.completeRemoveProvider方法。
AMS.refContentProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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; } }
该方法修改了conn的stable和unstable引用次数并返回connection是否存活,修改后的stable和unstable值都必须大于或等于0,且其和必须大于0。
AT.completeRemoveProvider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 final void completeRemoveProvider (ProviderRefCount prc) { synchronized (mProviderMap) { if (!prc.removePending) { return ; } 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); } } } ActivityManager.getService().removeContentProvider(prc.holder.connection, false ); } public void removeContentProvider (IBinder connection, boolean stable) { enforceNotIsolatedCaller("removeContentProvider" ); long ident = Binder.clearCallingIdentity(); try { synchronized (this ) { ContentProviderConnection conn; conn = (ContentProviderConnection)connection; if (conn == null ) { throw new NullPointerException("connection is null" ); } if (decProviderCountLocked(conn, null , null , stable)) { updateOomAdjLocked(); } } } finally { Binder.restoreCallingIdentity(ident); } }
Provider死亡 AMS.appDiedLocked 当provider进程死亡时,对于unstable的provider可直接调用unstableProviderDied,或者是当provider进程死亡后会有死亡回调binderDied,这两个方法最终都会调用到AMS.appDiedLocked()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 final void appDiedLocked (ProcessRecord app, int pid, IApplicationThread thread, boolean fromBinderDied) { synchronized (mPidsSelfLocked) { ProcessRecord curProc = mPidsSelfLocked.get(pid); if (curProc != app) { return ; } } if (!app.killed) { if (!fromBinderDied) { killProcessQuiet(pid); } killProcessGroup(app.uid, pid); app.killed = true ; } if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { handleAppDiedLocked(app, false , true ); if (doOomAdj) { updateOomAdjLocked(); } if (doLowMem) { doLowMemReportIfNeededLocked(app); } } } private final void handleAppDiedLocked (ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1 , false ); }
AMS.cleanUpApplicationRecordLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private final boolean cleanUpApplicationRecordLocked (ProcessRecord app, boolean restarting, boolean allowRestart, int index, boolean replacingPid) { for (int i = app.pubProviders.size() - 1 ; i >= 0 ; i--) { ContentProviderRecord cpr = app.pubProviders.valueAt(i); final boolean always = app.bad || !allowRestart; boolean inLaunching = removeDyingProviderLocked(app, cpr, always); if ((inLaunching || always) && cpr.hasConnectionOrHandle()) { restart = true ; } cpr.provider = null ; cpr.proc = null ; } app.pubProviders.clear(); if (cleanupAppInLaunchingProvidersLocked(app, false )) { restart = true ; } return false ; }
AMS.removeDyingProviderLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 private final boolean removeDyingProviderLocked (ProcessRecord proc, ContentProviderRecord cpr, boolean always) { final boolean inLaunching = mLaunchingProviders.contains(cpr); if (!inLaunching || always) { synchronized (cpr) { cpr.launchingApp = null ; cpr.notifyAll(); } mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid)); String names[] = cpr.info.authority.split(";" ); for (int j = 0 ; j < names.length; j++) { mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid)); } } for (int i = cpr.connections.size() - 1 ; i >= 0 ; i--) { ContentProviderConnection conn = cpr.connections.get(i); if (conn.waiting) { if (inLaunching && !always) { continue ; } } ProcessRecord capp = conn.client; conn.dead = true ; if (conn.stableCount > 0 ) { if (!capp.persistent && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { capp.kill("depends on provider " + cpr.name.flattenToShortString() + " in dying proc " + (proc != null ? proc.processName : "??" ) + " (adj " + (proc != null ? proc.setAdj : "??" ) + ")" , true ); } } else if (capp.thread != null && conn.provider.provider != null ) { try { capp.thread.unstableProviderDied(conn.provider.provider.asBinder()); } catch (RemoteException e) { } cpr.connections.remove(i); if (conn.client.conProviders.remove(conn)) { stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name); } } } if (inLaunching && always) { mLaunchingProviders.remove(cpr); } return inLaunching; }
小结 removePending是指即将被移除的引用,lastRef是指当前引用为0。
当Provider进程死亡后,当Client进程存在对某个provider的引用时,则会根据provider类型进行不同的处理:
对于stable provider(即conn.stableCount > 0): 会杀掉所有跟该provider建立stable连接的非persistent进程。
对于unstable provider(即conn.unstableCount > 0): 不会导致client进程被级联所杀,只会回调unstableProviderDied来清理相关信息。
当stable和unstable引用计数都为0时则移除connection信息。
FileProvider 概述 对于面向Android 7.0及以上的应用,Android禁止在应用外部公开file://url
,如果一项包含文件URI的intent离开应用,则应用会抛出FileUriExposedException异常。
解决方案:要在应用间共享文件,应发送一项content://url
,并授予URI临时访问权限,进行此授权的最简单方式是使用FileProvider类。FileProvider是ContentProvider的一个特殊的子类,它让应用间共享文件变得更加容易,其通过创建一个Content URI来代替File URI。
具体使用见:Android-FileProvider用法 。
FileProvider.getUriForFile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class FileProvider extends ContentProvider { private static final String META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS" ; private static final String TAG_ROOT_PATH = "root-path" ; private static final String TAG_FILES_PATH = "files-path" ; private static final String TAG_CACHE_PATH = "cache-path" ; private static final String TAG_EXTERNAL = "external-path" ; private static final String TAG_EXTERNAL_FILES = "external-files-path" ; private static final String TAG_EXTERNAL_CACHE = "external-cache-path" ; private static final String TAG_EXTERNAL_MEDIA = "external-media-path" ; private static final String ATTR_NAME = "name" ; private static final String ATTR_PATH = "path" ; private static final File DEVICE_ROOT = new File("/" ); private static HashMap<String, PathStrategy> sCache = new HashMap<String, PathStrategy>(); private PathStrategy mStrategy; public static Uri getUriForFile (@NonNull Context context, @NonNull String authority, @NonNull File file) { final PathStrategy strategy = getPathStrategy(context, authority); return strategy.getUriForFile(file); } private static PathStrategy getPathStrategy (Context context, String authority) { PathStrategy strat; synchronized (sCache) { strat = sCache.get(authority); if (strat == null ) { try { strat = parsePathStrategy(context, authority); } catch (IOException e) { throw new IllegalArgumentException("..." ); } catch (XmlPullParserException e) { throw new IllegalArgumentException("..." ); } } } return strat; } }
解析xml配置 接下来看看parsePathStrategy方法,这个方法主要是通过解析xml文件,获取到paths标签下配置的路径,并将具体的File对象存储到PathStrategy对象中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 private static PathStrategy parsePathStrategy (Context context, String authority) throws IOException, XmlPullParserException { final SimplePathStrategy strat = new SimplePathStrategy(authority); final ProviderInfo info = context.getPackageManager().resolveContentProvider(authority, PackageManager.GET_META_DATA); final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS); if (in == null ) { throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data" ); } int type; while ((type = in.next()) != END_DOCUMENT) { if (type == START_TAG) { final String tag = in.getName(); final String name = in.getAttributeValue(null , ATTR_NAME); String path = in.getAttributeValue(null , ATTR_PATH); File target = null ; if (TAG_ROOT_PATH.equals(tag)) { target = DEVICE_ROOT; } else if (TAG_FILES_PATH.equals(tag)) { target = context.getFilesDir(); } else if (TAG_CACHE_PATH.equals(tag)) { target = context.getCacheDir(); } else if (TAG_EXTERNAL.equals(tag)) { target = Environment.getExternalStorageDirectory(); } else if (TAG_EXTERNAL_FILES.equals(tag)) { File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null ); if (externalFilesDirs.length > 0 ) { target = externalFilesDirs[0 ]; } } else if (TAG_EXTERNAL_CACHE.equals(tag)) { File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context); if (externalCacheDirs.length > 0 ) { target = externalCacheDirs[0 ]; } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && TAG_EXTERNAL_MEDIA.equals(tag)) { File[] externalMediaDirs = context.getExternalMediaDirs(); if (externalMediaDirs.length > 0 ) { target = externalMediaDirs[0 ]; } } if (target != null ) { strat.addRoot(name, buildPath(target, path)); } } } return strat; }
PathStrategy 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 interface PathStrategy { Uri getUriForFile (File file) ; File getFileForUri (Uri uri) ; } static class SimplePathStrategy implements PathStrategy { private final String mAuthority; private final HashMap<String, File> mRoots = new HashMap<String, File>(); SimplePathStrategy(String authority) { mAuthority = authority; } void addRoot (String name, File root) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("Name must not be empty" ); } root = root.getCanonicalFile(); mRoots.put(name, root); } @Override public Uri getUriForFile (File file) { String path; path = file.getCanonicalPath(); Map.Entry<String, File> mostSpecific = null ; for (Map.Entry<String, File> root : mRoots.entrySet()) { final String rootPath = root.getValue().getPath(); if (path.startsWith(rootPath) && (mostSpecific == null || rootPath.length() > mostSpecific.getValue().getPath().length())) { mostSpecific = root; } } if (mostSpecific == null ) { throw new IllegalArgumentException("Failed to find configured root that contains " + path); } final String rootPath = mostSpecific.getValue().getPath(); if (rootPath.endsWith("/" )) { path = path.substring(rootPath.length()); } else { path = path.substring(rootPath.length() + 1 ); } path = Uri.encode(mostSpecific.getKey()) + '/' + Uri.encode(path, "/" ); return new Uri.Builder().scheme("content" ).authority(mAuthority).encodedPath(path).build(); } @Override public File getFileForUri (Uri uri) { String path = uri.getEncodedPath(); final int splitIndex = path.indexOf('/' , 1 ); final String tag = Uri.decode(path.substring(1 , splitIndex)); path = Uri.decode(path.substring(splitIndex + 1 )); final File root = mRoots.get(tag); if (root == null ) { throw new IllegalArgumentException("Unable to find configured root for " + uri); } File file = new File(root, path); try { file = file.getCanonicalFile(); } catch (IOException e) { throw new IllegalArgumentException("Failed to resolve canonical path for " + file); } if (!file.getPath().startsWith(root.getPath())) { throw new SecurityException("Resolved path jumped beyond configured root" ); } return file; } }
可知分享的应用调用getUriForFile方法获得一个临时的uri,目标应用通过getFileForUri方法将uri解析成对应的文件路径,然后获取目标文件对象。
总结