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);

Xamarin.Android开发实践(十二)的更多相关文章

  1. Xamarin.Android开发实践(二)

    原文:Xamarin.Android开发实践(二) 一.准备 开始学习本教程前必须先完成该教程http://www.cnblogs.com/yaozhenfa/p/xamarin_android_qu ...

  2. Xamarin.Android开发实践(五)

    原文:Xamarin.Android开发实践(五) 一.服务的生命周期 服务与活动一样,在它的整个生命周期中存在着一些事件,下图可以很好解释整个过程以及涉及到的方法: 在真实的使用中,Service来 ...

  3. Xamarin.Android开发实践(四)

    原文:Xamarin.Android开发实践(四) Xamarin.Android下获取与解析JSON 一.新建项目 1.新建一个Android项目,并命名为为NetJsonList 2.右击引用,选 ...

  4. Xamarin.Android开发实践(三)

    原文:Xamarin.Android开发实践(三) 一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没 ...

  5. Xamarin.Android开发实践(一)

    原文:Xamarin.Android开发实践(一) 一.准备工作 1.创建一个空的解决方案,并命名为Phoneword 2.右击解决方案 新建->新建项目 并命名为Phoneword_Droid ...

  6. Xamarin.Android开发实践(十五)

    Xamarin.Android学习之应用程序首选项 一.前言 任何App都会存在设置界面,如果开发者利用普通控件并绑定监听事件保存设置,这 一过程会非常的枯燥,而且耗时.我们可以看到Android系统 ...

  7. Xamarin.Android开发实践(十四)

    Xamarin.Android之ListView和Adapter 一.前言 如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xamarin去实现它,以及如何使用适配器和自定义适配器(本文 ...

  8. Xamarin.Android开发实践(十八)

    Xamarin.Android之SlidingMenu 一.前言 有位网友在评论中希望能够出个在Xamarin.Android下实现SlidingMenu效果的随笔,刚好昨天在观看官网示例项目的时候也 ...

  9. Xamarin.Android开发实践(十)

    Xamarin.Android之SQLiteOpenHelper 一.前言 在手机中进行网络连接不仅是耗时也是耗电的,而耗电却是致命的.所以我们就需要数 据库帮助我们存储离线数据,以便在用户未使用网络 ...

随机推荐

  1. 也谈闭包--小白的JS进阶之路

    JavaScript当然是会用的,不过没有深入系统的学习罢了.平常还是用JQuery比较多,原生的JS用到的很少. 不过前端时间学习Ruby,被动态语言的强大和魔幻给震惊了一把.了解Ruby后,我把目 ...

  2. Adele的生活

    3个小时的电影,有点冗长,中间抽空吃了个饭,跑出去抽了3根烟,接着洗了个脸才回来接着看完,法国人做事真是拖沓啊有木有. 电影看完后给人一种淡淡的忧伤,第一次了解lesbians的真实世界(如果电影是来 ...

  3. ubuntu快速清理磁盘垃圾

    .快速清理磁盘垃圾 磁盘空间又不够用了?尝试在终端窗口中输入sudo apt-get autoremove然后输入sudo apt-get clean,前一个命令会卸载系统中所有未被使用的依赖关系,后 ...

  4. DOM: 如何获取元素下的第一个子元素

    Element.firstChild ?,是的,这是第一种方法,当然,通常来说支持 W3C 规范的浏览器,如 Firefox 等取到的应该是 TEXT_NODE.很早之前,或者说现在最流行的方法可能是 ...

  5. Linux/Ubuntu tree 命令以树形结构显示文件夹目录结构

    1.安装命令工具 sudo apt-get -y install tree 2.可以查看关于tree命令的帮助信息 $ tree --help usage: tree [-adfghilnpqrstu ...

  6. HDU 1029 Ignatius and the Princess IV

    解题报告: 题目大意:就是要求输入的N个数里面出现的次数最多的数是哪一个,水题.暴力可过,定义一个一位数组,先用memset函数初始化,然后每次输入一个数就将下标对应的上标对应的那个数加一,最后将整个 ...

  7. 使用Unity创造动态的2D水体效果

    者:Alex Rose 在本篇教程中,我们将使用简单的物理机制模拟一个动态的2D水体.我们将使用一个线性渲染器.网格渲染器,触发器以及粒子的混合体来创造这一水体效果,最终得到可运用于你下款游戏的水纹和 ...

  8. phpcms某处储存型XSS(demo+本地演示)

    文章转载:http://www.myhack58.com/Article/html/3/7/2016/71726.htm 详细说明: demo+本地演示存在xss漏洞的地方在商务中心的商家资料的我的资 ...

  9. HTML5 自制本地网页视频播放器

    HTML5初试:本地视频用网页打开啦半个广告都可以没有,看来暴风什么的快要淘汰了. 视频格式还是有要求的,看来要备一个转码器. 格式 IE Firefox Opera Chrome Safari Og ...

  10. [ruby on rails] 跟我学之(8)修改数据

    修改views 修改index视图(app/views/posts/index.html.erb),添加编辑链接,如下: <h1>Our blogs</h1> <% @p ...