Android之ContentProvider总结

1.适用场景

1) ContentProvider为存储和读取数据提供了统一的接口

2) 使用ContentProvider,应用程序可以实现数据共享

3) android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等)

2.相关概念介绍

1)ContentProvider简介
       当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。

2)Uri类简介

      Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

      在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如select, add, delete, modify等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。以下是一些示例URI:

     content://media/internal/images  这个URI将返回设备上存储的所有图片
     content://contacts/people/  
这个URI将返回设备上的所有联系人信息
     content://contacts/people/45
这个URI返回单个结果(联系人信息中ID45的联系人记录)

  尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面content://contacts/people/45这个URI就可以写成如下形式:

  Uri person = ContentUris.withAppendedId(People.CONTENT_URI,  45);

然后执行数据查询:

Cursor cur = managedQuery(person, null, null, null);

这个查询返回一个包含所有数据字段的游标,我们可以通过迭代这个游标来获取所有的数据:

package com.wissen.testApp;
public class ContentProviderDemo extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
displayRecords();
}

private void displayRecords() {
//该数组中包含了所有要返回的字段
String columns[] = new String[] { People.NAME, People.NUMBER };
Uri mContacts = People.CONTENT_URI;
Cursor cur = managedQuery(
mContacts,
columns, // 要返回的数据字段
  null, // WHERE子句
  null, // WHERE 子句的参数
   null // Order-by子句
);
if (cur.moveToFirst()) {
String name = null;
String phoneNo = null;
do {
// 获取字段的值
   name = cur.getString(cur.getColumnIndex(People.NAME));
phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();
} while (cur.moveToNext());
}
}
}

 

  上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。

修改记录:
我们可以使用ContentResolver.update()方法来修改数据,我们来写一个修改数据的方法:

private void updateRecord(int recNo, String name) {
Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
ContentValues values = new ContentValues();
values.put(People.NAME, name);
getContentResolver().update(uri, values, null, null);
}

现在你可以调用上面的方法来更新指定记录:

updateRecord(10, ”XYZ”);   //更改第10条记录的name字段值为“XYZ”

添加记录:
要增加记录,我们可以调用ContentResolver.insert()方法,该方法接受一个要增加的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。
上面的例子中我们都是基于联系人信息簿这个标准的Content Provider,现在我们继续来创建一个insertRecord() 方法以对联系人信息簿中进行数据的添加:

private void insertRecords(String name, String phoneNo) {
ContentValues values = new ContentValues();
values.put(People.NAME, name);
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
Log.d(”ANDROID”, uri.toString());
Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.NUMBER, phoneNo);
getContentResolver().insert(numberUri, values);
}

这样我们就可以调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。


删除记录:
Content Provider中的getContextResolver.delete()方法可以用来删除记录,下面的记录用来删除设备上所有的联系人信息:

private void deleteRecords() {
Uri uri = People.CONTENT_URI;
getContentResolver().delete(uri, null, null);
}

你也可以指定WHERE条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除name为‘XYZ XYZ’的记录。

3. 创建ContentProvider

要创建我们自己的Content Provider的话,我们需要遵循以下几步:
a. 创建一个继承了ContentProvider父类的类

b. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:
public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

c. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。

d. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。

e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,通过它来读取对应的文件数据。处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源;如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。

f. 声明public static String型的变量,用于指定要从游标处返回的数据列。

g. 查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。

h. 在AndroidMenifest.xml中使用<provider>标签来设置Content Provider。

i. 如果你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。MIME类型有两种形式:一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种常用的格式:

  vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的MIME类型)
  比如, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

  vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的MIME类型)
  比如, 一个请求所有列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。

下列代码将创建一个Content Provider,它仅仅是存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据):

public class MyUsers {
public static final String AUTHORITY = “com.wissen.MyContentProvider”;

// BaseColumn类中已经包含了 _id字段
public static final class User implements BaseColumns {
public static final Uri CONTENT_URI = Uri.parse(”content://com.wissen.MyContentProvider”);
// 表数据列
public static final String USER_NAME = “USER_NAME”;
}
}

上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面我们将定义基于上面的类来定义实际的Content Provider类:

public class MyContentProvider extends ContentProvider {
private SQLiteDatabase sqlDB;
private DatabaseHelper dbHelper;
private static final String DATABASE_NAME = “Users.db”;
private static final int DATABASE_VERSION= 1;
private static final String TABLE_NAME= “User”;
private static final String TAG = “MyContentProvider”;

private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
//创建用于存储数据的表
db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME);
onCreate(db);
}
}

