Xamarin.Android之ContentProvider


一、前言

掌握了如何使用SQLiteOpenHelper之后,我們就可以進行下一步的學習。本章我們將會學習如何使用ContentProvider來將數據庫方面的操作封裝起來,同時它還可以供其他應用訪問並操作數據庫。

 

二、概念

首先我們不會急於寫代碼,而是要搞懂如何利用ContentProvider對數據庫進行操作,因為我們不會直接操作數據庫對象,而是通過URI來操作數據庫。這就好比你要獲取User表的全部內容,那么這個URI就是content://base/user其中base是自己命名的,最好是能夠唯一。因為我們需要依靠這個區分數據庫,然后就是user是用來區分操作的是哪個表,當然你也可以不用命名為user可以是其他的名稱,最終反正要依靠代碼去判斷的。這樣我們就可以避免在活動中直接對數據庫對象操作,也方便對數據庫進行統一的維護。

 

三、實際操作

 

1、搭建基本框架

新建一個LocationContentProvider類,並且繼承自ContentProvider,還要重寫該類的OnCreateDeleteGetTypeInsertQueryUpdate方法。

 

2、設計數據庫以及URI

下面是筆者設計好的結構:

 1 public static string PROVIDER_NAME = "xamarin-cn.location";
 2 
 3 private const string DATABASE_NAME = "location";
 4 private const int DATABASE_VERSION = 1;
 5 
 6 private const string USERTABLE_NAME = "tuser";
 7 private const int USER = 1;
 8 private const int USER_ID = 2;
 9 private static IDictionary<string, string> userProjectionMap;
10 public class UserTable
11 {
12     public static Android.Net.Uri CONTENT_URI = Android.Net.Uri.Parse("content://" + PROVIDER_NAME + "/user");
13     public const string _ID = "_id";
14     public const string UserName = "username";
15     public const string UserPwd = "userpwd";
16     public const string Age = "age";
17 }

 其中 PROVIDER_NAME 是URI的基址,DATABASE_NAMEDATABASE_VERSION是數據庫的命名的和版本號,下面就是tuser表的信息,分別是表名、URI標識1、URI標識2、表結構鍵值對。UserTable類中的就是該表公開的屬性,其中包含表的字段以及查詢該表的URI路徑。我們可以看到該表的URI路徑為content://xamarin-cn.location/user

 

3、初始化UriMatcher

我們需要通過UriMatcher這個類來判斷URI操作的是哪個數據庫的哪個表,這樣就不需要我們自己通過字符串進行判斷,具體的初始化可以見如下代碼:

 1         private static UriMatcher uriMatcher;
 2         static LocationContentProvider()
 3         {
 4             userProjectionMap = new Dictionary<string, string>();
 5             userProjectionMap.Add(UserTable._ID, UserTable._ID);
 6             userProjectionMap.Add(UserTable.UserName, UserTable.UserName);
 7             userProjectionMap.Add(UserTable.UserPwd, UserTable.UserPwd);
 8             userProjectionMap.Add(UserTable.Age, UserTable.Age);
 9             uriMatcher = new UriMatcher(UriMatcher.NoMatch);
10             uriMatcher.AddURI(PROVIDER_NAME, "user", USER);
11             uriMatcher.AddURI(PROVIDER_NAME, "user/#", USER_ID);
12         }

 這里我們通過UriMatcherAddURI方法添加的不同類型URI,其中有針對整張表的,還有針對表中某條數據的。

 

4、建立數據庫

這里我就不一一介紹了直接放出代碼:

 1         private LocationSqliteOpenHelper dbHelper;
 2         class LocationSqliteOpenHelper : SQLiteOpenHelper
 3         {
 4             public LocationSqliteOpenHelper(Context context)
 5                 : base(context, DATABASE_NAME, null, DATABASE_VERSION)
 6             {
 7             }
 8 
 9             public override void OnCreate(SQLiteDatabase db)
10             {
11                 StringBuilder strSql = new StringBuilder();
12                 strSql.AppendFormat("CREATE TABLE {0} (", USERTABLE_NAME);
13                 strSql.AppendFormat("{0} INTEGER PRIMARY KEY NOT NULL,", UserTable._ID);
14                 strSql.AppendFormat("{0} TEXT NOT NULL,", UserTable.UserName);
15                 strSql.AppendFormat("{0} TEXT NOT NULL,", UserTable.UserPwd);
16                 strSql.AppendFormat("{0} INTEGER NOT NULL)", UserTable.Age);
17                 db.ExecSQL(strSql.ToString());
18             }
19 
20             public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
21             {
22                 db.ExecSQL("DROP TABLE IF EXISTS " + USERTABLE_NAME);
23                 OnCreate(db);
24             }
25         }

 

 

5.初始化ContentProvider

首先我們需要在OnCreate方法中將dbHelper初始化:

1 public override bool OnCreate()
2 {
3    dbHelper = new LocationSqliteOpenHelper(Context);
4    return true;
5 }

 

 

6.完善Query方法

