前言:

总结这篇文章之前我们先来回顾一下Android Sqlite数据库,参考文章:http://www.cnblogs.com/whoislcj/p/5506294.html,Android程序内部数据存储如果使用Sqlite数据库,那么Android 如何实现程序间数据共享?Android 提供了一种机制可以实现程序间的数据共享,它就是Android 四大组件之一ContentProvider,Android为存储和获取数据提供统一的接口,用于实现程序间数据共享,不要将其理解为数据库。

为什么说是熟悉又陌生呢?因为我们经常使用到,Android内置的许多数据都是采用ContentProvider,比如图片,视频,音频,手机联系人等,至于陌生那是因为我很少自己去实现一个ContentProvider,今天我们重点是来实现一个自定义ContentProvider。

ContentProvider类简介:

1.) 我们一般要继承ContentProvider,那么要实现那些函数呢?
  • ContentProvider()   构造函数
  • onCreate()    创建数据时调用的回调函数
  • insert()      插入数据
  • delete()     删除数据
  • update()    更新数据
  • query()      查询数据
  • getType()  得到数据类型
2.)URI简介:

ContentProvider通过URI来访问数据执行增删改查的操作,一个完整的URI有 content://自定义ContentProvider/xxx数据库名称 

我们先声明一个作用域:

    //访问URI作用域
public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider";

对应URI举例说明一下:

  • content://com.whoislcj.testsqlite.personprovider/person   返回person所以数据
  • content://com.whoislcj.testsqlite.personprovider/person/10 返回id为10的person数据
3.)UriMatcher简介

主要用于匹配Uri,为什么要匹配Uri呢?通过上面的Uri举例可以看出操作域不一样,对于执行一个delete、update、query来说我们要获取参数参数执行不能对应操作。

使用:

  //定义一个UriMatcher类对象,用来匹配Uri的。
private static final UriMatcher uriMatcher;
//集合操作
public static final int INCOMING_COLLECTION = 1;
//单个ID操作
public static final int INCOMING_SIGNAL = 2;
static {
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路径,返回匹配码为1
uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2
uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#号为通配符
}
4.)ContentUris简介

ContentUris是对URI的操作类,比如获取URI路径里的参数,或者给URI拼接一个参数

举例说明:

  • long id = ContentUris.parseId(uri);//从uri中获取id
  • Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成该条数据完整的URI地址
5.)ContentResolver简介

ContentResolver主要用于为外部程序提供增删改查的操作函数,也可以注册观察者来监听数据的变化。

