ContentProvider
一、废话
好不容易完成出差任务,又被派到另一个地方出差!苦逼的我!年终奖还不给我发,再不发的话我都快没钱坐车回家了!
二、正文
今天内容是ContentProvider——如果做过电话薄应用程序的人肯定都用过这个类,那ContentProvider到底是个什么东西,有什么用,如何用呢?
1、 ContentProvider是个啥?
ContentProvider——内容提供者。它是一个类,这个类主要是对Android系统中进行共享的数据进行包装,并提供了一组统一的访问接口供其他程序调用。这些被共享的数据,可以使系统自己的也可以使我们个人应用程序中的数据。
2、 为什么要有ContentProvider这个类?
在Android中,数据的存储有很多种方式,最常用的就是SQLite和XML文件方式。在不同的应用程序间,其实数据是不能直接被相互访问和操作的,在这种情况下,ContentProvider很好的被用来解决了不同应用程序间数据共享的问题。
其实在Android系统中,已经为我们提供了许多ContentProvider,如:Contacts、Browser、CallLog、Settings等等。那么,Android系统中提供了这么多的ContentProvider,另外还有我们自己公开的共享数据,我们在写程序的时候,怎么才能让我们的应用程序知道去哪儿取、如何取这些数据呢?我们自然的会想到URI。
3、 URI是个啥?在ContentProvider中有什么用处?URI中的几个方法。
URI(Uniform Resource Identifier)——统一资源定位符,URI在ContentProvider中代表了要操做的数据。
在Android系统中通常的URI格式为:content://LiB.cprovider.myprovider.Users/User/21
在万维网访问时通常用的URI格式为:http://www.XXXX.com/AAA/123
- content://——schema,这个是Android中已经定义好的一个标准。我个人一直认为这和我们的http://有异曲同工之妙,都是代表的协议。
- LiB.cprovider.myprovider.Users——authority(主机名),用于唯一标识这个ContentProvider,外部调用者通过这个authority来找到它。相当于www.XXXX.com,代表的是我们ContentProvider所在的”域名”,这个”域名”在我们Android中一定要是唯一的,否则系统怎么能知道该找哪一个Provider呢?所以一般情况下,建议采用完整的包名加类名来标识这个ContentProvider的authority。
- /User/21——路径,用来标识我们要操作的数据。/user/21表示的意思是——找到User中id为21的记录。其实这个相当于/AAA/123。
综上所述,content://LiB.cprovider.myprovider.Users/User/21所代表的URI的意思为:标识LiB.cprovider.myprovider中Users表中_ID为21的User项。
通过上面的介绍,我想大家都知道URI到底是如何标识我们的ContentProvider了,我们就来看看Android系统中为我们提供了些有关操作URI的方法吧。
ContentUris类——操作URI
withAppendedId(builder, id)——在builder最后加上id组成URI。
Uri uri = ContentUris.withAppendedId(User.CONTENT_URI, id);
parseId(uri)——用于从URI中提取出ID值。
long returnID = ContentUris.parseId(User.CONTENT_URI);
UriMatcher类——匹配URI
addURI(authority, path, code)——为UriMatcher添加规则,并在匹配成功后返回code值,否则返回构造函数中传入的默认值,一般为UriMatcher.NO_MATCH
参数解释:authority——主机名,path——路径,code——匹配后返回的值
uriMatcher.addURI(Users.AUTHORITY, "User", USER);
4、 ContentProvider中公开的几个方法
- public boolean onCreate():该方法在ContentProvider创建后就会被调用,Android系统运行后,ContentProvider只有在被第一次使用它时才会被创建。
- public Uri insert(Uri uri, ContentValues values):外部应用程序通过这个方法向 ContentProvider添加数据。
- uri——标识操作数据的URI
- values——需要添加数据的键值对
- public int delete(Uri uri, String selection, String[] selectionArgs):外部应用程序通过这个方法从 ContentProvider中删除数据。
- uri——标识操作数据的URI
- selection——构成筛选添加的语句,如"id=1" 或者 "id=?"
- selectionArgs——对应selection的两种情况可以传入null 或者 new String[]{"1"}
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):外部应用程序通过这个方法对 ContentProvider中的数据进行更新。
- values——对应需要更新的键值对,键为对应共享数据中的字段,值为对应的修改值
- 其余参数同delete方法
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):外部应用程序通过这个方法从ContentProvider中获取数据,并返回一个Cursor对象。
- projection——需要从Contentprovider中选择的字段,如果为空,则返回的Cursor将包含所有的字段。
- sortOrder——默认的排序规则
- 其余参数同delete方法
- public String getType(Uri uri):该方法用于返回当前Url所代表数据的MIME类型。
- 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有user记录的Uri为content:// LiB.cprovider.myprovider.Users /User,那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/user"。
- 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id为21的user记录,Uri为content:// LiB.cprovider.myprovider.Users /User/21,那么返回的MIME类型字符串为:"vnd.android.cursor.item/user"。
5、 如何公开我自己的数据?
看了那么多,如何才能够把我们自己的数据公开出来给别的程序使用呢?
首先,继承ContentProvider并重写它的几个抽象方法
1 package LiB.cprovider;2 3 import java.util.HashMap;4 5 import LiB.cprovider.Users.User;6 import android.content.ContentProvider;7 import android.content.ContentUris;8 import android.content.ContentValues;9 import android.content.UriMatcher;10 import android.database.Cursor;11 import android.database.sqlite.SQLiteDatabase;12 import android.database.sqlite.SQLiteQueryBuilder;13 import android.net.Uri;14 import android.text.TextUtils;15 16 public class myprovider extends ContentProvider {17 18 private DBHelper dbHelper;19 private static final UriMatcher uriMatcher;20 private static final int USER = 1;21 private static final int USER_ID = 2;22 private static HashMap<String, String> maps;23 static {24 // 当没有匹配成功是时,返回NO_MATCH的值25 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);26 // 匹配Users表中的所有User,匹配成功后返回USER整数值27 // content://LiB.cprovider.myprovider.Users/User28 uriMatcher.addURI(Users.AUTHORITY, "User", USER);29 // 匹配Users表中指定ID的User项,匹配成功后返回USER_ID整数值30 // content://LiB.cprovider.myprovider.Users/User/2131 uriMatcher.addURI(Users.AUTHORITY, "User/#", USER_ID);32 33 maps = new HashMap<String, String>();34 maps.put(User._ID, User._ID);35 maps.put(User.NAME, User.NAME);36 maps.put(User.SEX, User.SEX);37 maps.put(User.AGE, User.AGE);38 }39 40 @Override41 public int delete(Uri uri, String selection, String[] selectionArgs) {42 SQLiteDatabase db = dbHelper.getWritableDatabase();43 int count = 0;44 switch (uriMatcher.match(uri)) {45 case USER:46 count = db.delete(DBHelper.DATATABLE_NAME, selection, selectionArgs);47 break;48 case USER_ID:49 String noteId = uri.getPathSegments().get(1);50 count = db.delete(DBHelper.DATATABLE_NAME, User._ID51 + "="52 + noteId53 + (!TextUtils.isEmpty(selection) ? " AND (" + selection54 + ')' : ""), selectionArgs);55 break;56 default:57 throw new IllegalArgumentException();58 }59 this.getContext().getContentResolver().notifyChange(uri, null);60 return count;61 }62 63 @Override64 public String getType(Uri uri) {65 return null;66 }67 68 @Override69 public Uri insert(Uri uri, ContentValues values) {70 SQLiteDatabase db = dbHelper.getWritableDatabase();71 // User.NAME不能为空72 long _id = db.insert(DBHelper.DATATABLE_NAME, User.NAME, values);73 if (_id > 0) {74 Uri uri1 = ContentUris.withAppendedId(User.CONTENT_URI, _id);75 this.getContext().getContentResolver().notifyChange(uri1, null);76 return uri1;77 }78 return null;79 }80 81 @Override82 public boolean onCreate() {83 dbHelper = new DBHelper(this.getContext());84 return true;85 }86 87 @Override88 public Cursor query(Uri uri, String[] projection, String selection,89 String[] selectionArgs, String sortOrder) {90 SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();91 switch (uriMatcher.match(uri)) {92 case USER:93 sqb.setTables(DBHelper.DATATABLE_NAME);94 sqb.setProjectionMap(maps);95 break;96 case USER_ID:97 sqb.setTables(DBHelper.DATATABLE_NAME);98 sqb.setProjectionMap(maps);99 sqb.appendWhere(User._ID + "=" + uri.getPathSegments().get(1));
100 break;
101 default:
102 throw new IllegalArgumentException();
103 }
104 SQLiteDatabase db = dbHelper.getReadableDatabase();
105 Cursor cursor = sqb.query(db, projection, selection, selectionArgs,
106 null, null, null);
107 cursor.setNotificationUri(getContext().getContentResolver(), uri);
108 return cursor;
109 }
110
111 @Override
112 public int update(Uri uri, ContentValues values, String selection,
113 String[] selectionArgs) {
114 SQLiteDatabase db = dbHelper.getWritableDatabase();
115 int count;
116 switch (uriMatcher.match(uri)) {
117 case USER:
118 count = db.update(DBHelper.DATATABLE_NAME, values, selection,
119 selectionArgs);
120 break;
121 case USER_ID:
122 String noteId = uri.getPathSegments().get(1);
123 count = db.update(DBHelper.DATATABLE_NAME, values, User._ID
124 + "="
125 + noteId
126 + (!TextUtils.isEmpty(selection) ? " AND (" + selection+ ')' : ""), selectionArgs);
127 break;
128 default:
129 throw new IllegalArgumentException();
130 }
131 getContext().getContentResolver().notifyChange(uri, null);
132 return count;
133 }
134 }
然后,定义我们自己的数据存储(记住,在我们公开的数据中,一定要有个_ID字段,因为在Android中,数据的存储方式是以一个表格的形式存储的,_ID字段唯一标示了每项数据,所以常规做法是将我们的类继承自BaseColumns类,关于这个类请查看官方帮助文档)
1 package LiB.cprovider;2 3 import android.net.Uri;4 import android.provider.BaseColumns;5 //在BaseColumns中就已经包含了我们所必须的_ID字段,具体的可以查看帮助文档6 //下面的类组成了一个含有User项的Users表,每一项就是一个User对象,每个对象含有name,sex,age等属性7 public final class Users 8 {9 //请务必记住,这个AUTHORITY一定要和你在配置文件中配置的一样
10 public static final String AUTHORITY="LiB.cprovider.myprovider.Users";
11 private Users(){}
12 //包含的项
13 public static final class User implements BaseColumns{
14 private User(){}
15 //标识URI
16 public static final Uri CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/User");
17 //类型
18 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.LiB.Users";
19 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.LiB.Users";
20 // 表字段常量
21 public static final String NAME = "name"; // 姓名
22 public static final String SEX= "sex"; // 性别
23 public static final String AGE = "age"; //年龄
24 }
25 }
然后在AndroidManifest.xml中对我们的Provider进行注册
<?xml version="1.0" encoding="utf-8"?>2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"3 package="LiB.cprovider" android:versionCode="1" android:versionName="1.0">4 <uses-sdk android:minSdkVersion="9" />5 6 <application android:icon="@drawable/icon" android:label="@string/app_name">7 <activity android:name=".Android_ContentProviderDemoActivity"8 android:label="@string/app_name">9 <intent-filter>
10 <action android:name="android.intent.action.MAIN" />
11 <category android:name="android.intent.category.LAUNCHER" />
12 </intent-filter>
13 </activity>
14 <provider
15 android:name=".myprovider"
16 android:authorities="LiB.cprovider.myprovider.Users">
17 </provider>
18 </application>
19 </manifest>
在AndroidManifest.xml文件中,配置provider节时一定要注意android:name为你的继承自ContentProvider的那个类的类名,android:authorities与你在定义自己的数据存储时所定义的AUTHORITIES字段一定要相等,虽然这个字段值可以随意,但是非常强烈的建议你采用包名加类名的方式类命名。
6、 怎么操作我自己的数据呢?
为了方便我们操作ContentProvider,Android系统为我们提供了ContentResolver类。我们可以使用getContentResolver()方法返回一个ContentResolver对象,并使用它与ContentProvider对应的方法来进行操作。
// 删除方法2 private void delete(int id) {3 Uri uri = ContentUris.withAppendedId(User.CONTENT_URI, id);4 // 使用getContentResolver()获取ContentResolver对象,并使用它的delete()方法进行数据删除操作5 getContentResolver().delete(uri, null, null);6 System.out.println("del");7 }8 9 // 更新
10 private void update(int id) {
11 Uri uri = ContentUris.withAppendedId(User.CONTENT_URI, id);
12 ContentValues values = new ContentValues();
13 values.put(User.NAME, "LiB");
14 values.put(User.SEX, "male");
15 values.put(User.AGE, 20+id);
16 // 使用getContentResolver()获取ContentResolver对象,并使用它的update()方法进行数据更新操作
17 getContentResolver().update(uri, values, null, null);
18 System.out.println("update...");
19 }
20
21 // 查询
22 private void query() {
23 // 需要查询的列名
24 String[] PROJECTION = new String[] {
25 User._ID,
26 User.NAME,
27 User.SEX,
28 User.AGE
29 };
30 // 此处也可以使用Cursor c = managedQuery(User.CONTENT_URI, PROJECTION, null,
31 // null, null);方法来进行查询操作
32 Cursor c = getContentResolver().query(User.CONTENT_URI, PROJECTION,
33 null, null, null);
34 if (c.moveToFirst()) {
35 for (int i = 0; i < c.getCount(); i++) {
36 c.moveToPosition(i);
37 String name = c.getString(1);
38 String sex = c.getString(2);
39 int age = c.getInt(3);
40 // 输出日志
41 Log.i("query", name + ":" + sex + ":" + age);
42 }
43 }
44 System.out.println("query...");
45 }
46
47 // 插入
48 private void insert(int id) {
49 Uri uri = User.CONTENT_URI;
50 ContentValues values = new ContentValues();
51 values.put(User.NAME, "ZMR_"+id);
52 values.put(User.SEX, "female");
53 values.put(User.AGE, 20+id);
54 // 使用getContentResolver()获取ContentResolver对象,并使用它的insert()方法进行数据插入操作
55 getContentResolver().insert(uri, values);
56 System.out.println("insert");
57 }
三、总结
其实在Android中,系统已经为我们提供了许多的ContentProvider了,我们通常情况下是不需要去自定义ContentProvider的。但是,为了更好的理解ContentProvider,自己来实现一个ContentProvider是非常必要的。同时,在Android中,数据的存储方式有很多种,在上面的例子中提到了有SQLite和XML方式,下一篇笔记将介绍如何操作XML文件。
作者:Libo@Deng
出处:http://www.cnblogs.com/cxcco/
本文版权归作者和博客园共有,欢迎转载,原文网址:http://www.cnblogs.com/cxcco/archive/2012/01/07/2315711.html
ContentProvider相关推荐
- ContentProvider是如何实现数据共享的
ContentProvider用于跨进程通讯,一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数据 ...
- ContentProvider访问问题
问题解决一: 写了一个类,实现了ContentProvider,在清单文件中也注册了. <provider android:name=".provider.MyProvider&quo ...
- contentProvider的使用总结
一.使用ContentProvider(内容提供者)共享数据 ContentProvider在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给 ...
- android 53 ContentProvider内容提供者
ContentProvider内容提供者:像是一个中间件一样,一个媒介一样,可以以标准的增删改差操作对手机的文件.数据库进行增删改差.通过ContentProvider查找sd卡的音频文件,可以提供标 ...
- Android ContentProvider和getContentResolver
安卓系统中的数据库SqlLite操作和java中mysql的数据库操作很不一样,造成这样的原因是因为在安卓中数据库是属于进程的不存在数据库客户端,也不存在数据库服务器. 关于SqlLite数据库的文章 ...
- 创建ContentProvider的详细步骤
2019独角兽企业重金招聘Python工程师标准>>> Content Provider管理中央存储库的数据的访问, 你在Android程序中实现一个或者多个Provider, 连同 ...
- 使用ContentProvider
2019独角兽企业重金招聘Python工程师标准>>> 一. 了解 ContentProvider 1. 什么是ContentProvider 让开发者在多个应用中操作数据,如存 ...
- Android开发之ContentProvider结合LoaderManager加载数据(图文源代码分享)
ContentProvider作为Android的四大存储方式之一,有着广泛的应用性,它暴露了数据地址,可以让其他应用访问数据,可以用于存储图片.通讯录等信息,这篇博文将详细介绍ContentProv ...
- Android Service、 BroadcastReceiver、ContentProvider ANR 原理详解
Service ANR: 启动Service 的时候,会调用到ActiveServices 类的 realStartServiceLocked方法. private final void realSt ...
- ContentProvider使用场景解读
为什么需要ContentProvider? 我们知道,一个软件系统的架构通常是这样的: 为了降低上层业务对底层数据的依赖,需要增加一个数据访问层来解耦,我们今天要说的ContentProvider充当 ...
最新文章
- 数据仓库的未来 MariaDB ColumnStore
- 详解DNS递归查询与迭代查询
- 【译】Object Dumper: 函数式程序设计编码中的强大工具
- 开发函数计算的正确姿势 —— 爬虫
- 【Python刷题】_7
- android rild
- House Robber
- 世界上任何一件事的五个模块
- 在block中使用self
- 一文搞懂Spring Cloud Zuul
- APP测试与WEB测试
- Scrum板与Kanban如何抉择?kdliihoap板与按照xhvrcr
- html添加markdown,为自己的网站添加Markdown——showdown.js使用教程
- ImageJ自动批量荧光面积统计
- win10 添加打印机完整图文教程演示
- 网易云音乐歌单详情列表爬虫破解
- android notify() notifyAll()的区别
- linux clk驱动框架
- 对于java的初步认识
- SSL/TLS 双向认证(一) -- SSL/TLS 工作原理
热门文章
- WEB 1.HTML
- 计算机桌面导航栏中删除内容,教你如何一键去除Windows桌面右击菜单多余选项-win7右键菜单管理...
- STM32F103_study69_The punctual atoms(STM32 SPI communication principle and configuration)
- 我的世界java版和基岩版是什么意思_我的世界基岩版是什么意思
- shanzhi -接小球游戏4.0
- 快速上手efficient(keras)
- kuangbin 最小生成树
- 线性代数中解方程组的加减消元和求特征向量的加减消元的区别
- 建设城市之智慧灯杆网关
- 《惢客创业日记》2020.07.28(周二)因健忘症引发的痛点