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

0 0