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 类图

 ContentResolver相关类图
 ContentProvider相关类图

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的内部类。

  1. private Transport mTransport = new Transport();

发起端:

由上一小节分析可知,发起端的数据库对象是ContentProviderProxy。

由此,终于将发起端进程和目标进程通过IContentProvider连接起来了,经典的示意图如下:


发起端的增删改查等操作都会通过ContentProviderProxy跨进程调用到Transport中对应的增删改查等方法。

 

 

3, 增删改查


增删改查总体流程几乎一样,就以查询来说明流程,

开始,调用到ContentResolver的query方法, ContentResolver只是一层皮,

然后通过ContentProviderProxy进行跨进程通信,从发起端跳到目标端,

最后在目标端对应的ContentProvider中完成操作。

代码就不贴上去了,前面铺垫太多了,终于柳暗花明 。

1 0
原创粉丝点击