Android 四大组件之ContentProvider工作原理
来源:互联网 发布:软件测试 培训课程 编辑:程序博客网 时间:2024/06/06 02:13
ContentProvider启动
ContentProvider是一种内容共享型组件,实际上它是通过Binder向其它应用提供数据。当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS中,需要特别注意的是ContentProvider的onCreate方法要早于Application的onCreate方法执行。
废话不多说先看源码,As we all known,每个进程的入口都是ActivityThread.main
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
主要看attach方法
private void attach(boolean system) { ... final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { // Ignore }...}通过前面的分析,这里一眼就可以看出IPC通信调用AMS的同名方法,接着往下看ActivityManagerService.java
@Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }
AMS.attachApplicationLocked这个方法也是很长,这里截取最关键的
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ... ProfilerInfo profilerInfo = profileFile == null ? null : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop); thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, null); ...}
这里又IPC通信,返回客户端处理去了
总结一下,跟ContentProvider相关的事件
1.创建ActivityThread实例,并创建消息队列
2.ActivityThread的attach方法远程调用AMS的attachApplication方法,并将ApplicationThread对象传递给AMS。
3.AMS的attachApplication方法中会调用ApplicationThread的bingApplication(IPC调用)进而通过H对象切换到ActivityThread中执行handleBindApplication方法
这个方法有200多行,这里截取了最后比较关键的部分
private void handleBindApplication(AppBindData data) { ... try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (!data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers; if (providers != null) { installContentProviders(app, providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } // Do this after providers, since instrumentation tests generally start their // test thread at this point, and we don't want that racing. try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); } try { mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } finally { StrictMode.setThreadPolicy(savedPolicy); }}
其中依次会调用到
Application app = data.info.makeApplication(data.restrictedBackupMode, null);和installContentProviders(app, providers);方法,前者就是创建Application对象,而后者则是创建ContentProvider,创建ContentProvider之后则调用Application的onCreate方法。
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) { } }
创建好后通过IPC通信,发布到AMS中,这里还没完接着往下挖,ActivityThread.installProvider
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ... 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); } ...}仿佛看到曙光了,ContentProvider.attachInfo
private void attachInfo(Context context, ProviderInfo info, boolean testing) { /* * We may be using AsyncTask from binder threads. Make it init here * so its static handler is on the main thread. */ AsyncTask.init(); mNoPerms = testing; /* * Only allow it to be set once, so after the content service gives * this to us clients can't change it. */ if (mContext == null) { mContext = context; if (context != null) { mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE); } mMyUid = Process.myUid(); if (info != null) { setReadPermission(info.readPermission); setWritePermission(info.writePermission); setPathPermissions(info.pathPermissions); mExported = info.exported; mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0; setAuthorities(info.authority); } ContentProvider.this.onCreate(); } }最后一行代码验证了我们前面的结论,虽然Application创建在前,但ContentProvider的onCreate方法调用却在Application的onCreate之前.当ContentProvider所在的进程启动的时候,它会同时被启动并被发布到AMS中,这个时候它的onCreate要先去Application的onCreate执行。
这部分的逻辑,我在书上看到的一幅图我觉得无可挑剔,直接拍下来放在博客上
ContentProvider的启动过程:
1.当一个应用启动时,入口方法是ActivityThread的main方法,其中创建ActivityThread的实例并创建主线程的消息队列;
2.ActivityThread的attach方法中会远程调用ActivityManagerService的attachApplication,并将ApplicationThread提供给AMS,ApplicationThread主要用于ActivityThread和AMS之间的通信;
3.ActivityManagerService的attachApplication会调用ApplicationThread的bindApplication方法,这个方法会通过H切换到ActivityThread中去执行,即调用handleBindApplication方法;
4.handleBindApplication方法会创建Application对象并加载ContentProvider,注意是先加载ContentProvider,然后调用Application的onCreate方法。
(3)ContentProvider的android:multiprocess属性决定它是否是单实例,默认值是false,也就是默认是单实例。当设置为true时,每个调用者的进程中都存在一个ContentProvider对象。实际使用中并未发现这样的使用场景,所以一般我们可以认为其为单实例的。
ContentProvider工作原理
当调用ContentProvider的insert、delete、update、query方法中的任何一个时,如果ContentProvider所在的进程没有启动的话,那么就会触发ContentProvider的创建,并伴随着ContentProvider所在进程的启动。
这里直接从query方法顺藤摸瓜,我们的一般使用就是在Context中使用getContentResolver.query方法来获取数据
这里的Context.getContentResolver获取的是ApplicationContentResolver对象,它是ContextImpl的内部类,继承了ContentResolver并实现了其抽象方法。
代码在ContextImpl.java中
private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; private final UserHandle mUser; public ApplicationContentResolver( Context context, ActivityThread mainThread, UserHandle user) { super(context); mMainThread = Preconditions.checkNotNull(mainThread); mUser = Preconditions.checkNotNull(user); } @Override protected IContentProvider acquireProvider(Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true); } @Override protected IContentProvider acquireExistingProvider(Context context, String auth) { return mMainThread.acquireExistingProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true); } @Override public boolean releaseProvider(IContentProvider provider) { return mMainThread.releaseProvider(provider, true); } @Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); } @Override public boolean releaseUnstableProvider(IContentProvider icp) { return mMainThread.releaseProvider(icp, false); } @Override public void unstableProviderDied(IContentProvider icp) { mMainThread.handleUnstableProviderDied(icp.asBinder(), true); } @Override public void appNotRespondingViaProvider(IContentProvider icp) { mMainThread.appNotRespondingViaProvider(icp.asBinder()); } /** @hide */ protected int resolveUserIdFromAuthority(String auth) { return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier()); } }看完了getContentResolver.query的类,接着看方法的实现,方法实现在ApplicationContentResolver父类ContentResolver中
public final Cursor query(final Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null; if (cancellationSignal != null) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } try { qCursor = unstableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); }......首先会获取到一个IContentProvider对象,IContentProvider unstableProvider = acquireUnstableProvider(uri);其本质是通过调用了ApplicationContentResolver类自身的acquireProvider来获取的,
通过上面ApplicationContentResolver类的定义可以看出直接调用了ActivityThread的acquireProvider
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) { } 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; }
以上代码首先会在ActivityThread中查找是否已经存在目标ContentProvider,如果有则直接返回。成员变量mProviderMap就是用来存储ContentProvider的
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();当然不存在目标ContentProvider,则通过IPC调用给AMS,让AMS来启动目标ContentProvider进程并启动ContentProvider,再通过installProvider来修改引用计数。
ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable);
接下来重点关注,AMS怎样启动ContentProvider所在进程的,沿着IPC调用往下挖 AMS.getContentProvider
@Override public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { enforceNotIsolatedCaller("getContentProvider"); if (caller == null) { String msg = "null IApplicationThread when getting content provider " + name; Slog.w(TAG, msg); throw new SecurityException(msg); } // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal // with cross-user grant. return getContentProviderImpl(caller, name, null, stable, userId); }getContentProviderImpl也是一个长函数,截取其中重要的部分
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ... if (!providerRunning) { ... ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null) { if (DEBUG_PROVIDER) { Slog.d(TAG, "Installing in existing process " + proc); } 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); ... ...}
启动ContentProvider所在进程的就是AMS.startProcessLocked,其内部主要是通过Process的start方法来完成新进程的启动的,对这一部分好奇的读者可以移步到老罗的博客:Android应用程序进程启动过程的源代码分析。新进程启动后入口就是ActivityThread.main方法,这跟前面ContentProvider启动部分一致了。
就此总结一下前面ActivityThread.handleBindApplication方法
1.创建ContextImpl和Instrumentation
2.创建Application对象
3.启动ContentProvider所在进程并调用OnCreate方法
4.调用Application的OnCreate方法
到这里ContentProvider所在进程已经启动成功,其他应用也就可以通过它提供的接口方法来访问它了。需要注意的是其他应用拿到的并不是ContentProvider对象,而是IContentProvider对象,了解过AIDL的童鞋都知道这里的IContentProvider就是ContentProvider的Binder对象,IContentProvider的具体实现是ContentProviderNative和ContentProvider.Transport,而其中ContentProvider.Transport继承了ContentProviderNative。我们可以从AMN.getDefault()可以找到类似的设计。
其他应用通过AMS获取到ContentProvider的Binder引用IContentProvider,接着通过IPC调用执行ContentProvider所在进程方法
ContentProvider.Transport
class Transport extends ContentProviderNative { AppOpsManager mAppOpsManager = null; int mReadOp = AppOpsManager.OP_NONE; int mWriteOp = AppOpsManager.OP_NONE; ContentProvider getContentProvider() { return ContentProvider.this; } @Override public String getProviderName() { return getContentProvider().getClass().getName(); } @Override public Cursor query(String callingPkg, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal) { validateIncomingUri(uri); uri = getUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return rejectQuery(uri, projection, selection, selectionArgs, sortOrder, CancellationSignal.fromTransport(cancellationSignal)); } final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.query( uri, projection, selection, selectionArgs, sortOrder, CancellationSignal.fromTransport(cancellationSignal)); } finally { setCallingPackage(original); } }可以清楚的看到这ContentProvider调用了自己的query方法,结果再通过Binder返回给调用进程。至此其他应用调用ContentProvider提供方法的过程分析完毕。其他update、insert、delete流程类似。
- Android 四大组件之ContentProvider工作原理
- Android四大组件之ContentProvider
- android四大组件之ContentProvider
- android四大组件之 ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之ContentProvider
- android 四大组件之ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之ContentProvider
- android四大组件之ContentProvider
- android四大组件之ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之ContentProvider
- Android四大组件之~~ContentProvider
- Android 四大组件之ContentProvider
- Android四大组件之ContentProvider
- 根据集合中 某两个属性 进行集合排序
- 2016第七届蓝桥杯-方格填数
- LeetCode-136. Single Number/137. Single Number II/260. Single Number III
- 我的第一篇csdn博客
- Ubuntu终端(Terminal)常用快捷键
- Android 四大组件之ContentProvider工作原理
- HDU 4722 (数位DP 水~)
- 基于echo例子的netty4通信总结
- 单词拼接
- linux 定时任务 crontab
- 学习笔记:Android里JSON解析的几种方法
- 瑞昱RTL8710对标乐鑫ESP8266 谁将成为物联网首选
- 随笔之ros多线程
- 欢迎使用CSDN-markdown编辑器