分類:C#、Android、VS2015; 創建日期:2016-03-08 一、簡介 ContentProvider:內容提供程式。 Android的ContentProvider與.NET框架的EF(Entity Framework)非常類似。在EF中,每個類表示資料庫中的一個表,類中的每個屬性對...
分類: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; } } }