@Override
public int delete(Uri uri, String s, String[] as) {
return 0;
}

@Override
public String getType(Uri uri) {
return null;
}

@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
sqlDB = dbHelper.getWritableDatabase();
long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues);
if (rowId > 0) {
Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
getContext().getContentResolver().notifyChange(rowUri, null);
return rowUri;
}
throw new SQLException(”Failed to insert row into ” + uri);
}

@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
return (dbHelper == null) ? false : true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
SQLiteDatabase db = dbHelper.getReadableDatabase();
qb.setTables(TABLE_NAME);
Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}

@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
return 0;
}
}

一个名为MyContentProvider的Content Provider创建完成了,它用于从Sqlite数据库中添加和读取记录。

Content Provider的入口需要在AndroidManifest.xml中配置:

<provider android:name=”MyContentProvider” android:authorities=”com.wissen.MyContentProvider” />

之后,让我们来使用这个定义好的Content Provider:

1)为应用程序添加ContentProvider的访问权限。

2)通过getContentResolver()方法得到ContentResolver对象。

3)调用ContentResolver类的query()方法查询数据,该方法会返回一个Cursor对象。

4)对得到的Cursor对象进行分析,得到需要的数据。

5)调用Cursor类的close()方法将Cursor对象关闭。

 

public class MyContentDemo extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
insertRecord(”MyUser”);
displayRecords();
}

private void insertRecord(String userName) {
ContentValues values = new ContentValues();
values.put(MyUsers.User.USER_NAME, userName);
getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
}

private void displayRecords() {
String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
Uri myUri = MyUsers.User.CONTENT_URI;
Cursor cur = managedQuery(myUri, columns,null, null, null );
if (cur.moveToFirst()) {
String id = null;
String userName = null;
do {
id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show();
} while (cur.moveToNext());
}
}
}

android Content provider 组件

Content Provider 属于Android应用程序的组件之一,作为应用程序之间唯一的共享数据的途径,Content Provider 主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的借口。

    Android 系统为一些常见的数据类型(如音乐、视频、图像、手机通信录联系人信息等)内置了一系列的 Content Provider, 这些都位于android.provider包下。持有特定的许可,可以在自己开发的应用程序中访问这些Content Provider。

   让自己的数据和其他应用程序共享有两种方式:创建自己的Content Provier(即继承自ContentProvider的子类)  或者是将自己的数据添加到已有的Content Provider中去,后者需要保证现有的Content Provider和自己的数据类型相同且具有该 Content Provider的写入权限。对于Content Provider,最重要的就是数据模型(data model) 和 URI。
  
   1.数据模型
    Content Provider 将其存储的数据以数据表的形式提供给访问者,在数据表中每一行为一条记录,每一列为具有特定类型和意义的数据。每一条数据记录都包括一个 "_ID" 数值字段,改字段唯一标识一条数据。

   2.URI
    URI,每一个Content Provider 都对外提供一个能够唯一标识自己数据集(data set)的公开URI, 如果一个Content Provider管理多个数据集,其将会为每个数据集分配一个独立的URI。所有的Content Provider 的URI 都以"content://" 开头,其中"content:"是用来标识数据是由Content Provider管理的 schema。

      在几乎所有的Content Provider 的操作中都会用到URI,因此一般来讲,如果是自己开发的Content Provider,最好将URI定义为常量,这样在简化开发的同时也提高了代码的可维护性。

      首先来介绍如何访问Content Provider中的数据,访问 Content Provider中的数据主要通过ContentResolver对象,ContentResolver类提供了成员方法可以用来对Content Provider 中的数据进行查询、插入、修改和删除等操作。 以查询为例,查询一个 Content Provider 需要掌握如下的信息。

      唯一标识Content Provider 的URI
      需要访问的数据字段名称。
      该数据字段的数据类型

  提示: 如果需要访问特定的某条数据记录,只需该记录的ID 即可。

    查询Content Provider的方法有两个:ContentResolver的query() 和 Activity 对象的 managedQuery(),二者接收的参数均相同,返回的都是Cursor 对象,唯一不同的是 使用managedQuery 方法可以让Activity 来管理 Cursor 的生命周期。

    被管理的Cursor 会在 Activity进入暂停状态的时候调用自己的 deactivate 方法自行卸载,而在Activity回到运行状态时会调用自己的requery 方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用Activity的 startManagingCursor方法来实现。

 

Android应用程序可以使用文件或SqlLite数据库来存储数据。Content Provider提供了一种多应用间数据共享的方式,比如:联系人信息可以被多个应用程序访问。Content Provider是个实现了一组用于提供其他应用程序存取数据的标准方法的类。

应用程序可以在Content Provider中执行如下操作:
查询数据

修改数据

添加数据

删除数据

 

/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/MainActivity.java

代码

 

/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/Employees.java

代码
package com.amaker.ch10.app;

import android.net.Uri;
import android.provider.BaseColumns;

/**
* 通讯录常量类
*/
publicfinalclass Employees {
// 授权常量
publicstaticfinal String AUTHORITY ="com.amaker.provider.Employees";
private Employees() {}
// 内部类
publicstaticfinalclass Employee implements BaseColumns {
// 构造方法
private Employee() {}
// 访问Uri
publicstaticfinal Uri CONTENT_URI = Uri.parse("content://"+ AUTHORITY +"/employee");
// 内容类型
publicstaticfinal String CONTENT_TYPE ="vnd.android.cursor.dir/vnd.amaker.employees";
publicstaticfinal String CONTENT_ITEM_TYPE ="vnd.android.cursor.item/vnd.amaker.employees";

// 默认排序常量
publicstaticfinal String DEFAULT_SORT_ORDER ="name DESC";// 按姓名排序
// 表字段常量
publicstaticfinal String NAME ="name"; // 姓名
publicstaticfinal String GENDER="gender"; // 性别
publicstaticfinal String AGE ="age"; // 年龄
}
}

/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/EmployeeProvider.java

 

代码
package com.amaker.ch10.app;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

import com.amaker.ch10.app.Employees.Employee;

publicclass EmployeeProvider extends ContentProvider{
// 数据库帮助类
private DBHelper dbHelper;
// Uri工具类
privatestaticfinal UriMatcher sUriMatcher;
// 查询、更新条件
privatestaticfinalint EMPLOYEE =1;
privatestaticfinalint EMPLOYEE_ID =2;
// 查询列集合
privatestatic HashMap<String, String> empProjectionMap;
static {
// Uri匹配工具类
sUriMatcher =new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Employees.AUTHORITY,
"employee", EMPLOYEE);
sUriMatcher.addURI(Employees.AUTHORITY,
"employee/#", EMPLOYEE_ID);
// 实例化查询列集合
empProjectionMap =new HashMap<String, String>();
// 添加查询列
empProjectionMap.put(Employee._ID, Employee._ID);
empProjectionMap.put(Employee.NAME, Employee.NAME);
empProjectionMap.put(Employee.GENDER, Employee.GENDER);
empProjectionMap.put(Employee.AGE, Employee.AGE);
}

// 创建是调用
publicboolean onCreate() {
// 实例化数据库帮助类
dbHelper =new DBHelper(getContext());
returntrue;
}
// 添加方法
public Uri insert(Uri uri, ContentValues values) {
// 获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 插入数据,返回行ID
long rowId = db.insert(DBHelper.EMPLOYEES_TABLE_NAME, Employee.NAME, values);
// 如果插入成功返回uri
if (rowId >0) {
Uri empUri
= ContentUris.withAppendedId(Employee.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(empUri,
null);
return empUri;
}
returnnull;
}
// 删除方法
publicint delete(Uri uri, String selection, String[] selectionArgs) {
// 获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 获得数据库实例
int count;
switch (sUriMatcher.match(uri)) {
// 根据指定条件删除
case EMPLOYEE:
count
= db.delete(DBHelper.EMPLOYEES_TABLE_NAME, selection, selectionArgs);
break;
// 根据指定条件和ID删除
case EMPLOYEE_ID:
String noteId
= uri.getPathSegments().get(1);
count
= db.delete(DBHelper.EMPLOYEES_TABLE_NAME, Employee._ID +"="+ noteId
+ (!TextUtils.isEmpty(selection) ?" AND ("+ selection +')' : ""), selectionArgs);
break;

default:
thrownew IllegalArgumentException("错误的 URI "+ uri);
}
getContext().getContentResolver().notifyChange(uri,
null);
return count;
}

// 获得类型
public String getType(Uri uri) {
returnnull;
}

// 查询方法
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb
=new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
// 查询所有
case EMPLOYEE:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
break;
// 根据ID查询
case EMPLOYEE_ID:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
qb.appendWhere(Employee._ID
+"="+ uri.getPathSegments().get(1));
break;
default:
thrownew IllegalArgumentException("Uri错误! "+ uri);
}

// 使用默认排序
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy
= Employee.DEFAULT_SORT_ORDER;
}
else {
orderBy
= sortOrder;
}

// 获得数据库实例
SQLiteDatabase db = dbHelper.getReadableDatabase();
// 返回游标集合
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}

// 更新方法
publicint update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// 获得数据库实例
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
// 根据指定条件更新
case EMPLOYEE:
count
= db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, selection, selectionArgs);
break;
// 根据指定条件和ID更新
case EMPLOYEE_ID:
String noteId
= uri.getPathSegments().get(1);
count
= db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, Employee._ID +"="+ noteId
+ (!TextUtils.isEmpty(selection) ?" AND ("+ selection +')' : ""), selectionArgs);
break;
default:
thrownew IllegalArgumentException("错误的 URI "+ uri);
}
getContext().getContentResolver().notifyChange(uri,
null);
return count;
}

}

 

 

 

/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/DBHelper.java

代码

 

/Chapter10_ContentProvider_01_Test02/AndroidManifest.xml

代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package
="com.amaker.ch10.app"
android:versionCode
="1"
android:versionName
="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">

<provider android:name="EmployeeProvider"
android:authorities
="com.amaker.provider.Employees"/>

<activity android:name=".MainActivity"
android:label
="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="3"/>

</manifest>

 

Android学习十九:ContentProvider初步

一、Content Provider基本概念 

1ContentProvider为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。使用表的形式来组织数据。

Android学习十九:ContentProvider初步

2、使用ContentProvider可以在不同的应用程序之间共享数据。 

3Android为常见的一些数据提供了默认的ContentProvider包括音频、视频、图片和通讯录等   

ContentProvider所提供的函数

query(),insert(),update(),delete(),getType(),onCreate()等。

 二、URI统一资源标识符)的使用方法

为系统的每一个资源给其一个名字,比方说通话记录。

1、每一个ContentProvider都拥有一个公共的URI这个URI用于表示这个ContentProvider所提供的数据。 

2Android所提供的ContentProvider都存放在android.provider包中。 将其分为ABCD 4个部分:

Android学习十九:ContentProvider初步

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"

 BURI 的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称;"content://hx.android.text.myprovider"

C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"

D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id

三、ContentProvider的实现过程

自己实现ContentProvider不常见因为可能不需要和别的应用程序交换数据。使用内置的ContentProvider比较多 

    1、定义一个CONTENT_URI常量,提供了访问ContentProvider的标识符。 

 publicstaticfinalUri CONTENT_URI =Uri.parse("content://com.example.codelab.transportationprovider"); 

其中:content是协议

      Com.exmaple.codelab.transportationprovider是类名,包含完整的包名。

Uri.parse将一个字符串转换成Uri类型。

如果Provider包含子表,同样定义包含字表的CONTENT_URI

 content://com.example.codelab.transportationprovider/train  
 content://com.example.codelab.transportationprovider/air/domestic  
 content://com.example.codelab.transportationprovider/air/international 

然后定义列,确保里面包含一个_id的列。

    2、定义一个类继承ContentProvider 

public class FirstContentProvider extends ContentProvider

先介绍一下ContentProvider用到的UriMatcherUriMatcher的一个重要的函数是match(Uri uri)。这个函数可以匹配Uri,根据传入的不同Uri返回不同的自定义整形值,以表明Uri访问的不同资源的类型。

      例如

       public static final UriMatcher uriMatcher;   
       static {   
                      uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);   
                      uriMatcher.addURI(Book.AUTHORITY, "item", Book.ITEM);   
                      uriMatcher.addURI(Book.AUTHORITY, "item/#", Book.ITEM_ID);   
               }  

      这里UriMatcher类型的静态字段是用来匹配传入到ContentProvider中的Uri的类。其构造方法传入的匹配码是使用match()方法匹配根路径时返回的值,这个匹配码可以为一个大于零的数表示匹配根路径或传入-1,即常量UriMatcher.NO_MATCH表示不匹配根路径。 addURI()方法是用来增加其他URI匹配路径的,第一个参数传入标识ContentProviderAUTHORITY字符串。第二个参数传入需要匹配的路径,这里的#号为通配符,代表匹配任意数字,另外还可以用*来匹配任意文本。第三个参数必须传入一个大于零的匹配码,用于match()方法对相匹配的URI返回相对应的匹配码。 例如:sMatcher.addURI(com.test.provider.personprovider,person, 1);如果match()方法匹配content://com.test.provider.personprovider/person路径,返回匹配码为1

    3、实现query,insert,update,delete,getTypeonCreate方法。 

    4、在AndroidManifest.xml当中进行声明。

 <!-- android:name是完成ContentProvider类的全称 
              android:authorities是和FirstProvidermetaData中的常量AUTHORITY的值一样否则会报错 
          --> 
         <provider android:name="com.bj.FirstContentProvider" 
             android:authorities="com.bj.firstcontentprovider" 
             /> 

 

 

四、具体代码

Activity19Activity.java

public class Activity19Activity extends Activity {

   

    private Button queryButton = null;

    private Button insertButton = null;

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);

       setContentView(R.layout.main);

       queryButton = (Button) this.findViewById(R.id.query);

       queryButton.setOnClickListener(newQueryListener());

       insertButton = (Button) this.findViewById(R.id.insert);

       insertButton.setOnClickListener(newInsertListener());

        System.out.println(getContentResolver().getType(FirstProvidermetaData.UserTableMetaData.CONTENT_URI));

    }

 

    class InsertListener implementsOnClickListener {

 

       @Override

       public void onClick(View v) {

           // TODOAuto-generated method stub

           ContentValues values = new ContentValues();

           values.put(FirstProvidermetaData.UserTableMetaData.USER_NAME,

                  "michal");

           Uri uri = getContentResolver()

                  .insert(

                         FirstProvidermetaData.UserTableMetaData.CONTENT_URI,

                         values);

           System.out.println("uri--->" + uri.toString());

       }

 

    }

 

    class QueryListener implementsOnClickListener {

       public void onClick(View v) {

           Cursor c = getContentResolver().query(

                  FirstProvidermetaData.UserTableMetaData.CONTENT_URI, null,

                  null, null, null);

           while (c.moveToNext()) {

              System.out.println(c.getString(c.getColumnIndex("username")));

 

           }

 

       }

    }

}

FirstContentProvider.java

public class FirstContentProvider extendsContentProvider {

    // 当别的程序来访问这个ContentProvider,是通过Uri来访问的,UriMatcher检查是否符合标准

    // uri起一个规则,返回数字

    public static final UriMatcher uriMatcher;

    // 下面定义两个规则

    public static final int INCOMING_USER_COLLECTION = 1;

    public static final int INCOMING_USER_SINGLE = 2;

    private DatabaseHelper dh;

    static {//下面的users前面不能加/

       uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

       uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, "users",

              INCOMING_USER_COLLECTION);

       uriMatcher.addURI(FirstProvidermetaData.AUTHORITY, "users/#",

              INCOMING_USER_SINGLE);

    }

   

    //有点类似于sql里面表的别名,这个也是给列其别名,必须要用

    //列的别名还是原来的名,没必要修改

    public static HashMap<String,String> userProjectionMap;

    static{

       userProjectionMap = newHashMap<String,String>();

        userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID );

       userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);

    }

 

    @Override

    public int delete(Uri uri, String selection, String[] selectionArgs) {

       // TODOAuto-generated method stub

       System.out.println("delete");

       return 0;

    }

 

    // 根据传入的URI,返回该URI所表示的数据类型

    // 也就是说,我们通过URI要访问的数据,返回什么类型

    @Override

    public String getType(Uri uri) {

       // TODOAuto-generated method stub

       System.out.println("getType");

       switch (uriMatcher.match(uri)) {

       case INCOMING_USER_COLLECTION:

           // UserTableMetaDataFirstProvidermetaData的内部类

           return UserTableMetaData.CONTENT_TYPE;

       case INCOMING_USER_SINGLE:

           return UserTableMetaData.CONTENT_TYPE_ITEM;

       default:

           throw new IllegalArgumentException("Unknown uri" + uri);

       }

    }

 

   

    @Override

    public Uri insert(Uri uri, ContentValues values) {

       // TODOAuto-generated method stub

       System.out.println("insert");

       SQLiteDatabase db = dh.getWritableDatabase();

       //返回表中自动增长的列的值,否则返回-1      

       long rowId = db.insert(UserTableMetaData.TABLE_NAME,null, values);   

       if(rowId>0){

           //rowId追加到后面

           //contentUris:用来处理Uri的工具类

           Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);

           //通知监听器,数据已经改变

           getContext().getContentResolver().notifyChange(insertedUserUri, null);

           return insertedUserUri;

       }

       throw new SQLException("Failed to insert row into "+uri);

    }

   

    //是一个回调方法,所以说在ContentProvider创建的时候执行

    //也就是创建这个DatabaseHelper对象

    @Override

    public boolean onCreate() {

       // TODOAuto-generated method stub

       //getContext得到当前正在运行着的context

       dh = new DatabaseHelper(getContext(),FirstProvidermetaData.DATABASE_NAME);

       System.out.println("on create");

        SQLiteDatabase db = dh.getReadableDatabase();

       return true;

    }

   

    //projection:查询的列有哪些

    //selection:where子句的内容,可以用?

    //selectionArgs:占位符对应的参数

    //sortOrder:排序

    @Override

    public Cursor query(Uri uri, String[] projection, String selection,

           String[] selectionArgs, String sortOrder) {

       // TODOAuto-generated method stub

       System.out.println("query");

       //创建一个查询的语句

       SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

       switch(uriMatcher.match(uri)){

       case INCOMING_USER_COLLECTION:

           //设置查询哪张表

           qb.setTables(UserTableMetaData.TABLE_NAME);

           qb.setProjectionMap(userProjectionMap);

           break;

       case INCOMING_USER_SINGLE:

           qb.setTables(UserTableMetaData.TABLE_NAME);

           qb.setProjectionMap(userProjectionMap);

           //添加where条件,getPathSegments:得到uripath部分content:XXX/user/1get(1)得到1

           qb.appendWhere(UserTableMetaData._ID+"="+uri.getPathSegments().get(1));

           break;

       }

      

       String orderBy;

       if(TextUtils.isEmpty(sortOrder)){

           orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;

       }

       else

       {

           orderBy = sortOrder;

       }

       SQLiteDatabase db = dh.getWritableDatabase();

       //下面的query使用qb这个对象

       Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);

       //也是通知下

       c.setNotificationUri(getContext().getContentResolver(), uri);

       System.out.println("query");

       return c;

    }

 

    @Override

    public int update(Uri uri, ContentValues values, String selection,

           String[] selectionArgs) {

       // TODOAuto-generated method stub

       return 0;

    }

 

}

FirstProvidermetaData.java

 

public class FirstProvidermetaData {

    public static final String AUTHORITY="com.bj.firstcontentprovider"; //继承了contentprovider的类的全名

    //数据库名称 

    public static final String DATABASE_NAME = "FirstProvider.db"; 

    //数据库的版本  

    public static final int DATABASE_VERSION = 1; 

    //表名  

    public static final String USERS_TABLE_NAME = "users"; 

   

    public static final class UserTableMetaData implements BaseColumns{

       //表名

       public static final String TABLE_NAME="users";

       //访问该ContentProviderURI

       public static final Uri CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/users");

      

       public static final String CONTENT_TYPE="vnd.android.cursor.dir/vnd.firstprovider.user";

       public static final String CONTENT_TYPE_ITEM="vnd.android.cursor.item/vnd.firstprovider.user";

       //列名,在users表中添加一个名为name的列    

       public static final String USER_NAME="name";

       //默认排序方式

       public static final String DEFAULT_SORT_ORDER="_id desc";

    }

 

}

 

posted on 2012-07-02 12:00  清沁  阅读(2043)  评论(3编辑  收藏  举报