近日来学习ContentProvider相关的知识,做了一个demo,想和网友分享下。

  首先说一点相关的知识:

  一:作用

  ContentProvider是不同应用程序共享数据的接口,跟共享数据的别的方法相比,ContentProvider更好地提供了数据共享接口的统一性。CP(ContentProvider的简称)通过一张或者多张表的形式向外部应用程序提供数据(就像关系型数据库中看见的表那样)。

  二:Content URIS

  URI(Uniform Resource Identifier)统一资源标示符,能表示provider中的数据。由三部分组成,分别是scheme,authority,path.它的scheme,Android系统规定为"content://",authority唯一表示了要访问的CP名字,而path表示了要访问的CP中的数据。在使用ContentResolver对象访问CP时,需要一个Uri参数。构造Uri时可能会用到三个类:

  Uri.Builder类可用来通过String构建Uri

  ContentUris.withAppendedId()可在Uri后面加一个id

  UriMatcher:在CP中这个类可帮你从接受到Uri中选择出要执行的ACTION

  三:MIME type

  MIME,多用途互联网邮件扩展,是一种互联网标准。它的作用是:服务器会通过MIME值来告诉客户端所传送的多媒体数据的类型。CP也是一种C/S架构,所以需要使用到这个值。它的格式为:type/subtype,比如text/html,代表所传输的文本为html的格式。在Android中MIME可提供两类值:统一的MIME数据类型和定制的MIME类型字符串。在CP中前者在传输文件时会用到,而后者更常用,发送结构化的数据,譬如SQLiteDatabase时会使用定制的MIME类型字符串。在android中后者有自己的规定:

  如果返回单行数据,type被设置为"vnd.android.cursor.item"

  如果返回多行数据,type被设置为"vnd.android.cursor.dir"

  而subtype部分由CP类自己设置。

  接下来进入正题,说代码环节,这篇代码是通过结构化数据来阐述CP的。

  一:客户端

  在客户端,是通过一个ContentResolver对象作为client和CP交互的。ContentResolver对象可调用CP中的同名方法,可提供“增删改查”的功能。

  Step1:在客户端的manifest文件申请要访问的CP的权限(该权限在CP端已向系统注册,下文会讲)

1 <uses-permission android:name="com.example.cpserver.permission" />

  Step1.在“增删改查”之前需要判断将要请求的Uri的有效性,代码如下:

 1 //判断所要使用的Provider是否有效
2 private boolean checkValidProvider(Uri uri)
3 {
4 ContentProviderClient client = getContentResolver().acquireContentProviderClient(uri);
5 if(client == null)
6 {
7 System.out.println("provider is invalid!");
8 return false;
9 }
10 else
11 {
12 client.release();
13 return true;
14 }
15 }

  Step3:"增删改查"功能,这些方法都是SQL中DDL语言的一个封装,具体每个方法的返回值,参数意义不展开讲了,否则很长,可留言询问。

 1 private int insert()
2 {
3 if(!checkValidProvider(Contract.CONTENT_URI))
4 return -1;
5 ContentValues values = new ContentValues();
6 values.put(Contract.COLUMN_NAME_1, "小卫的春天");
7 values.put(Contract.COLUMN_NAME_2, "翟卫华");
8 values.put(Contract.COLUMN_NAME_3, "100");
9 Uri uri = getContentResolver().insert(Contract.CONTENT_URI, values);
10 String lastPath = uri.getLastPathSegment();
11 if(TextUtils.isEmpty(lastPath))
12 {
13 System.out.println("insert failure!");
14 }
15 else
16 {
17 System.out.println("insert success! the id is " + lastPath);
18 }
19 return Integer.parseInt(lastPath);
20 }
21
22 //删除所有行
23 private int delete()
24 {
25 if(!checkValidProvider(Contract.CONTENT_URI))
26 return -1;
27 int count = getContentResolver().delete(Contract.CONTENT_URI, null, null);
28 return count;
29 }
30
31 //将所有行的数据进行一个修改
32 private int update()
33 {
34 if(!checkValidProvider(Contract.CONTENT_URI))
35 return -1;
36 ContentValues values = new ContentValues();
37 values.put(Contract.COLUMN_NAME_1,"小宝的春天");
38 values.put(Contract.COLUMN_NAME_2, "翟小宝");
39 values.put(Contract.COLUMN_NAME_3, "200");
40 int count = getContentResolver().update(Contract.CONTENT_URI, values, null, null);
41 if(count == 0)
42 {
43 System.out.println("update failed!");
44 }
45 return count;
46 }
47
48 //row:要查找的行号, "-1"代表查找多行
49 private void query(int row)
50 {
51 if(!checkValidProvider(Contract.CONTENT_URI))
52 return;
53 Cursor cursor = null;
54 if(row != -1)
55 {
56
57 }
58 else
59 {
60 cursor = getContentResolver().query(Contract.CONTENT_URI,
61 new String[]{Contract.COLUMN_NAME_1,Contract.COLUMN_NAME_2,Contract.COLUMN_NAME_3},null, null, null);
62 }
63 if(cursor == null)
64 System.out.println("query failure!");
65 else
66 {
67 String strDisplay = getDataFromCursor(cursor);
68 tvDisplay.setText(strDisplay);
69 cursor.close();
70 }
71 }

  二:CP端

  在CP端要通过继承ContentProvider来实现。CP是Android的四大组件之一,比较重要,而且系统本身就能提供很多的ContentProvider供开发者使用,比如可通过CP请求道所有的图片,音频,视频等数据。在Android系统中CP默认是可被别的任何应用程序请求到的,如果你不设置权限加以限制的话。所以第一步应该设置一个permission字段,并把它添加到provider标签里面去。

 1 <permission
