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返回回去