分类:C#、Android、VS2015;
创建日期:2016-03-08
一、简介
ContentProvider:内容提供程序。
Android的ContentProvider与.NET框架的EF(Entity Framework)非常类似。在EF中,每个类表示数据库中的一个表,类中的每个属性对应表的字段,类的每个实例表示数据库表的一行记录。同样,在Android中,每个ContentProvider类的实例表示数据表的一行记录,ContentProvider实例集合中的每一项表示数据表中的一列。
另外,Entity Framework的数据源不一定是数据库,也可以是其他类型的数据。同样,Android的ContentProvider可访问的数据源也不一定是数据库,也一样可以是其他类型的数据。而且EF和ContentProvider也都提供了对数据源的CRUD(Create、Read、Update、Delete)操作。
可将ContentProvider理解为在不同的进程之间实现数据交互或数据共享的工具。换言之,利用ContentProviders,可访问由其他应用程序公开的数据,比如访问Android的系统数据(如联系人、媒体和日历信息)、SQLite数据库、文件等。
1、安卓内置的内容提供程序(Built-In Providers)
安卓内置的所有Provide都在Android.Provider命名空间下,这些类有:
- Android.Provider.Browser类–浏览书签的历史记录(此操作需要READ_HISTORY_BOOKMARKS或WRITE_HISTORY_BOOKMARKS权限)。
- Android.Provider.CallLog类 –通话记录。查看最近拨出或收到的电话。
- Android.Provider.ContactsContract类 –联系人。查看用户的联系人名单,包括姓名、电话、照片等信息。
- Android.Provider.MediaStore类 –媒体存储。查看设备上用户存储的媒体文件,包括音频、图像、视频等。
- Android.Provider.Settings类 –系统设置和首选项。
- Android.Provider.UserDictionary类 –用于预测文本输入的用户定义的字典。
- Android.Provider.VoicemailContract类 –语音信箱中的联系人及其历史记录。
安卓内置的所有Provide实际上都是通过ContentProvider来操作的,只是它封装以后你看不到ContentProvider了,换言之,你直接使用它对ContentProvider进一步封装后的这些类就行了,这样用起来更方便些。
2、如何使用这些内置的提供程序
使用ContentProvider的首选方式是利用LoaderManager中的CursorLoader类得到ContentProvider的实例。,这种方式实际上是通过先关闭主线程然后再利用数据绑定来加载数据的。得到ContentProviders的实例以后,就可以通过CursorAdapters将ContentProviders加载的数据显示出来。
前面刚刚说过,由于Android对ContentProvider做了进一步的封装,因此在代码中访问Android系统通过ContentProvider公开的数据时,它实际是使用ContentProvider类来处理它的,只是你看不到它是这样做的而已。换言之,对于开发人员来说,它对其封装以后,要求你先通过Uri创建一个游标(cursor),然后再利用这个cursor对象访问它公开的数据。
下面以android.provider.ContactsContract类为例说明Uri的含义和用法,Built-In Providers中其他类的用法与此相似。
Uri实际上就是把DNS颠倒过来写的一个字符串,例如:
com.android.contacts/data
为了不让开发人员耗费精力去理解和记住这个原始字符串,Android的Contacts(联系人)提供程序在android.provider.ContactsContract类中又以常量的形式公开了这些元数据,你只需要通过这些常量,即可得到ContentProvider的Uri、可查询的表名以及列名。
有3种通过Uri创建游标(cursor)的方式:
- CursorLoader().LoadInBackground() –这是首选方式,是从Android 3.0 (API Level 11)开始引入的处理办法。由于CursorLoader是通过后台线程查询ContentResolver的,因此该方法不会引起界面阻塞,而且不用时还能自动关闭它。另外,如果你希望在比Android 3.0更低的版本中使用这个类,可通过v4兼容库(v4 compatibility library)来访问。
- ContentResolver.Query() –使用这种办法得到cursor对象后,不用时必须调用cursor.Close()关闭它,否则就会引起内存泄漏。
- ManagedQuery() –这是Android 2.3 (API Level 10) 及更早版本中提供的方法。注意该方法在 Android 3.0 (API 级别 11) 中就已经被标记为已过时,因此不要再使用它。
在Android公开的这些方法中,不论使用哪种方式,都需要提供以下几个基本参数:
- Uri –内容的完全限定名称。
- Projection – 投影。其作用是为cursor指定选择的一列或多列。
- Selection – 其作用类似于SQL的WHERE子句。
- SelectionArgs – 用参数替换WHERE子句中所选的内容。
- SortOrder – 指定排序的列。
再次强调一下,CursorLoader是使用ContentProvider的首选方式。
3、自定义ContentProvider
除了上面介绍的Android内置的Provider外,你还可以创建自定义的Provider。数据库一章中已经介绍过其用法了,这里不再重复。
二、例19-3—用CursorLoader读取和更新通讯录
这一节仍以读取和更新通讯录为例,演示CursorLoader的基本用法。该例子是在【12.5利用Intent读取和更新通讯录】例子的基础上改进的,在这个该例子中,除了像例12-4那样显示出联系人外,如果通讯录中你输入了联系人的照片,还能把对应的照片显示出来。如果你没有在通讯录中输入照片,就用默认的照片来代替。
1、运行截图
由于是例子,所以截图中的通讯录照片是随便选的一个图。
2、设计步骤
(1)该例子需要的权限
由于前面章节的例子已经添加过这些权限,所以不需要再添加了。如果你单独创建一个项目,必须添加下面的权限。
<uses-permission android:name="android.permission.WRITE_PROFILE" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
(2)添加ch1903_contact_picture.png
在drawable文件夹下添加该文件。用于找不到联系人照片时显示的替换照片。
(3)添加ch1903ContactListItem.xml
在layout文件夹下添加该文件。
<?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/ch1903_ContactImage" android:layout_width="50dp" android:layout_height="50dp" android:layout_margin="5dp" /> <TextView android:id="@+id/ch1903_ContactName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_marginLeft="5dp" /> </LinearLayout>
(4)添加ch1903Main.axml
在layout文件夹下添加该文件。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:id="@+id/ch1903_ContactsListView" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
(5)添加ch1903MainActivity.cs
在SrcDemos文件夹下添加该文件。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; using Android.Provider; using Android.Database; namespace MyDemos.SrcDemos { [IntentFilter(new[] { Intent.ActionMain }, Categories = new[] { ch.MyDemosCategory })] [Activity(Label = "【例19-3】ContentProvider基本用法")] public class ch1903MainActivity : Activity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.ch1903Main); var contactsAdapter = new ContactsAdapter(this); var contactsListView = FindViewById<ListView>(Resource.Id.ch1903_ContactsListView); contactsListView.Adapter = contactsAdapter; } } public class ContactsAdapter : BaseAdapter<ch1903Contact> { List<ch1903Contact> contactList; Activity activity; public override int Count { get { return contactList.Count; } } public override ch1903Contact this[int position] { get { return contactList[position]; } } public ContactsAdapter(Activity activity) { this.activity = activity; FillContacts(); } public override long GetItemId(int position) { return contactList[position].Id; } public override View GetView(int position, View convertView, ViewGroup parent) { var view = convertView ?? activity.LayoutInflater.Inflate(Resource.Layout.ch1903ContactListItem, parent, false); var contactName = view.FindViewById<TextView>(Resource.Id.ch1903_ContactName); var contactImage = view.FindViewById<ImageView>(Resource.Id.ch1903_ContactImage); contactName.Text = contactList[position].DisplayName; if (contactList[position].PhotoId == null) { contactImage = view.FindViewById<ImageView>(Resource.Id.ch1903_ContactImage); contactImage.SetImageResource(Resource.Drawable.ch1903_contact_picture); } else { var contactUri = ContentUris.WithAppendedId(ContactsContract.Contacts.ContentUri, contactList[position].Id); var contactPhotoUri = Android.Net.Uri.WithAppendedPath(contactUri, ContactsContract.Contacts.Photo.ContentDirectory); contactImage.SetImageURI(contactPhotoUri); } return view; } private void FillContacts() { var uri = ContactsContract.Contacts.ContentUri; string[] projection = { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName, ContactsContract.Contacts.InterfaceConsts.PhotoId }; // 下面这条语句已过时,不要使用它 //var cursor = activity.ManagedQuery (uri, projection, null, null, null); // 如果用下面这条语句,不用时必须调用cursor.Close()关闭它 //var cursor = activity.ContentResolver.Query(uri, projection, null, null, null); // 下面是建议的用法 var loader = new CursorLoader(activity, uri, projection, null, null, null); var cursor = (ICursor)loader.LoadInBackground(); contactList = new List<ch1903Contact>(); if (cursor.MoveToFirst()) { do { contactList.Add(new ch1903Contact { Id = cursor.GetLong(cursor.GetColumnIndex(projection[0])), DisplayName = cursor.GetString(cursor.GetColumnIndex(projection[1])), PhotoId = cursor.GetString(cursor.GetColumnIndex(projection[2])) }); } while (cursor.MoveToNext()); } } } public class ch1903Contact { public long Id { get; set; } public string DisplayName { get; set; } public string PhotoId { get; set; } } }