2018-08-02
《Android开发艺术探索》9.5章
使用ContentResolver读取联系人
private ArrayList<HashMap<String, String>> readContact() {
String NUM = ContactsContract.CommonDataKinds.Phone.NUMBER;
String NAME = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME;
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(uri,new String[]{
NUM,NAME},null,null,null);
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex(NAME));
String phone = cursor.getString(cursor.getColumnIndex(NUM));
HashMap<String,String> contact = new HashMap<>();
contact.put("name",name);
contact.put("phone",phone);
list.add(contact);
}
return list;
工作过程
ContentProvider
是一种内容共享型组件,它通过Binder
向其他组件乃至其他应用提供数据。当ContentProvider
所在的进程启动时,ContentProvider
会同时启动并被发布到AMS中。需要注意的是,这个时候ContentProvider
的onCreate
要先于Application
的onCreate
而执行。
当一个应用启动时,入口方法为ActivityThread
的main
方法,main
方法是一个静态方法,在main
方法中会创建ActivityThread
的实例并创建主线程的消息队列,然后在ActivityThread
的attach
方法中会远程调用AMS
的attachApplication
方法并将ApplicationThread
对象提供给AMS
。ApplicationThread
是一个Binder
对象,它的Binder
接口是IApplicationThread
,它主要用于ActivityThread
和AMS
之间的通信,这一点在前面多次提到。在AMS
的attachApplication
方法中,会调用ApplicationThread
的bindApplication
方法,注意这个过程同样是跨进程完成的,bindApplication
的逻辑会经过ActivityThread
中的mH Handler
切换到ActivityThread
中去执行,具体的方法是handleBindApplication
。在handleBindApplication
方法中,ActivityThread
会创建Application
对象并加载ContentProvider
。需要注意的是,ActivityThread
会先加载ContentProvider
,然后再调用Application
的onCreate
方法。
这就是ContentProvider
的启动过程,ContentProvider
启动后,外界就可以通过它所提供的增删改查这四个接口来操作ContentProvider
中的数据源,即insert、delete、update和query四个方法。这四个方法都是通过Binder
来调用的,外界无法直接访问ContentProvider
,它只能通过AMS根据Uri来获取对应的ContentProvider
的Binder
接口IConentProvider
,然后再通过IConentProvider
来访问ContentProvider
中的数据源。
一般来说,ContentProvide
r都应该是单实例的。ContentProvider
到底是不是单实例,这是由它的android:multiprocess
属性来决定的,当android:multiprocess
为false
时,ContentProvider
是单实例,这也是默认值;当android:multiprocess
为true
时,ContentProvider
为多实例,这个时候在每个调用者的进程中都存在一个ContentProvider
对象。
访问ContentProvide
r需要通过ContentResolver
,ContentResolver
是一个抽象类,通过Context
的getContentResolver
方法获取的实际上是ApplicationContentResolver
对象,ApplicationContentResolver
类继承了ContentResolver
并实现了ContentResolver
中的抽象方法。当ContentProvider
所在的进程未启动时,第一次访问它时就会触发ContentProvider
的创建,当然这也伴随着ContentProvider
所在进程的启动。通过ContentProvider
的四个方法的任何一个都可以触发ContentProvider
的启动过程,这里选择query
方法。ContentProvider
的query
方法中,首先会获取IContentProvider
对象,不管是通过acquireUnstableProvider
方法还是直接通过acquireProvider
方法,它们的本质都是一样的,最终都是通过acquireProvider
方法来获取ContentProvider
。下面是ApplicationContentResolver
的acquireProvider
方法的具体实现:
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
ApplicationContentResolver
的acquireProvider
方法并没有处理任何逻辑,它直接调用了ActivityThread
的acquireProvider
方法,ActivityThread
的acquireProvider
方法的源码如下:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
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.
ContentProviderHolder holder = null;
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
</