6.)自定义ContentProvider具体实现:
public class PersonProvider extends ContentProvider {
// DatabaseHelper操作句柄
private DBHelper dbHelper;
//访问URI
public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider";
// 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";
// 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头
public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person";
//定义一个UriMatcher类对象,用来匹配Uri的。
private static final UriMatcher uriMatcher;
//集合操作
public static final int INCOMING_COLLECTION = 1;
//单个ID操作
public static final int INCOMING_SIGNAL = 2;
static {
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路径,返回匹配码为1
uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.ljq.provider.personprovider/person/230路径,返回匹配码为2
uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#号为通配符
} public PersonProvider() {
} /**
* 回调函数,在ContentProvider创建的时候,就会运行
* 作用获取操作用户的句柄
*/
@Override
public boolean onCreate() {
//这里会调用 DBHelper的构造函数创建一个数据库;
dbHelper = new DBHelper(getContext());
return true;
} /**
* 执行插入数据函数
*
* @param uri
* @param values
* @return
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
//获取一个可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
//调用数据库的插入操作 也可以自己构造sql语句 执行 db.execSQL();相对比较麻烦
long rowId = db.insert(DBHelper.TABLE_NAME, "", values);
//判断是否插入成功
if (rowId > 0) {
Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成该条数据完整的URI地址
getContext().getContentResolver().notifyChange(uri, null);
return rowUri;
}
throw new SQLException("Failed to insert row" + uri);
} /**
* 删除数据操作
*
* @param uri
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//获取一个可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case INCOMING_COLLECTION:
//执行删除操作
count = db.delete(DBHelper.TABLE_NAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
case INCOMING_SIGNAL:
long id = ContentUris.parseId(uri);//从uri中获取id
String where = "id=" + id; // 删除指定id的记录
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; // 把其它条件附加上
count = db.delete(DBHelper.TABLE_NAME, where, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new SQLException("Failed to delete row " + uri);
}
//关闭数据库
db.close();
return count;
} /**
* 更新数据操作
*
* @param uri
* @param values
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//获取一个可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (uriMatcher.match(uri)) {
case INCOMING_COLLECTION:
//执行更新数据
count = db.update(DBHelper.TABLE_NAME, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
case INCOMING_SIGNAL:
long id = ContentUris.parseId(uri);//从uri中获取id
String where = "id=" + id; // 删除指定id的记录
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
//执行更新数据
count = db.update(DBHelper.TABLE_NAME, values, where, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
break;
default:
throw new SQLException("Failed to update row " + uri);
}
//关闭数据库
db.close();
return count;
} /**
* 查询操作
*
* @param uri
* @param projection
* @param selection
* @param selectionArgs
* @param sortOrder
* @return
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//获取一个可读的数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case INCOMING_COLLECTION:
//执行查询
cursor = db.query(DBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
break;
case INCOMING_SIGNAL:
long id = ContentUris.parseId(uri);//从uri中获取id
String where = "id=" + id; // 删除指定id的记录
where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
cursor = db.query(DBHelper.TABLE_NAME, projection, where, selectionArgs, null, null, sortOrder);
break;
default:
throw new SQLException("Failed to query " + uri);
}
return cursor;
} /**
* 该方法用于返回当前Url所代表数据的MIME类型。
*
* @param uri
* @return
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case INCOMING_COLLECTION:
return CONTENT_TYPE;
case INCOMING_SIGNAL:
return CONTENT_TYPE_ITME;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
}
7.)外部如何访问
        ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person");
//添加一条记录
ContentValues values = new ContentValues();
values.put("name", "whoislcj");
resolver.insert(uri, values); //更新一条数据
ContentValues updateValues = new ContentValues();
updateValues.put("name", "lcj");
//组合
resolver.update(uri, updateValues, "id=?", new String[]{"2"});
//单个
Uri updateIdUri = ContentUris.withAppendedId(uri, 5);
resolver.update(updateIdUri, updateValues, null, null);
//删除person表指定数据
Uri deleteIdUri = ContentUris.withAppendedId(uri, 5);
resolver.delete(deleteIdUri, null, null); //获取person表指定数据
Uri tempUri = ContentUris.withAppendedId(uri, 5);
Cursor cursor = resolver.query(tempUri, null, null, null, "id asc");
while (cursor.moveToNext()) {
Log.e("testContentProvider", "signal id=" + cursor.getInt(0) + ",name=" + cursor.getString(1));
}
cursor.close(); //获取person表中所有记录
cursor = resolver.query(uri, null, null, null, "id asc");
while (cursor.moveToNext()) {
Log.e("testContentProvider", "id=" + cursor.getInt(0) + ",name=" + cursor.getString(1));
}
cursor.close();
8.)如何监听数据变化

需要注册一个自定义的观察者,当时如下

     // 为uri的数据改变注册监听器
getContentResolver().registerContentObserver(
Uri.parse("content://com.whoislcj.testsqlite.personprovider/person"), true,
new Observer(new Handler())); // 提供方自定义的ContentOberver监听器
private final class Observer extends ContentObserver { public Observer(Handler handler) {
super(handler);
} @Override
public void onChange(boolean selfChange, Uri uri) {
// 查询发送邮箱中的短息(处于正在发送状态的短信放在发送箱)
Log.e("MainActivity", "onChange--->uri :" + uri.toString());
}
}

同样数据操作位置也需要执行如下代码

getContext().getContentResolver().notifyChange(uri, null);
9.)访问权限控制

声明读写自定义权限

    <permission android:name="com.whoislcj.testsqlite.personprovider.read" />
<permission android:name="com.whoislcj.testsqlite.personprovider.write" /> <uses-permission android:name="com.whoislcj.testsqlite.personprovider.read" />
<uses-permission android:name="com.whoislcj.testsqlite.personprovider.write" />

ContentProvider注册声明:

       <provider
android:name=".PersonProvider"
android:authorities="com.whoislcj.testsqlite.personprovider"
android:enabled="true"
android:exported="true"
android:readPermission="com.whoislcj.testsqlite.personprovider.read"
android:writePermission="com.whoislcj.testsqlite.personprovider.write">
</provider>

10.)关于getTpye

ContentProvider里面一个getType ()函数很多人不知道 这个干嘛的,接下来介绍一下,

    // 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";
// 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头
public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person"; /**
* 该方法用于返回当前Url所代表数据的MIME类型。
*
* @param uri
* @return
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case INCOMING_COLLECTION:
return CONTENT_TYPE;
case INCOMING_SIGNAL:
return CONTENT_TYPE_ITME;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}

假设我们在项目搞了一个联系人列表Activity,我们需要外面来访问这个Activity,首先看下这个Activity的注册声明:

<activity android:name=".TestActivity" android:icon="@mipmap/ic_launcher">
<intent-filter>
<action android:name="com.whoislcj.testsqlite.personprovider" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.dir/person" />
</intent-filter> </activity>

看到上面的mimeType:vnd.android.cursor.dir/person

外部如何启动呢:

    Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person");
Intent intent = new Intent();
intent.setAction("com.whoislcj.testsqlite.personprovider");
intent.setData(uri);
startActivity(intent);

这样以来系统会去调用你定义的ContentProvider中的getType,去匹配出相应的Activity来实现跳转。

Android探索之ContentProvider熟悉而又陌生的组件的更多相关文章

  1. 《android开发艺术探索》读书笔记(九)--四大组件

    接上篇<android开发艺术探索>读书笔记(八)--WindowManager No1: 四大组件除了BroadcastReceiver,其他三种组件都必须在AndroidManifes ...

  2. android中的ContentProvider实现数据共享

    为了在应用程序之间交换数据,android中提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API.当一个应用程序需要把自己的数据暴露给其他程序 ...

  3. Android零基础入门第78节:四大组件的纽带——Intent

    前面学习Activity时己经多次使用了 Intent,当一个Activity需要启动另一个Activity时, 程序并没有直接告诉系统要启动哪个Activity,而是通过Intent来表达自己的意图 ...

  4. Android热身:通过网络获取资源并更新UI组件

    Android热身:通过网络获取资源并更新UI组件 目标 点击"发送请求"按钮,下载某网页的html源码,并显示在TextView控件上:点击"清空",清除Te ...

  5. Android开发工程师文集-1 小时学会Widget小组件开发

    前言 大家好,给大家带来Android开发工程师文集-1 小时学会Widget小组件开发的概述,希望你们喜欢 学会用Widget (小组件) Widget小组件很方便,很快捷,可以个性化,自己定制,相 ...

  6. Android探索之旅 | AIDL原理和实例讲解

    轉載自http://www.jianshu.com/p/ef86f682a8f9 -- 作者 谢恩铭 转载请注明出处 前言 为使应用程序之间能够彼此通信,Android提供了IPC (Inter Pr ...

  7. Android探索之AIDL实现进程间通信

    前言: 前面总结了程序间共享数据,可以使用ContentProvider也可以使用SharedPreference,那么进程间怎么共享内存呢?Android系统中的进程之间不能共享内存,因此,需要提供 ...

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

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

  9. Android细笔记--ContentProvider

    Provider的不常见访问方式 Batch access:访问ContentProvider的一中模式,使用该模式可以同时对provider进行多个操作,且支持同时操作多个表.使用时首先构建一个Co ...

随机推荐

  1. Easyui之datagrid实现点击单元格修改单元格背景颜色

    前段时间有个需求中有点击datagrid的单元格实现某种事件,调用datagrid的onclickCell这个方法很容易实现,但是体验不好啊,完全不知道自己刚才点击的是哪个单元格,然后就尝试单击单元格 ...

  2. 【第一课】WEBIX 入门自学-介绍WEBIX

    Webix是跨浏览器.跨平台的JavaScript框架,使用JavaScript.CSS,HTML5技术构建交互式web应用程序.库中提供几十个完全可定制的组件,提供了JQuery集成和可以处理任何服 ...

  3. gulp的基本使用

    gulp 了解 首先我们了解一下什么是gulp, gulp是前端自动化构建工具,在开发过程中很多重复的任务,我们都可以正确的使用gulp来完成,gulp基于nodejs,使用gulp可以做很多事情 例 ...

  4. ZOJ1913 Euclid's Game (第一道简单的博弈题)

    题目描述: Euclid's Game Time Limit: 2 Seconds      Memory Limit: 65536 KB Two players, Stan and Ollie, p ...

  5. Python之路第一课Day11--随堂笔记(异步IO\数据库\队列\缓存之二)

    一.RabbitMQ队列 1.安装: a.官网: 安装 http://www.rabbitmq.com/install-standalone-mac.html b.安装python rabbitMQ ...

  6. javascript 执行上下文的理解

    首先,为什么某些函数以及变量在没有被声明以前就可以被使用,javascript引擎内部在执行代码以前到底做了些什么?这里,想信大家都会想到,变量声明提前这个概念: 但是,以下我要讲的是,声明提前的这个 ...

  7. Ubuntu中的快捷键

    Ubuntu中的许多操作在终端(Terminal)中十分的快捷,记住一些快捷键的操作更得心应手. 在Ubuntu中打开终端的快捷键是Ctrl+Alt+T.其他的一些常用的快捷键如下: 快捷键 功能 T ...

  8. WUI 前端组件

    为什么会有WUI前端组件,我们接触的UI组件如:YUI.EXTjs.EasyUI,这些组件虽然提供了丰富的UI,并且一定程度上缩短了开始时间,单这些组件提供的页面风格是统一的,我们的产品风格不可能像这 ...

  9. 使用DataList实现数据分页的技术

    今天做网站的时候,用到了分页技术,我把使用方法记录下来,以便日后查阅以及帮助新手朋友们. DataList控件可以按照列表的形式显示数据表中的多行记录,但是被显示的多行记录没有分页功能,使用起来不太方 ...

  10. Mysql修改root密码

    一.启动命令行,输入: taskkill /f /im mysqld.exe //关闭mysql 二.转入mysql的bin目录下 三.输入:mysqld --skip-grant-tables // ...