2 android:name="com.example.cpserver.permission"
3 android:label="Example Data"
4 android:protectionLevel="signature" />
5
6 <provider
7 android:name="provider.TestProvider"
8 android:authorities="com.example.cpserver.provider"
9 android:permission="com.example.cpserver.permission"
10 android:exported="true"
11 android:enabled="true" />

  Step1:实现一个SQLiteOpenHelper作为Provider的数据存储库

 1 package db;
2
3 import com.example.cpserver.Contract;
4
5 import android.content.Context;
6 import android.database.sqlite.SQLiteDatabase;
7 import android.database.sqlite.SQLiteOpenHelper;
8 /*
9 * 实现一个SQLiteOpenHelper作为Provider的数据存储库
10 */
11 public final class MainDatabaseHelper extends SQLiteOpenHelper {
12
13 //创建一张表的SQL语句
14 private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
15 Contract.TABLE_NAME + "(" +
16 " _ID INTEGER PRIMARY KEY, " +
17 Contract.COLUMN_NAME_1 + " TEXT," +
18 Contract.COLUMN_NAME_2 + " TEXT," +
19 Contract.COLUMN_NAME_3 + " INTEGER )";
20
21 public MainDatabaseHelper(Context context)
22 {
23 super(context, Contract.DB_NAME, null, 1);
24 }
25
26 /*
27 *当Provider设法打开数据存储库,并且数据库不存在时该方法被调用
28 */
29 @Override
30 public void onCreate(SQLiteDatabase db) {
31 // Creates the main table
32 db.execSQL(SQL_CREATE_MAIN);
33 }
34
35 @Override
36 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
37 // TODO Auto-generated method stub
38
39 }
40 }

  Step2:实现ContentProvider的子类,并且扩展抽象方法,代码注释很详细。

  1 package provider;
2
3 import com.example.cpserver.Contract;
4 import db.MainDatabaseHelper;
5 import android.content.ContentProvider;
6 import android.content.ContentUris;
7 import android.content.ContentValues;
8 import android.content.UriMatcher;
9 import android.database.Cursor;
10 import android.database.SQLException;
11 import android.database.sqlite.SQLiteDatabase;
12 import android.net.Uri;
13 import android.text.TextUtils;
14
15 /*
16 * 1.除了onCreate方法,别的方法都可能会被多线程调用,所以这些方法要设计成线程安全的
17 * 2.在onCreate中避免耗时操作
18 * 3.虽然以下方法都要被继承,但不必重写每个方法,除了getType
19 */
20 public class TestProvider extends ContentProvider
21 {
22 //创建一个UriMatcher对象,该对象帮你从接受的URI中选择出要执行的动作
23 private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
24 private MainDatabaseHelper mOpenHelper;
25 private SQLiteDatabase db = null;
26
27 //调用addURI方法添加provider可以识别的所有URI类型
28 static
29 {
30 sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME, 1);
31 sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME+"/#", 2);
32 }
33 /*
34 * 该方法作用:初始化该Provider
35 * 注意:直到一个ContentResolver对象访问时,该方法才被调用
36 * 这个方法里不应做耗时的操作,因为这样可能延缓Provider对别的应用程序的相应
37 */
38 @Override
39 public boolean onCreate() {
40 mOpenHelper = new MainDatabaseHelper(getContext());
41 return true;
42 }
43
44 @Override
45 public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
46 {
47 int type = sUriMatcher.match(uri);
48 switch (type)
49 {
50 //多行请求的URI
51 case 1:
52 if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
53 break;
54 case 2:
55 //单行请求的URI
56 selection = selection + "_ID = " + uri.getLastPathSegment();
57 break;
58 default:
59 //如果URI不匹配,做一些错误提示,返回null或者抛出异常
60 throw new IllegalArgumentException("Unknown URI " + uri);
61 }
62 if(db == null)
63 db = mOpenHelper.getWritableDatabase();
64 Cursor cursor = db.query(Contract.TABLE_NAME, projection, selection, selectionArgs, null, null,sortOrder);
65 return cursor;
66 }
67
68 //返回content URI相应的MIME 类型
69 @Override
70 public String getType(Uri uri)
71 {
72 int type = sUriMatcher.match(uri);
73 switch (type)
74 {
75 case 1:
76 return Contract.CONTENT_TYPE;
77 case 2:
78 return Contract.CONTENT_ITEM_TYPE;
79 default:
80 throw new IllegalArgumentException("Unknown URI " + uri);
81 }
82 }
83
84 @Override
85 public Uri insert(Uri uri, ContentValues initialValues)
86 {
87 int type = sUriMatcher.match(uri);
88 if(type != 1)
89 throw new IllegalArgumentException("Unknown URI " + uri);
90
91 //创建一个可写数据库,将调用MainDatabaseHelper的onCreate方法,如果数据库还不存在的话
92 if(db == null)
93 db = mOpenHelper.getWritableDatabase();
94
95 //确保所有的域都被设置
96 ContentValues values;
97 if (initialValues != null)
98 values = new ContentValues(initialValues);
99 else
100 values = new ContentValues();
101 if (values.containsKey(Contract.COLUMN_NAME_1) == false) {
102 values.put(Contract.COLUMN_NAME_1, "");
103 }
104 if (values.containsKey(Contract.COLUMN_NAME_2) == false) {
105 values.put(Contract.COLUMN_NAME_2, "");
106 }
107 if (values.containsKey(Contract.COLUMN_NAME_3) == false) {
108 values.put(Contract.COLUMN_NAME_3, "");
109 }
110
111 long rowId = db.insert(Contract.TABLE_NAME,null, values);
112 if(rowId > 0)
113 {
114 Uri noteUri = ContentUris.withAppendedId(Contract.CONTENT_URI, rowId);
115 getContext().getContentResolver().notifyChange(noteUri, null);
116 return noteUri;
117 }
118 throw new SQLException("Failed to insert row into " + uri);
119 }
120
121 @Override
122 public int delete(Uri uri, String selection, String[] selectionArgs)
123 {
124 if(db == null)
125 db = mOpenHelper.getWritableDatabase();
126 int type = sUriMatcher.match(uri);
127 int count;
128 switch (type)
129 {
130 case 1:
131 count = db.delete(Contract.TABLE_NAME, selection, selectionArgs);
132 break;
133 case 2:
134 String noteId = uri.getLastPathSegment();
135 count = db.delete(Contract.TABLE_NAME, "_ID" + "=" + noteId +
136 (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
137 break;
138 default:
139 throw new IllegalArgumentException("Unknown URI " + uri);
140 }
141 getContext().getContentResolver().notifyChange(uri, null);
142 return count;
143 }
144
145 @Override
146 public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs)
147 {
148 if(db == null)
149 db = mOpenHelper.getWritableDatabase();
150 int type = sUriMatcher.match(uri);
151 int count;
152 switch (type)
153 {
154 case 1:
155 count = db.update(Contract.TABLE_NAME, values, selection, selectionArgs);
156 break;
157 case 2:
158 String noteId = uri.getLastPathSegment();
159 count = db.update(Contract.TABLE_NAME, values,"_ID" + "=" + noteId +
160 (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
161 break;
162 default:
163 throw new IllegalArgumentException("Unknown URI " + uri);
164 }
165 getContext().getContentResolver().notifyChange(uri, null);
166 return count;
167 }
168
169 //如果Provider提供file数据,要用这个方法返回MIME类型
170 @Override
171 public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
172 // TODO Auto-generated method stub
173 return super.getStreamTypes(uri, mimeTypeFilter);
174 }
175
176 }

  我认为CP之所以不那么容易理解,是因为它涉及到的东西较多,还涉及到计算机网络中的的URI,MIME等概念,确实不是那么容易,作为码农的我们只能去啃了,无别的办法。总结一下,CP机制中主要涉及到这么几点知识:permission权限,uri,MIME,使用UriMatcher来匹配uri的类型,还要掌握一些基本的SQL语言等。关于使用CP来实现File分享,又是很长的一个篇幅,等得空再研究吧。我的项目用百度云做个下载链接吧,有需求的朋友可拿去用。欢迎留言交流。

http://pan.baidu.com/s/1gdkPia3

 

  Author:Andy Zhai

  2014-02-11    20:44:00

  

  

ContentProvider使用总结的更多相关文章

  1. Android之ContentProvider数据存储

    一.ContentProvider保存数据介绍 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数 ...

  2. Xamarin.Android之ContentProvider

    一.前言 掌握了如何使用SQLiteOpenHelper之后,我们就可以进行下一步的学习.本章我们将会学习如何使用ContentProvider来将数据库方面的操作封装起来,同时它还可以供其他应用访问 ...

  3. ContentProvider域名替换小工具

    开发项目域名想怎么换就怎么换,就是这么任性! 这是一个很有意思的小工具! 这是一个方便开发人员和测试人员的小工具!! 吐槽: 一直在做Android开发,一直总有一个问题存在:做自己公司的apk开发时 ...

  4. Android开发学习—— ContentProvider内容提供者

    * 应用的数据库是不允许其他应用访问的* 内容提供者的作用就是让别的应用访问到你的数据库.把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用. Uri:包含一个具有一定格式的字符串的对 ...

  5. 简单的学习心得:网易云课堂Android开发第六章SQLite与ContentProvider

    一.SQLite 1.基本操作: (1)创建数据库:在SQLiteOpenHelper的子类构造器中创建. (2)创建表:在SQLiteOpenHelper的子类onCreate方法中,调用execS ...

  6. ContentProvider中央档案馆,以及获取联系人电话的示例

    Android官方文档介绍的数据存储方式共有五种,sqlite,SharedPreferences,网络存储,外储存储,文件存储,但是这些数据都无法进行共享,那么我们就引入了今天的主角:Content ...

  7. Android基础 : Android ContentProvider

    Android 应用程序通过ContentProvider实现方式统一的数据共享功能. 外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activi ...

  8. 安卓初級教程(3):ContentProvider的運用原理

    package com.example.android.provider; import java.util.ArrayList; import java.util.HashMap; import j ...

  9. Android探索之ContentProvider熟悉而又陌生的组件

    前言: 总结这篇文章之前我们先来回顾一下Android Sqlite数据库,参考文章:http://www.cnblogs.com/whoislcj/p/5506294.html,Android程序内 ...

  10. 四大组件之ContentProvider

    前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...

随机推荐

  1. java多线程系列12 ConcurrentHashMap CopyOnWriteArrayList 简介

    我们知道 ,hashmap 和 arraylist 是线程不安全的 在多线程环境下有数据安全问题, 当然 我们可以通过Collections的一些方法把他们变成线程安全的, Collections.s ...

  2. SpringMvc在返回数据之前进行统一处理

    这里其实有多种解决方案 如果你不需要获取request对象 可以采用aop(环绕通知)的方式来统一修改 如果你需要获取request对象,那么就需要采用下面的方式 0自己定义一个注解,内容如下 @Ta ...

  3. html样式板

    一.bootstrap 二.element 三.iconfont图标 四.font awesome图标

  4. websocket的简单使用

    一 轮询 什么是轮询:设置每一段时间去访问一次服务器,然后服务器返回最新的数据.这样服务器的压力会非常的大,并且还会有延迟.适用于小型程序. 实现:再客户端的页面设置一个定时发送请求的任务,每个这段时 ...

  5. liunx Ubuntu 设置IP、网关、DNS

    说明:在网上给的教程上面通常会有这样的一个误导思路,按照配置文件设置后会不生效的问题,甚至没有一点效果,经过排查发现Linux下设置IP这个话题的入口线索应该分为两种:1为Server版,2为Desk ...

  6. 修改maven 本地仓库,加入阿里云

    阿里云仓库服务 http://maven.aliyun.com/mvn/view maven加入阿里云服务 在maven  conf文件下修改settings.xml 修改本地仓库<localR ...

  7. C# 最齐全的上传图片方法

    方法里包括了图片大小限制.图片尺寸.文件内容等等的判断... 该案例是mvc下的demo,支持单张图片上传. public ActionResult Upload() { string imgurl ...

  8. hdu 4911 Inversion and poj2299 [树状数组+离散化]

    题目 题意:  给你一串数字,然后给你最多进行k次交换(只能交换相邻的)问交换后的最小逆序对个数是多少. 给你一个序列,每次只能交换相邻的位置,把他交换成一个递增序列所需要的最少步数 等于 整个序列的 ...

  9. Servlet的补充知识

    ServletContextAware是获取ServletContext一个接口.只需要实现此接口重写里面的setServletContext方法,spring在初始化的时候通过xmlClasspat ...

  10. MFC 不同窗体之间变量调用

    应用场景: (1)主对话框包含一个Tab控件,Tab控件用来切换显示若干子对话框,子对话框类的成员需要互相访问. (2)或者程序中包含多个类,各类之间需要互相访问. 方法1-定义指针成员变量: 详情参 ...