為了能夠方便的組織查詢語句這里筆者使用了SQLiteQueryBuilder對象來組織,下面就是查詢的代碼:

 1         public override Android.Database.ICursor Query(Android.Net.Uri uri, string[] projection, string selection, string[] selectionArgs, string sortOrder)
 2         {
 3             SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
 4             qb.Tables = USERTABLE_NAME;
 5             //根據uri判斷查詢的是某條數據還是針對整個表,從而決定條件語句
 6             switch (uriMatcher.Match(uri))
 7             {
 8                 case USER:
 9                     {
10                         //設置需要獲取的字段
11                         //userProjectionMap默認包含的所有字段
12                         qb.SetProjectionMap(userProjectionMap);
13                     }
14                     break;
15                 case USER_ID:
16                     {
17                         qb.SetProjectionMap(userProjectionMap);
18                         //拼接條件語句
19                         //其中uri.PathSegments.ElementAt(1) 將會獲取第二個片段,
20                         //就是第二個“/”后台的內容,如果后面還存在“/”則獲取他們之間的內容
21                         qb.AppendWhere(UserTable._ID + "=" + uri.PathSegments.ElementAt(1));
22                     }
23                     break;
24             }
25             SQLiteDatabase db = dbHelper.ReadableDatabase;
26             ICursor c = qb.Query(db, projection, selection, selectionArgs, null, null, " _id desc");
27             c.SetNotificationUri(Context.ContentResolver, uri);
28             return c;
29         }

 代碼中的注釋已經將重點部分都介紹了,關於Query的參數可以跟數據庫對象的Query進行比較,都是一樣的只是少了一部分參數。

 

7.完善Insert

因為SQLite規定了id只能是數據庫自動生成的,所以在插入數據庫這塊只需要判斷操作的是哪個表,介於筆者這里只有一個表所以沒有該項操作,下面是具體的代碼:

1         public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
2         {
3             SQLiteDatabase db = dbHelper.WritableDatabase;
4             long rowId = db.Insert(USERTABLE_NAME, null, values);
5             //拼接最終形成的URI
6             Android.Net.Uri result = ContentUris.WithAppendedId(UserTable.CONTENT_URI, rowId);
7             Context.ContentResolver.NotifyChange(result, null);
8             return result;
9         }

唯一要說明的就是在添加完數據之后要將這條數據組成的uri返回,這樣就可以方便以后的查詢。

 

8.完善Update

 1         public override int Update(Android.Net.Uri uri, ContentValues values, string selection, string[] selectionArgs)
 2         {
 3             SQLiteDatabase db = dbHelper.WritableDatabase;
 4             int count = 0;
 5             switch (uriMatcher.Match(uri))
 6             {
 7                 case USER:
 8                     {
 9                         count = db.Update(USERTABLE_NAME, values, selection, selectionArgs);
10                     }
11                     break;
12                 case USER_ID:
13                     {
14                         String userid = uri.PathSegments.ElementAt(1);
15                         string select = "";
16                         //如果還有附加的查詢語句則拼接上去
17                         if (selection != null)
18                             select = " AND(" + selection + ")";
19                         count = db.Update(USERTABLE_NAME, values, UserTable._ID + "=" + userid + select, selectionArgs);
20                     }
21                     break;
22             }
23             Context.ContentResolver.NotifyChange(uri, null);
24             return count;
25         }

這里跟查詢一樣,需要判斷是針對某條數據還是整個表。

 

9.完善Delete

理解了Update,刪除就簡單了,只是將db.Update方法改寫成Delete即可,代碼如下所示:

 1         public override int Delete(Android.Net.Uri uri, string selection, string[] selectionArgs)
 2         {
 3             SQLiteDatabase db = dbHelper.WritableDatabase;
 4             int count = 0;
 5             switch (uriMatcher.Match(uri))
 6             {
 7                 case USER:
 8                     {
 9                         count = db.Delete(USERTABLE_NAME, selection, selectionArgs);
10                     }
11                     break;
12                 case USER_ID:
13                     {
14                         String userid = uri.PathSegments.ElementAt(1);
15                         string select = "";
16                         if (selection != null)
17                             select = " AND(" + selection + ")";
18                         count = db.Delete(USERTABLE_NAME,
19                             UserTable._ID + "=" + userid + select,
20                             selectionArgs);
21                     }
22                     break;
23             }
24             Context.ContentResolver.NotifyChange(uri, null);
25             return count;
26         }

 最后就是GetType方法,只要返回空字符串即可。

 

四、操作ContentProvider

現在我們回到MainActivity中使用ContentProvider對數據庫進行操作,其中最關鍵的是ContentResolver是不是跟ContentProvider是配對的?通過ContentResolver我們就可以通過URI來操作數據庫,而不需要關注具體的數據庫對象,比如下面的代碼我們進行了插入、查詢、更新和刪除操作,代碼量要比使用SQLiteOpenHelper更少,同時也便於后期的維護:

 1             ContentValues values = new ContentValues();
 2             values.Put(LocationContentProvider.UserTable.UserName, "yzf");
 3             values.Put(LocationContentProvider.UserTable.UserPwd, "123");
 4             values.Put(LocationContentProvider.UserTable.Age, 23);
 5             Android.Net.Uri uri = ContentResolver.Insert(LocationContentProvider.UserTable.CONTENT_URI, values);
 6 
 7             var c = ContentResolver.Query(uri, null, null, null, null);
 8             c.MoveToFirst();
 9             string username = c.GetString(c.GetColumnIndex(LocationContentProvider.UserTable.UserName));
10 
11             values.Clear();
12             values.Put(LocationContentProvider.UserTable.UserName, "zn");
13             ContentResolver.Update(uri, values, null, null);
14 
15             c = ContentResolver.Query(uri, null, null, null, null);
16             c.MoveToFirst();
17             username = c.GetString(c.GetColumnIndex(LocationContentProvider.UserTable.UserName));
18 
19             ContentResolver.Delete(uri, null, null);

 

 

示例項目下載請點我

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM