Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统提供了像SharedPreferences这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性。

ContentProvider(数据提供者)是应用程序之间共享数据的一种接口机制,是一种更为高级的数据共享方法。

  • ContentProvider可以指定需要共享的数据,而其他应用程序则可以在不知道数据来源、路径的情况下,对共享数据进行增删改查等操作。
  • 在Android系统中,许多Android系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等。

ContentProvider机理

-

调用关系

在创建ContentProvider前,首先要实现底层的数据源,数据源包括数据库、文件系统或网络等,然后继承ContentProvider类中实现基本数据操作的接口函数。调用者不能直接调用ContentProvider的接口函数,需要通过ContentResolver对象,通过URI间接调用ContentProvider。
-

ContentResolver对象与ContentProvider的交互

在ContentResolver对象与ContentProvider进行交互时,通过URI确定要访问的ContentProvider数据集。在发起的一个请求的过程中,Android系统根据URI确定处理这个查询的ContentProvider,然后初始化ContentProvider所有需要的资源,这个初始化的工作是Android系统完成的,无需我们参与。一般情况下只有一个ContentProvider对象,但却可以同时与多个ContentResolver进行交互。

-

ContentProvider的屏蔽性

ContentProvider完全屏蔽了底层数据源的数据存储方法。数据提供者通过ContentProvider提供了一组标准的数据操作接口,但却无须知道数据提供者的内部数据的存储方法。数据提供者可以使用SQLite数据库存储数据,也可以通过文件系统或SharedPreferences存储数据,甚至是使用网络存储的方法,这些数据的存储方法和存储设备对数据使用者都是不可见的。同时,也正是这种屏蔽模式,很大程度上简化了ContentProvider的使用方法,使用者只要调用ContentProvider提供的接口函数,即可完成所有的数据操作,而数据存储方法则是ContentProvider设计者需要考虑的问题。

- ContentProvider提供的数据形式

ContentProvider的数据集类似于数据库的数据表,每行是一条记录,每列具有相同的数据类型。每条记录都包含一个长整型的字段 _ID,用来唯一标识每条记录。ContentProvider可以提供多个数据集,调用者使用URI对不同数据集的数据进行操作。

-

通用资源标识符(Uniform Resource Identifier)

URI是一个用于标识某一互联网资源名称的字符串。 该种标识允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。在ContentProvider机制中,使用ContentResolver对象通过URI定位ContentProvider提供的资源。
ContentProvider使用的URI语法结构如下:

    content://<authority>/<data_path>/<id>
  • content:// 是通用前缀,表示该UIR用于ContentProvider定位资源。
  • < authority > 是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般< authority >都由类的小写全称组成,以保证唯一性。
  • < data_path > 是数据路径,用来确定请求的是哪个数据集。如果ContentProvider近提供一个数据集,数据路径则可以省略;如果ContentProvider提供多个数据集,数据路径必须指明具体数据集。数据集的数据路径可以写成多段格式,例如people/girl和people/boy。
  • < id > 是数据编号,用来唯一确定数据集中的一条记录,匹配数据集中_ID字段的值。如果请求的数据不只一条,< id >可以省略。

如请求整个people数据集的URI为:

content://com.example.peopleprovider/people

而请求people数据集中第3条数据的URI则应写为:

content://com.example.peopleprovider/people/3

创建数据提供者

1. 创建一个类让其继承ContentProvider,并重载6个函数

  • onCreate()
    一般用来初始化底层数据集和建立数据连接等工作

  • getType()
    用来返回指定URI的MIME数据类型,若URI是单条数据,则返回的MIME数据类型以vnd.android.cursor.item开头;若URI是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。

  • insert()、delete()、update()、query()
    用于对数据集的增删改查操作。

2. 声明CONTENT_URI,实现UriMatcher

示例:

public static final String AUTHORITY = "com.example.peopleprovider";
public static final String PATH_SINGLE = "people/#";
public static final String PATH_MULTIPLE = "people";
public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);
public static final int MULTIPLE_PEOPLE = 1;
public static final int SINGLE_PEOPLE = 2;
public static final UriMatcher uriMatcher;
static{uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(AUTHORITY, PATH_SINGLE, SINGLE_PEOPLE );uriMatcher.addURI(AUTHORITY, PATH_MULTIPLE , MULTIPLE_PEOPLE );
}

其中UriMatcher类引用官方文档中的解释:

Utility class to aid in matching URIs in content providers.

可见UriMatcher本质上是一个文本过滤器,用在contentProvider中帮助我们过滤,分辨出查询者想要查询哪个数据表。
UriMatcher的构造函数中,UriMatcher.NO_MATCH是URI无匹配时的返回代码,值为-1。 addURI() 方法用来添加新的匹配项,语法为:

public void addURI(String authority, String path, int code)

其中authority表示匹配的授权者名称,path表示数据路径(#代表任何数字),code表示返回代码。

关于UriMatcher的使用

switch(uriMatcher.match(uri)){case MULTIPLE_PEOPLE://多条数据的处理break;case SINGLE_PEOPLE://单条数据的处理break;default:throw new IllegalArgumentException("不支持的URI:" + uri);
}

3. 注册ContentProvider

在AndroidManifest.xml文件中的 application节点下使用< provider >标签注册。示例:

<provider
            android:authorities="com.example.peopleprovider"android:name=".Peopleprovider" />

上例中注册了一个授权者名称为com.example.peopleprovider的ContentProvider,其实现类为 Peopleprovider 。

使用数据提供者

每个Android组件都有一个ContentResolver对象,通过调用getContentResolver() 方法可得到ContentResolver对象。

准备工作:

ContentResolver resolver = getContentResolver();
String KEY_ID = "_id";
String KEY_NAME = "name";
String KEY_AGE = "age";
String KEY_HEIGHT = "height";

1. 添加操作

通过insert()函数添加单条数据
(returns : the URL of the newly created row.)

    ContentValues values = new ContentValues();values.put(KEY_NAME, "Tom");values.put(KEY_AGE, 21);values.put(KEY_HEIGHT, 1.81f);Uri newUri = resolver.insert(CONTENT_URI, values);

通过bulkInsert()函数添加多条数据
(returns:the number of newly created rows.)

ContentValues[] arrayValues = new ContentValues[10];
//实例化每一个ContentValues...
int count = resolver.bulkInsert(CONTENT_URI, arrayValues );

2. 删除操作

指定ID删除单条数据

Uri uri = Uri.parse(CONTENT_URI_STRING + "/" +"2");
int result = resolver.delete(uri, null, null);

通过selection语句删除多条数据

String selection = KEY_ID + ">4";
int result = resolver.delete(CONTENT_URI, selection, null);

3. 更新操作

    ContentValues values = new ContentValues();values.put(KEY_NAME, "Tom");values.put(KEY_AGE, 21);values.put(KEY_HEIGHT, 1.81f);Uri rui = Uri.parse(CONTENT_URI_STRING + "/" + "7");int result = resolver.update(uri, values, null, null);

4. 查询操作

Uri uri = Uri.parse(CONTENT_URI_STRING + "/" + "2");
Cursor cursor = resolver.query(uri, new String[]{KEY_ID, KEY_NAME, KEY_AGE, KEY_HEIGHT}, null, null, null);

在URI中定义了需要查询数据的ID后,在query()函数中没有必要再加入其他的查询条件,如果要获取数据集全部数据,则可以直接使用CONTENT_URI且不加查询条件。
在Android系统中,数据库查询结果的返回值并不是数据集合的完整拷贝,而是返回数据集的指针,这个指针就是Cursor类。ContentProvider的数据集类似数据库的数据表,其查询结果的返回值同样是数据集的指针:Cursor类。在提取Cursor数据中的数据前,推荐测试Cursor中的数据数量,避免在数据获取中产生异常。示例如下:

public people[] getPeople(Cursor cursor){int resultCounts = cursor.getCount();if(resultCounts == 0 !cursor.moveToFirst()){return null;}People[] peoples = new People[resultCounts];for(int i=0; i<resultCounts; i++){peoples[i] = new People();peoples[i].ID = cursor.getInt(0);peoples[i].Name = cursor.getString(cursor.getColumnIndex(KEY_NAME));peoples[i].Age = cursor.getInt(cursor.getColumnIndex(KEY_AGE));peoples[i].Height= cursor.getFloat(cursor.getColumnIndex(KEY_HEIGHT));cursor.moveToNext();}return peoples;
}

ContentProvider Demo

Demo结构如下:

People.java

public class People {public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";public static final String MIME_ITEM = "vnd.example.people";public static final String MIME_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MIME_ITEM ;public static final String MIME_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MIME_ITEM ;public static final String AUTHORITY = "com.example.peopleprovider";public static final String PATH_SINGLE = "people/#";public static final String PATH_MULTIPLE = "people";public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING);public static final String KEY_ID = "_id";public static final String KEY_NAME = "name";public static final String KEY_AGE = "age";public static final String KEY_HEIGHT = "height";}

PeopleProvider.java

package com.example.contentproviderdemo;import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.annotation.Nullable;/*** Created by yinghao on 2016/5/3.*/
public class PeopleProvider extends ContentProvider {private static final String DB_NAME="people.db";private static final String DB_TABLE="peopleinfo";private static final int DB_VERSION = 1;private SQLiteDatabase db;private DBOpenHelper dbOpenHelper;private static final int MULTIPLE_PEOPLE = 1;private static final int SINGLE_PEOPLE = 2;private static final UriMatcher uriMatcher ;static {uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(People.AUTHORITY, People.PATH_MULTIPLE, MULTIPLE_PEOPLE);uriMatcher.addURI(People.AUTHORITY, People.PATH_SINGLE, SINGLE_PEOPLE);}@Overridepublic boolean onCreate() {Context context = getContext();dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);db = dbOpenHelper.getWritableDatabase();if(db == null){return false;}else{return true;}}@Nullable@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {SQLiteQueryBuilder qb = new SQLiteQueryBuilder();qb.setTables(DB_TABLE);switch (uriMatcher.match(uri)){case SINGLE_PEOPLE:qb.appendWhere(People.KEY_ID+"="+uri.getPathSegments().get(1));break;default:break;}Cursor cursor = qb.query(db,projection,selection,selectionArgs,null,null,sortOrder);cursor.setNotificationUri(getContext().getContentResolver(), uri);return cursor;}@Nullable@Overridepublic String getType(Uri uri) {switch (uriMatcher.match(uri)){case MULTIPLE_PEOPLE:return People.MIME_TYPE_MULTIPLE;case SINGLE_PEOPLE:return People.MIME_TYPE_SINGLE;default:throw new IllegalArgumentException("Unkown uro:"+uri);}}@Nullable@Overridepublic Uri insert(Uri uri, ContentValues values) {long id =db.insert(DB_TABLE, null, values);if(id>0){Uri newUri = ContentUris.withAppendedId(People.CONTENT_URI,id);getContext().getContentResolver().notifyChange(newUri, null);return newUri;}throw new SQLException("failed to insert row into " + uri);}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {int count = 0;switch (uriMatcher.match(uri)){case MULTIPLE_PEOPLE:count = db.delete(DB_TABLE, selection, selectionArgs);break;case SINGLE_PEOPLE:String segment = uri.getPathSegments().get(1);count = db.delete(DB_TABLE, People.KEY_ID + "=" + segment, selectionArgs);break;default:throw new IllegalArgumentException("Unsupported URI:" + uri);}getContext().getContentResolver().notifyChange(uri,null);return count;}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {int count;switch (uriMatcher.match(uri)){case MULTIPLE_PEOPLE:count = db.update(DB_TABLE, values, selection, selectionArgs);break;case SINGLE_PEOPLE:String segment = uri.getPathSegments().get(1);count = db.update(DB_TABLE, values, People.KEY_ID + "=" + segment, selectionArgs);break;default:throw new IllegalArgumentException("Unknow URI: " + uri);}getContext().getContentResolver().notifyChange(uri, null);return count;}private static class DBOpenHelper extends SQLiteOpenHelper {private static final  String DB_CREATE = "create table "+DB_TABLE+"("+People.KEY_ID+" integer primary key autoincrement, "+People.KEY_NAME+" text not null, "+People.KEY_AGE+" integer, "+People.KEY_HEIGHT+" float);";public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(DB_CREATE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);onCreate(db);}}}

MainActivity.java

package com.example.contentresolverdemo;import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private EditText nameText;private EditText ageText;private EditText heightText;private EditText idEntry;private TextView labelView;private TextView displayView;private Button add;private Button queryAll;private Button clear;private Button del;private Button query;private Button deleteAll;private Button update;private ContentResolver resolver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);resolver = this.getContentResolver();initView();initEvent();}private void initEvent() {add.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ContentValues values = new ContentValues();values.put(People.KEY_NAME, nameText.getText().toString());values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString()));values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString()));Uri newUri = resolver.insert(People.CONTENT_URI, values);labelView.setText("添加成功,URI:" + newUri);}});queryAll.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Cursor cursor = resolver.query(People.CONTENT_URI,new String[]{People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT},null, null, null);if (cursor == null) {labelView.setText("数据库中没有数据");return;}labelView.setText("数据库:" + String.valueOf(cursor.getCount()) + "条记录");String msg= "";if (cursor.moveToFirst()) {do {msg += "ID: " + cursor.getString(cursor.getColumnIndex(People.KEY_ID)) + ",";msg += "姓名: " + cursor.getString(cursor.getColumnIndex(People.KEY_NAME)) + ",";msg += "年龄: " + cursor.getInt(cursor.getColumnIndex(People.KEY_AGE)) + ",";msg += "身高: " + cursor.getFloat(cursor.getColumnIndex(People.KEY_HEIGHT)) + ",";} while (cursor.moveToNext());}displayView.setText(msg);}});deleteAll.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {resolver.delete(People.CONTENT_URI, null, null);String msg = "数据全部删除";labelView.setText(msg);}});update.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ContentValues values = new ContentValues();values.put(People.KEY_NAME, nameText.getText().toString());values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString()));values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString()));Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString());int result = resolver.update(uri, values, null, null);String msg = "更新ID为" + idEntry.getText().toString() + "的数据" + (result > 0 ? "成功" : "失败");labelView.setText(msg);}});}private void initView() {}
}

