Android进程间通讯——ContentProvider
来源:互联网 发布:2333网络语是什么意思 编辑:程序博客网 时间:2024/04/30 02:14
ContentProvider是Android四大组件之一,可以提供数据给应用程序。ContentProvider可以提供数据在进程之间共享。ContentProvider能跨进程通信我是知道的。但是我就在之前的几天我还在认为ContentProvider只是为数据库服务的,还是和在和朋友的聊天中忽然提到了这个问题,我才知道ContentProvider不仅仅是查找数据库的数据。关于ContentProvider的文章猫神的这篇http://bbs.51cto.com/thread-1068382-1.html我觉的是写的最好的(其他的文章也是屌屌的)
1. 在应用程序A里面怎么跨进程拿到ContentProvider的对象呢?
a.ContentResolver.query是怎么实现的
*/ 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); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable // reference though, so we might recover!!! Let's try!!!! // This is exciting!!1!!1!!!!1 unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } if (qCursor == null) { return null; } // Force query execution. Might fail and throw a runtime exception here. qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); // Wrap the cursor object into CursorWrapperInner object. CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, stableProvider != null ? stableProvider : acquireProvider(uri)); stableProvider = null; qCursor = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { if (qCursor != null) { qCursor.close(); } if (cancellationSignal != null) { cancellationSignal.setRemote(null); } if (unstableProvider != null) { releaseUnstableProvider(unstableProvider); } if (stableProvider != null) { releaseProvider(stableProvider); } } }代码的第一行就是IContentProvider unstableProvider = acquireUnstableProvider(uri);
b.然后acquireUnstableProvider(uri)方法是这样的:
/** * Returns the content provider for the given content URI. * * @param uri The URI to a content provider * @return The ContentProvider for the given URI, or null if no content provider is found. * @hide */ 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; }
在这段代码里面,关键地方在这里 String auth = uri.getAuthority();这里取得的auth就是我们在AndroidManifes.xml文件中配置的ContentProvider的android:authorities的值.所以,这个android:authorities属性配置的就是该ContentProvider的名字,是它在Android系统中的名字,我们是通过这个名字去找对应的ContentProvider对象的。
c. ok..既然现在我们拿到ContentProvider的名字了,我们就来看看acquireUnstableProvider方法怎么通过名字来找到ContentProvider对象的。
这个acquireUnstableProvider方法会调用到ActivityThread的acquireProvider方法,这个方法的实现是:
public final IContentProvider acquireProvider(Context c, String name) { IContentProvider provider = acquireExistingProvider(c, name); 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(), name); } catch (RemoteException ex) { } if (holder == null) { Slog.e(TAG, "Failed to find provider info for " + name); return null; } // Install provider will increment the reference count for us, and break // any ties in the race. provider = installProvider(c, holder.provider, holder.info, true /*noisy*/, holder.noReleaseNeeded); if (holder.provider != null && provider != holder.provider) { if (localLOGV) { Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous " + "reference to the content provider"); } try { ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), name); } catch (RemoteException ex) { } } return provider; }
这里就是查找ContentProvider实现的精髓所在了。。
首先,它去找acquireExistingProvider方法,这个方法其实就是根据我们传过来的名称在一个map里面找,如:
ProviderClientRecord pr = mProviderMap.get(name);
由于我们的ActivityThread和我们的应用程序还在一个进程里面,所以这个步骤我们可以理解为:在本地缓存中寻找ContentProvider对象
ok...在本地找了之后,如果找到了,就直接返回。
if (provider != null) {
return provider;
}
如果没有找到,就继续往下面走:
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name, stable);
这个方法就是调用到ActivityManagerService的getContentProvider方法去寻找ContentProvider.这里是一个跨进程调用,因为ActivityThread和ActivityManagerService不在一个进程里面。
而ActivityManagerService会把所有的ContentProvider都实例化出来,并且缓存在一个map里面,所以我们就可以通过
holder = ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), name);从ActivityManagerService远程得到一个ContentProvider对象。那么这一步,我们可以理解为:从远程服务中寻找ContentProvider对象,接下来
holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider;调用installProvider方法,这个方法其实就是往本地的ContentProvider map缓存中添加一条缓存记录。
ok...那么这整个过程,我们就可以理解为这样:
1. 从ActivityThread本地缓存中找,如果找到,一切ok,就返回。如果没有,进行第二步。
2. 在ActivityManagerService里面,会取判断是不是允许客户端进程加载ContentProvider,如果允许,就返回了。 如果不允许,就创建ContentProvider进程,然后初始化里面所有的ContentProvider对象。还有一个等待机制,等待ContentProvider进程 初始化完毕,然后才返回。
3. 如果允许客户端加载ContentProvider对象,就用java反射把ContentProvider对象实例化出来。否则就用从ActivityManagerService 返回的ContentProvider对象。然后把这个对象缓存起来,以便下次查询。下次查询的话,直接从第1步就返回了。
4. ContentProvider对象会在它所在的进程启动的时候初始化,比如,你的ContentProvider进程里面有个Activity,这个Activity开机之后 就启动了,那么,它就会顺便把这个进程里面所有要启动的ContentProvider一起启动,装载完毕。
如果想看ActivityManagerService源码的:http://androidxref.com/4.4.4_r1/xref/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
————————————————————————————————————————————————————————————————————————————
上面的这些都是猫神写的东西,只是搬过来了解一下ContentProvider的工作流程。至于为什么还得借用猫神的一句话:因为子曾经曰过:知其然,知其所以然。下面是一些ContentProvider跨进程的一些东西。
基本上为四个关键步骤:
1. acquireUnstableProvider,得到ContentProvider的实例,
2. unstableProvider.query(......), unstableProvider实际上是IContentProvider实例,IContentProvider是进行IPC通讯的接口,这个query实际上调用的是目标ContentProvider中的query方法然,在真正调用目标ContentProvider的query方法之前,还需要经过enforceReadPermission方法,这一步主要是看下该ContentProvier有没有export,读写权限等等(enforceReadPermission方法只判断读权限)。随后执行query方法,并且返回一个cursor对象。
IPC通信需要两端,对于我们的例子,这两段分别是ContentProviderProxy和ContentProviderNative,首先会执行ContentProviderProxy的query方法,然后通过binder通信执行ContentProviderNative的onTransact方法。ContentProviderProxy的query方法有一下五个主要步骤:
a. new一个BulkCursorToCursorAdaptor对象——adaptor
b. 填充data用于binder通信
c. 调用mRemote.transact,这是一个阻塞的过程,直到ContentProviderNative的onTransact方法返回
d. 读取reply数据,new一个BulkCursorDescriptor并以此初始化adaptor
e. return adaptor
ContentProviderNative的onTransact会调用ContentProvider的query方法,并根据query返回的cursor初始化一个CursorToBulkCursorAdaptor对象,最终将BulkCursorDescriptor对象写入reply中。
3. qCursor.getCount();getCount会调用SQLiteCursor的fillWindow,制执行数据库query
4. return new CursorWrapperInner(......)
ContentProvider跨进程主要指的就是上面的第二步,QLiteCursor的是数据库的大概没人不知道他是可以跨进程的吧,这里主要说的是MatrixCursor
public class SuggestionProvider extends ContentProvider {private static final String TAG = "SuggestionProvider"; private static final int SEARCH_SUGGESTIONS = 1; // 初始化 private static final UriMatcher sURLMatcher = new UriMatcher( UriMatcher.NO_MATCH); //注册需要的Uri: static { sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGESTIONS); sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGESTIONS); } private static final String[] COLUMNS = new String[] { "_id", SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_INTENT_ACTION, SearchManager.SUGGEST_COLUMN_QUERY }; public SuggestionProvider() { } @Override public boolean onCreate() { return true; } @Override public Cursor query(Uri url, String[] projectionIn, String selection, String[] selectionArgs, String sort) { //与已经注册的Uri进行匹配: int match = sURLMatcher.match(url); switch (match) { case SEARCH_SUGGESTIONS: String query = url.getLastPathSegment(); MatrixCursor cursor = new MatrixCursor(COLUMNS); String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" }; for (String suffix : suffixes) { addRow(cursor, query + suffix); } return cursor; default: throw new IllegalArgumentException("Unknown URL: " + url); } } private void addRow(MatrixCursor cursor, String string) { long id = cursor.getCount(); cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string); } @Override public String getType(Uri url) { int match = sURLMatcher.match(url); switch (match) { case SEARCH_SUGGESTIONS: return SearchManager.SUGGEST_MIME_TYPE; default: throw new IllegalArgumentException("Unknown URL: " + url); } } @Override public int update(Uri url, ContentValues values, String where, String[] whereArgs) { throw new UnsupportedOperationException("update not supported"); } @Override public Uri insert(Uri url, ContentValues initialValues) { throw new UnsupportedOperationException("insert not supported"); } @Override public int delete(Uri url, String where, String[] whereArgs) { throw new UnsupportedOperationException("delete not supported"); }}在query的时候创建一个cursor返回回去
- Android进程间通讯——ContentProvider
- Android进程间通讯——AIDL,Messenger(信使),ContentProvider,Clipboard(剪切板)
- Android进程间通讯——AIDL
- Android进程间通讯——多进程共用SharedPreferences
- Android进程间通讯——多进程共用SharedPreferences
- Android进程间通讯——多进程共用SharedPreferences
- 进程间通讯[android]
- Android进程间通讯
- Android进程间通讯
- 进程间通讯—管道
- Android进程间通讯——Messager(信使)
- Android进程间通讯——Clipboard(剪切板)
- android—binder进程间通讯流程分析
- Android之进程通讯——广播
- Android之进程通讯——广播
- android aidl 进程间通讯
- android进程间通讯方式
- android IPC 进程间通讯
- 使用VBA实现Excel合并相同内容的相邻单元格
- iOS保存app内容到手机桌面
- 黑马程序员——【C语言】原码、反码和补码概述
- 摩托罗拉defy.apk+安装recovery和刷机的教程
- easyui自带的日历功能和生日年月日的三级联动
- Android进程间通讯——ContentProvider
- ios-day10-06(UIApplication的介绍。UIApplication的常用属性、使用UIApplication控制和管理状态栏)
- Netty5入门(3)
- a+b问题(1000)
- C++ 指针详解(2)
- 郑州社保查询路径
- 【Android问题】解决 Android SDK下载和更新失败“Connection to https://dl-ssl.google.com refused”的问题
- 归并排序
- C++检测空字符串