ContentProvider的工作过程
2016-04-27 10:47
597 查看
ContentProvider的工作过程
ContentProvider是一种内容共享型组件,它通过Binder向其他组件乃至其他应用提供数据,当ContentProvider所在的进程启动时,ContentProvider会同时启动并发布到AMS中。需要注意,ContentProvider的onCreate要先于Application的onCreate而执行。当一个应用启动时,入口方法为ActivityThread的main方法,在main中创建ActivityThread的实例并创建主线程的消息队列,然后在ActivityThread的attach方法中远程调用AMS的attachApplication方法并将ApplicationThread对象提供给AMS。ApplicationThread是一个Binder对象,它的Binder接口是IApplicationThread,它主要用于ActivityThread和AMS间的通讯。在AMS的attachApplication方法中,会调用ApplicationThread的bindApplication方法(跨进程完成),bindApplication的逻辑会经过ActivityThread中的Handler切换到ActivityThread中执行,具体方法是handleBindApplication。
在handleBindApplication方法中,ActivityThread会创建Application对象并加载ContentProvider。需要注意,ContentProvider的onCreate要先于Application的onCreate而执行。
ContentProvider提供增删改查四个接口来操作数据源。这四个方法都是通过Binder来调用的,外界无法直接访问ContentProvider,只能通过AMS根据Uri来获取对应的ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider的数据源。
下面来分析单实例的ContentProvider的启动过程的源码。
访问ContentProvider需要通过ContentResolver(抽象类,具体实现是ApplicationContentResolver)。通过ContentProvider四个中任一方法都可以触发ContentProvider启动,这里选择query方法。
ContentProvider的query方法中,首先会获取IContentProvider对象(通过ApplicationContentResolve的acquireProvider方法实现)。
ApplicationContentResolve的acquireProvider直接调用了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,存在直接返回。如果没有启动,那就发送一个进程间请求给AMS让其启动目标ContentProvider,再通过installProvider方法来修改引用计数。在AMS中首先会启动ContentProvider所在的进程,然后再启动ContentProvider。启动进程是由AMS的startProcessLocked方法完成,其内部主要通过Process的start方法来完成新进程启动,新进程启动后的入口方法为ActivityThread的main。
public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); 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()); AndroidKeyStoreProvider.install(); // 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(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
创建ActivityThread实例并调用attach方法来进行一系列初始化,接着开始循环消息队列。
ActivityThread的attach方法会将ApplicationThread对象通过AMS的attachApplication方法跨进程传递给AMS。AMS的attachApplication调用了attachApplicationLocked,attachApplicationLocked又调用了bindApplication,这个过程也是跨进程调用。
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(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked());
ActivityThread#ApplicationThread#bindApplication
public final void bindApplication(String processName, ApplicationInfo appInfo, ..... AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; data.providers = providers; data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; data.instrumentationUiAutomationConnection = instrumentationUiConnection; data.debugMode = debugMode; data.enableOpenGlTrace = enableOpenGlTrace; data.restrictedBackupMode = isRestrictedBackupMode; data.persistent = persistent; data.config = config; data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; sendMessage(H.BIND_APPLICATION, data); }
发送BIND_APPLICATION消息,调用ActivityThread的handleBindApplication方法。此方法完成了ContentProvider的创建,分为四个步骤:
创建ContextImpl和Instrumentation
ContextImpl instrContext = ContextImpl.createAppContext(this, pi); try { java.lang.ClassLoader cl = instrContext.getClassLoader(); mInstrumentation = (Instrumentation) cl.loadClass(data.instrumentationName.getClassName()).newInstance(); } catch (Exception e) { throw new RuntimeException( "Unable to instantiate instrumentation " + data.instrumentationName + ": " + e.toString(), e); } mInstrumentation.init(this, instrContext, appContext, new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
创建Application对象
Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app;
启动当前进程的ContentProvider并调用其onCreate方法
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); }
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) { } }
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对象的创建。
private void attachInfo(Context context, ProviderInfo info, boolean testing) { 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(); } }
通过ContentProvider的attachInfo来调用它的onCreate
调用Application的onCreate方法
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); } }
相关文章推荐
- php整理(一):变量和字符串
- <Yii 学习>写入日志
- list 获取应用icon
- php 共享库.so不能从一台主机直接copy到另一台主机
- tftp环境搭建
- 创建一个Windows的NTP Server
- PHP中获取当前页面的完整URL
- phpstorm编辑器智能提示框架代码
- add_unit.php
- THNIKPHP URL重写不成功的问题
- PHP中文处理 中文字符串截取(mb_substr)和获取中文字符串字数
- PHP == 和 ===
- 使用Dezender对zend加密后的php文件进行解密
- 每天laravel-20160725| Genaerator command-3
- 每天laravel-20160725| Genaerator command-3
- vtk实战(十)—文本格式转vtp格式
- php在原生代码中如何简单快速的发送email
- 夺命雷公狗ThinkPHP项目之----企业网站30之网站前台头部导航的高亮显示
- PHP中session变量的销毁
- 夺命雷公狗ThinkPHP项目之----企业网站29之网站前台左侧导航的高亮显示