ContentProviderDemo的AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.contentproviderdemo"><application
        android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><provider
            android:authorities="com.example.peopleprovider"android:name=".PeopleProvider"/></application></manifest>

ContentResolverDemo的AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.contentresolverdemo"><application
        android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

对于以上Demo其中的细节:(API 23)

  • SQLiteQueryBuilder

public class
SQLiteQueryBuilder
extends Object

This is a convience class that helps build SQL queries to be sent to
SQLiteDatabase objects.

  • uri.getPathSegments()

public abstract List getPathSegments ()
Added in API level 1
Gets the decoded path segments.
Returns
decoded path segments, each without a leading or trailing ‘/’

  • uri.getPathSegments().get(position)

public abstract E get (int location)
Added in API level 1
Returns the element at the specified location in this List.
Parameters
location
the index of the element to return.
Returns
the element at the specified location.
Throws
IndexOutOfBoundsException
if location < 0 || location >= size()

Android ContentProvider 完全解析及简单DEMO相关推荐

  1. Spring Boot自动装配过程解析及简单Demo演示

    文章目录 1.约定大于配置 2.自动装配原理 2.1.`@SpringBootApplication` 2.2.`@EnableAutoConfiguration` 2.3.`@Import` 2.4 ...

  2. Android json数据解析及简单例子

    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据 ...

  3. 动态规划解析以及简单demo

    简介 在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果.因此各个阶段决策的选取不能任意确定,它依赖于当前 ...

  4. ContentProvider总结与简单Demo

    一.ContentProvider简介         当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.虽然使用其他方法也可以对外共享数 ...

  5. android:layout 冒号,android-json解析及简单例子(补汉6个汉字字).pdf

    android json 解析及简单例子 JSON 的定义: 一 种轻量级的数据交换格式,具有良好的可读和便于快速 编写的特性.业内主流技术为其提供了完整的解决方案(有 点类似于正则表达式 ,获得了当 ...

  6. Android音频开发(五)如何存储和解析最简单的音频wav文件

    我们大家都知道,无论是文字.图像还是声音,都必须以一定的格式来组织和存储起来,然后其它的软件再以相同的协议规则,相应的格式才能去打开解析这一段数据,例如,对于原始的图像数据,我们常见的格式有 YUV. ...

  7. Android——ContentProvider详解

    1. 简介 ContentProvider,内容提供者属于Android的四大组件之一 用于进程间 进行数据交互 & 共享,即跨进程通信 原理:使用binder机制(后续再进行介绍 统一资源标 ...

  8. 【Android 修炼手册】常用技术篇 -- Android 热修复解析

    这是[Android 修炼手册]第 8 篇文章,如果还没有看过前面系列文章,欢迎点击 这里 查看- 预备知识 了解 android 基本开发 了解 ClassLoader 相关知识 看完本文可以达到什 ...

  9. Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示...

    Android JSON原生解析的几种思路,以号码归属地,笑话大全,天气预报为例演示 今天项目中要实现一个天气的预览,加载的信息很多,字段也很多,所以理清了一下思路,准备独立出来写一个总结,这样对大家 ...

最新文章

  1. Redis配置不当可导致服务器被控制,已有多个网站受到影响 #通用程序安全预警#...
  2. java中xml怎样配置_如何通过XML方式配置并实现Mybatis
  3. 特性(property)
  4. ORACLE审计初步入门
  5. 微软软件保护平台 白皮书.pdf
  6. 【深度学习】CornerNet: 将目标检测问题视作关键点检测与配对
  7. 不是每个人都适合linux
  8. @Value 注入静态变量
  9. android 调用系统方法,android 调用系统功窗口方法
  10. Node.js listen EADDRINUSE 错误解决 How to solve nodejs Error: listen EADDRINUSE
  11. 第18天:京东网页头部制作
  12. secureCRT下载地址,亲测有效
  13. Automation Anywhere, Blue Prism, UiPath, Pega, 码栈功能对比大全
  14. linux怎么cat结果导出txt,cat 输出文件内容
  15. encode()和decode()编码与解码的解析、常用编码与为何要encode和decode
  16. 高等数学---第一章导数定义的考法
  17. [PED01]Deep Subspace Clustering Networks
  18. html自动适应屏幕分辨率,css如何设置适配不同分辨率屏幕?
  19. 应用实践 | Apache Doris 在网易互娱的应用实践
  20. FRP + 腾讯云 内网穿透

热门文章

  1. 用Android-X86和VMware打造高性能Android开发环境
  2. 段码液晶屏是如何生产的1?
  3. 佩服,主动让自己不舒服的人
  4. 【WebGIS】Openlayers流动线与风场效果
  5. 网络摄像机的网络连接
  6. 《互联网DSP广告揭秘——精准投放与高效转化之道》一一1.9 程序化交易广告的前世今生 ...
  7. 【MM32F5270开发板试用】快速移植STM32应用到MM32F5270(以OLED为例)
  8. HDU - 5546
  9. 网络安全学习day1
  10. js-控制浏览器和移动端的后退按钮 . popstate