ContentProvider 源码解析
来源:互联网 发布:保险网络大学认证教育 编辑:程序博客网 时间:2024/06/14 23:04
1, 相关类图
主要文件路径: packages/providers/ContactsProvider
frameworks/base
ContentProvider是四大组件之一,是android中专门用于不同应用进程间进行数据共享的方式。由此, ContentProvider必然涉及到发起进程,AMS(AMS服务所在进程),目标进程(也就是真正实现增删改查的ContentProvider所在的进程)三个进程之间的通信。目标ContentProvider至少有5个方法,其中onceate运行于主线程中,其他四个增删改查方法都由外界回调并且运行在Binder线程池中。
Android系统中那么多的数据库,那么根据什么来区分呢?或者说发起进程如何找到对应的数据库呢?以下都以联系人数据库来进行说明。
1. <provider android:name="ContactsProvider2"
2. android:authorities="contacts;com.android.contacts"
3. android:label="@string/provider_label"
4. android:multiprocess="false" // 是否可以在其他进程中创建
5. android:exported="true"
6. android:grantUriPermissions="true"
7. android:readPermission="android.permission.READ_CONTACTS" // 读权限
8. android:writePermission="android.permission.WRITE_CONTACTS">//写权限
9.
其中android:authorities 是ContentProvider的唯一标识,就像每个人的身份证一样,通过authorities就可以找到对应的数据库,在本例中,如果发起端进行中的URI中包含contacts或者com.android.contacts 就可以找到ContactsProvider2数据库了。
1.1 类图
2, 代码解析
2.1 getContentResolver
在客户端,在增删改查之前,首先都会调用getContentResolver方法,
10. public ContentResolver getContentResolver() {
11. return mContentResolver;
12. }
在ContextImpl的构造函数中,
13. mContentResolver = new ApplicationContentResolver(this, mainThread, user);
可以看到, mContentResolver是一个ApplicationContentResolver对象,并且继承自ApplicationContentResolver,是ContextImpl的一个内部类。这些流程都运行于发起端进程。
2.2 query
14. public final @Nullable Cursor query(final@NonNull Uri uri, @Nullable String[] projection,
15. @Nullable String selection,@Nullable String[] selectionArgs,
16. @Nullable String sortOrder,@Nullable CancellationSignal cancellationSignal) {
17. android.util.SeempLog.record_uri(13,uri);
18. Preconditions.checkNotNull(uri,"uri");
19. IContentProvider unstableProvider =acquireUnstableProvider(uri);
20. if (unstableProvider == null) {
21. return null;
22. }
23. IContentProvider stableProvider = null;
24. Cursor qCursor = null;
25. try {
26. long startTime =SystemClock.uptimeMillis();
27.
28. ICancellationSignalremoteCancellationSignal = null;
29. if (cancellationSignal != null) {
30. cancellationSignal.throwIfCanceled();
31. remoteCancellationSignal = unstableProvider.createCancellationSignal();
32. cancellationSignal.setRemote(remoteCancellationSignal);
33. }
34. try {
35. qCursor =unstableProvider.query(mPackageName, uri, projection,
36. selection,selectionArgs, sortOrder, remoteCancellationSignal);
37. } catch (DeadObjectException e) {
38. // The remote process hasdied... but we only hold an unstable
39. // reference though, so wemight recover!!! Let's try!!!!
40. // This is exciting!!1!!1!!!!1
41. unstableProviderDied(unstableProvider);
42. stableProvider = acquireProvider(uri);
43. if (stableProvider == null) {
44. return null;
45. }
46. qCursor = stableProvider.query(mPackageName, uri, projection,
47. selection,selectionArgs, sortOrder, remoteCancellationSignal);
48. }
49. ···
50. }
51. }
主要分为2个步骤,
1,获取具体的数据库对象。这一过程对于增删改查来说都是通用的
2,对数据库对象进行操作。
2.2.1 acquireProvider
52. public final IContentProvideracquireProvider(Uri uri) {
53. if(!SCHEME_CONTENT.equals(uri.getScheme())) {
54. return null;
55. }
56. finalString auth = uri.getAuthority();
57. if (auth != null) {
58. return acquireProvider(mContext,auth);
59. }
60. return null;
61. }
62. protected IContentProvideracquireProvider(Context context, String auth) {
63. return mMainThread.acquireProvider(context,
64. ContentProvider.getAuthorityWithoutUserId(auth),
65. resolveUserIdFromAuthority(auth),true);
66. }
67.
68. public final IContentProvideracquireProvider(
69. Context c, String auth, int userId,boolean stable) {
70. final IContentProvider provider = acquireExistingProvider(c, auth,userId, stable);
71. if (provider != null) {
72. return provider;
73. }
74. IActivityManager.ContentProviderHolderholder = null;
75. try {
76. holder =ActivityManagerNative.getDefault().getContentProvider(
77. getApplicationThread(),auth, userId, stable);
78. } catch (RemoteException ex) {
79. }
80. if (holder == null) {
81. Slog.e(TAG, "Failed to findprovider info for " + auth);
82. return null;
83. }
84.
85. // Install provider will increment thereference count for us, and break
86. // any ties in the race.
87. holder = installProvider(c, holder, holder.info,
88. true /*noisy*/,holder.noReleaseNeeded, stable);
89. return holder.provider;
90. }
首先调用acquireExistingProvider在mProviderMap表中检测应用是否已经创建了对应的数据库,如果已经创建了(启动)就可以直接使用,否则继续向AMS跨进程请求获取对应的数据库;最后根据对应的数据库调用installProvider方法在本进程中安装获取的数据库。
1 getContentProvider
91. public final ContentProviderHoldergetContentProvider(
92. IApplicationThread caller, Stringname, int userId, boolean stable) {
93. enforceNotIsolatedCaller("getContentProvider");
94. ···
95. return getContentProviderImpl(caller,name,null, stable, userId);
96. }
97. private final ContentProviderHoldergetContentProviderImpl(IApplicationThread caller,
98. String name, IBinder token, booleanstable, int userId) {
99. ContentProviderRecordcpr;
100. ContentProviderConnection conn = null;
101. ProviderInfo cpi = null;
102. cpr = mProviderMap.getProviderByName(name,userId);
103.
104.
105. boolean providerRunning = cpr != null;
106. if (!providerRunning) {
107.
108. ProcessRecord proc = getProcessRecordLocked(
109. cpi.processName, cpr.appInfo.uid, false);
110. try {
111. proc.thread.scheduleInstallProvider(cpi);
112. } catch(RemoteException e) {
113. }
114.
115. proc = startProcessLocked(cpi.processName,
116. cpr.appInfo, false, 0, "content provider",
117. newComponentName(cpi.applicationInfo.packageName,
118. cpi.name), false, false, false);
119. returncpr != null ? cpr.newHolder(conn) :null;
getContentProviderImpl方法的代码比较长,主要逻辑如下:
1,如果目标数据库所在进程已启动,并且数据库已启动,那么直接返回
2, 如果目标数据库所在进程已启动,数据库未启动,那么就跨进程调用目标进程来启动目标数据库。scheduleInstallProvider
3, 如果目标数据库所在进程未启动,那么就启动进程。startProcessLocked
在这里仅分析第二种情况,调用scheduleInstallProvider方法启动目标数据库
完成之后,调用ContentProviderRecord 的newHolder方法构造返回对象,
120.public ContentProviderHolder newHolder(ContentProviderConnectionconn) {
121. ContentProviderHolder holder = new ContentProviderHolder(info);
122. holder.provider = provider;
123. holder.noReleaseNeeded = noReleaseNeeded;
124. holder.connection = conn;
125. return holder;
126. }
ContentProviderHolder实现了Parcelable方法,所以可以进行跨进程通信,数据库代理对象ContentProviderProxy就包含在ContentProviderHolder中。所以最后发起端acquireProvider得到的数据库就是ContentProviderProxy。
127.private ContentProviderHolder(Parcel source){
128. info = ProviderInfo.CREATOR.createFromParcel(source);
129. provider = ContentProviderNative.asInterface(
130. source.readStrongBinder());
131. connection = source.readStrongBinder();
132. noReleaseNeeded = source.readInt() != 0;
133. }
134.static public IContentProviderasInterface(IBinder obj)
135. {
136. if (obj == null) {
137. return null;
138. }
139. IContentProvider in =
140. (IContentProvider)obj.queryLocalInterface(descriptor);
141. if (in != null) {
142. return in;
143. }
144.
145. return new ContentProviderProxy(obj);
146. }
2 启动ContentProvider
首先是跨进程到目标数据库所在进程,然后发送消息跳到主线程来启动目标数据库,这些套路都是一样的,直接看最后的installContentProviders 方法
147.private void installContentProviders(
148. Context context, List<ProviderInfo> providers) {
149. final ArrayList<IActivityManager.ContentProviderHolder> results =
150. new ArrayList<IActivityManager.ContentProviderHolder>();
151.
152. for (ProviderInfo cpi : providers) {
153. if (DEBUG_PROVIDER) {
154. StringBuilder buf = newStringBuilder(128);
155. buf.append("Pub ");
156. buf.append(cpi.authority);
157. buf.append(": ");
158. buf.append(cpi.name);
159. Log.i(TAG, buf.toString());
160. }
161. IActivityManager.ContentProviderHoldercph = installProvider(context, null,cpi,
162. false /*noisy*/, true/*noReleaseNeeded*/, true /*stable*/);
163. if (cph != null) {
164. cph.noReleaseNeeded = true;
165. results.add(cph);
166. }
167. }
168.
169. try {
170. ActivityManagerNative.getDefault().publishContentProviders(
171. getApplicationThread(),results);
172. } catch (RemoteException ex) {
173. }
174. }
这个方法也是2个步骤,首先调用installProvider方法启动数据库,然后跨进程调用AMS的publishContentProviders进行登记注册。
175.public final voidpublishContentProviders(IApplicationThread caller,
176. List<ContentProviderHolder> providers) {
177. if (providers == null) {
178. return;
179. }
180.
181. enforceNotIsolatedCaller("publishContentProviders");
182. synchronized (this) {
183. final ProcessRecord r = getRecordForAppLocked(caller);
184. if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
185. if (r == null) {
186. throw new SecurityException(
187. "Unable to findapp for caller " + caller
188. + " (pid=" +Binder.getCallingPid()
189. + ") when publishingcontent providers");
190. }
191.
192. final long origId = Binder.clearCallingIdentity();
193.
194. final int N = providers.size();
195. for (int i = 0; i < N; i++) {
196. ContentProviderHolder src =providers.get(i);
197. if (src == null || src.info ==null || src.provider == null) {
198. continue;
199. }
200. ContentProviderRecord dst =r.pubProviders.get(src.info.name);
201. if (DEBUG_MU) Slog.v(TAG_MU,"ContentProviderRecord uid = " + dst.uid);
202. if (dst != null) {
203. ComponentName comp = newComponentName(dst.info.packageName, dst.info.name);
204. mProviderMap.putProviderByClass(comp, dst);
205. String names[] =dst.info.authority.split(";");
206. for (int j = 0; j <names.length; j++) {
207. mProviderMap.putProviderByName(names[j], dst);
208. }
209.
210. int launchingCount =mLaunchingProviders.size();
211. int j;
212. booleanwasInLaunchingProviders = false;
213. for (j = 0; j <launchingCount; j++) {
214. if(mLaunchingProviders.get(j) == dst) {
215. mLaunchingProviders.remove(j);
216. wasInLaunchingProviders = true;
217. j--;
218. launchingCount--;
219. }
220. }
221. if(wasInLaunchingProviders) {
222. mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
223. }
224. synchronized (dst) {
225. dst.provider =src.provider;
226. dst.proc = r;
227. dst.notifyAll();
228. }
229. updateOomAdjLocked(r);
230. maybeUpdateProviderUsageStatsLocked(r,src.info.packageName,
231. src.info.authority);
232. }
233. }
234.
235. Binder.restoreCallingIdentity(origId);
236. }
237. }
看到了吧, mProviderMap是一个ProviderMap对象,和其他组件一样,负责管理所有的数据库。getContentProviderImpl 方法查询数据库的时候就是在mProviderMap中查询。
2.2.2 installProvider
238.privateIActivityManager.ContentProviderHolder installProvider(Context context,
239. IActivityManager.ContentProviderHolder holder, ProviderInfo info,
240. boolean noisy, boolean noReleaseNeeded, boolean stable) {
241. ContentProvider localProvider = null;
242. IContentProvider provider;
243. if (holder == null || holder.provider == null) { // 目标进程
244. if (DEBUG_PROVIDER || noisy) {
245. Slog.d(TAG, "Loadingprovider " + info.authority + ": "
246. + info.name);
247. }
248. Context c = null;
249. ApplicationInfo ai = info.applicationInfo;
250. if (context.getPackageName().equals(ai.packageName)) {
251. c = context;
252. } else if (mInitialApplication != null &&
253. mInitialApplication.getPackageName().equals(ai.packageName)) {
254. c = mInitialApplication;
255. } else {
256. try {
257. c =context.createPackageContext(ai.packageName,
258. Context.CONTEXT_INCLUDE_CODE);
259. } catch(PackageManager.NameNotFoundException e) {
260. // Ignore
261. }
262. }
263. if (c == null) {
264. Slog.w(TAG, "Unable to getcontext for package " +
265. ai.packageName +
266. " while loadingcontent provider " +
267. info.name);
268. return null;
269. }
270. try {
271. final java.lang.ClassLoader cl= c.getClassLoader();
272. localProvider =(ContentProvider)cl.
273. loadClass(info.name).newInstance();
274. provider = localProvider.getIContentProvider();
275. if (provider == null) {
276. Slog.e(TAG, "Failed toinstantiate class " +
277. info.name + "from sourceDir " +
278. info.applicationInfo.sourceDir);
279. return null;
280. }
281. if (DEBUG_PROVIDER) Slog.v(
282. TAG, "Instantiatinglocal provider " + info.name);
283. // XXX Need to create thecorrect context for this provider.
284. localProvider.attachInfo(c,info);
285. } catch (java.lang.Exception e) {
286. if(!mInstrumentation.onException(null, e)) {
287. throw new RuntimeException(
288. "Unable to getprovider " + info.name
289. + ": " +e.toString(), e);
290. }
291. return null;
292. }
293. } else {
294. provider = holder.provider; // 发起进程
295.
296. }
297.
298. IActivityManager.ContentProviderHolder retHolder;
299.
300. synchronized (mProviderMap) {
301. if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
302. + " / " +info.name);
303. IBinder jBinder = provider.asBinder();
304. if (localProvider != null) { // 目标进程
305. ComponentName cname = newComponentName(info.packageName, info.name);
306. ProviderClientRecord pr =mLocalProvidersByName.get(cname);
307. if (pr != null) {
308. if (DEBUG_PROVIDER) {
309. Slog.v(TAG,"installProvider: lost the race, "
310. + "usingexisting local provider");
311. }
312. provider = pr.mProvider;
313. } else {
314. holder = newIActivityManager.ContentProviderHolder(info);
315. holder.provider = provider;
316. holder.noReleaseNeeded =true;
317. pr = installProviderAuthoritiesLocked(provider,localProvider, holder);
318. mLocalProviders.put(jBinder, pr);
319. mLocalProvidersByName.put(cname, pr);
320. }
321. retHolder = pr.mHolder;
322. } else { // 发起进程
323. ProviderRefCount prc =mProviderRefCountMap.get(jBinder);
324. if (prc != null) {
325. if (DEBUG_PROVIDER) {
326. Slog.v(TAG,"installProvider: lost the race, updating ref count");
327. }
328. // We need to transfer ournew reference to the existing
329. // ref count, releasing theold one... but only if
330. // release is needed (thatis, it is not running in the
331. // system process).
332. if (!noReleaseNeeded) {
333. incProviderRefLocked(prc, stable);
334. try {
335. ActivityManagerNative.getDefault().removeContentProvider(
336. holder.connection,stable);
337. } catch(RemoteException e) {
338. //do nothingcontent provider object is dead any way
339. }
340. }
341. } else {
342. ProviderClientRecord client= installProviderAuthoritiesLocked(
343. provider,localProvider, holder);
344. if (noReleaseNeeded) {
345. prc = new ProviderRefCount(holder,client, 1000, 1000);
346. } else {
347. prc = stable
348. ? newProviderRefCount(holder, client, 1, 0)
349. : newProviderRefCount(holder, client, 0, 1);
350. }
351. mProviderRefCountMap.put(jBinder, prc);
352. }
353. retHolder = prc.holder;
354. }
355. }
356.
357. return retHolder;
358. }
359.
首先看目标端进程: getIContentProvider方法
360.public IContentProvider getIContentProvider(){
361. return mTransport;
362. }
获取的是一个Transport类的对象, Transport继承自ContentProviderNative,并且是ContentProviderNative的内部类。
- private Transport mTransport = new Transport();
发起端:
由上一小节分析可知,发起端的数据库对象是ContentProviderProxy。
由此,终于将发起端进程和目标进程通过IContentProvider连接起来了,经典的示意图如下:
发起端的增删改查等操作都会通过ContentProviderProxy跨进程调用到Transport中对应的增删改查等方法。
3, 增删改查
增删改查总体流程几乎一样,就以查询来说明流程,
开始,调用到ContentResolver的query方法, ContentResolver只是一层皮,
然后通过ContentProviderProxy进行跨进程通信,从发起端跳到目标端,
最后在目标端对应的ContentProvider中完成操作。
代码就不贴上去了,前面铺垫太多了,终于柳暗花明 。
- ContentProvider 源码解析
- 【Android源码系列】ContentProvider启动源码解析
- Android ContentProvider启动流程源码解析(8.0)
- Android四大组件之ContentProvider 全面解析,ContentResolver源码解析如何调用其它APP的ContentProvider
- ContentProvider解析
- ContentResolver与ContentProvider的联系之源码解析
- ContentProvider源码分析
- ContentProvider源码分析
- ContentProvider源码分析
- ContentProvider源码分析
- Android ContentProvider源码分析
- ContentProvider源码分析
- ContentProvider源码分析
- android ContentProvider解析使用
- 多媒体ContentProvider详细解析
- contentprovider解析2
- ContentProvider解析3
- Android ContentProvider全面解析
- 记录一下python的数据结构 - array
- ACM图论算法—邮递员投递问题
- Android studio文件结构
- 文章概要
- 关于Android 6.0二维码生成和识别的简单理解和常见问题
- ContentProvider 源码解析
- mysql安装后没有弹出配置向导
- 单链公共结点问题
- Redis学习随笔
- HDU 5925 Coconuts 【离散化+BFS】 (2016CCPC东北地区大学生程序设计竞赛)
- python-web
- Timer的缺陷 用ScheduledExecutorService替代
- Struts2+Spring+Hibernate的工作流程及原理(整理的)
- PS CC 2015 GIF动画制作 和 导出方法,图像序列