ContentProvider的实现及定义自己的ContentProvider

Android 应用程序能够将它们的数据保存到文件、SQLite 数据库中,甚至是任何有效的设备中。当你想将你的应用数据与其它的应用共享时,内容提供器就可以发挥作用了。因为内容提供器类实现了一组标准的方法,从而 能够让其它的应用保存或读取此内容提供器处理的各种数据类型。数据是应用的核心。在Android 中,默认使用鼎鼎大名的SQLite 作为系统DB。但是在Android 中,使用方法有点小小的不一样。在Android 中每一个应用都运行在各自的进程中,当你的应用需要访问其他应用的数据时,也就需要数据在不同的虚拟机之间传递,这样的情况操作起来可能有些困难(正常情 况下,你不能读取其他的应用的db 文件),ContentProvider 正是用来解决在不同的应用包之间共享数据的工具。所有被一个Android 应用程序创建的偏好设置,文件和数据库都是私有的。为了和其他应用程序共享数据,应用程序不得不创建一个Content Provider要回索其他应用程序的数据。

如果需要创建一个Content Provider,则需要进行的工作主要分为以下3个步骤。

(1)  建立数据的存储系统
数据的存储系统可以由开发人员任意决定,一般来讲,大多数的Content Provider都通过Android的文件存储系统或SQLite 数据库建立自己的数据存储系统。

(2)扩展 ContentProvider类
开发一个继承自ContentProvider类的 子类代码来扩展 ContentProvider类,在这个步骤主要的工作是将要共享的数据包装并以ContentResolver 和 Cursor对象能够访问到的形式对外展示。具体来说需要实现ContentProvider 类中的6个抽象方法。
Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):将查询的数据以Cursor 对象的形式返回。
Uri insert(Uri uri, ContentValues values):向 Content Provider中插入新数据记录,ContentValues 为数据记录的列名和列值映射。
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新Content Provider中已存在的数据记录。
int delete(Uri uri, String selection, String[] selectionArgs):从Content Provider中删除数据记录。
String getType(Uri uri):返回Content Provider中的数据( MIME )类型。
boolean onCreate():当 Content Provider 启动时被调用。
以上方法将会在ContentResolver 对象中调用,所以很好地实现这些抽象方法会为ContentResolver提供一个完善的外部接口。除了实现抽象方法外,还可以做一些提高可用性的工作。
定义一个 URI 类型的静态常量,命名为CONTENT_URI。 必须为该常量对象定义一个唯一的URI字符串,一般的做法是将 ContentProvider子类的全称类名作为URI字符串,如:
“content://wyf.wpf.MyProvider”。
定义每个字段的列名,如果采用的数据库存储系统为SQLite 数据库,数据表列名可以采用数据库中表的列名。不管数据表中有没有其他的唯一标识一个记录的字段,都应该定义一个”_id”字段 来唯一标识一个记录。模式使用 “INTEGER PRIMARY KEY AUTOINCREMENT” 自动更新 一般将这些列名字符串定义为静态常量, 如”_id”字段名定义为一个名为”_ID”  值为 “_id” 的静态字符串对象。

(3)在应用程序的AdnroidManifest.xml 文件中声明Content Provider组件。
创建好一个Content Provider必须要在应用程序的AndroidManifest.xml 中进行声明,否则该Content Provider对于 Android系统将是不可见的。如果有一个名为MyProvider的类扩展了 ContentProvider类,声明该组件的代码如下:

1
2
<provider android:name="com.lpq.FirstContentProvider"
            android:authorities="com.lpq.FirstContentProvider" />

其中name属性为ContentProvider 子类的全称类名,authorities 属性唯一标识了一个ContentProvider。还可以通过 setReadPermission() 和 setWritePermission() 来设置其操作权限。当然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission属性来控制其权限。
注意:因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。

下面我们来看一个具体的例子:

FirstProviderMetaData.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.lpq;

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

public class FirstProviderMetaData {
    public static final String AUTHORITY = "com.lpq.FirstContentProvider";
    // 数据库名称
    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";
        // 访问该ContentProvider的URI
        public static final Uri CONTENT_URI = Uri.parse("content://"
                + AUTHORITY + "/users");
        // 该contentProvider所返回的数据类型的定义
        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";
        // 列名
        public static final String USER_NAME = "name";
        // 默认的排序方法
        public static final String DEFAULT_SORT_ORDER = "_id desc";
    }
}

FirstContentProvider.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package com.lpq;

import java.util.HashMap;

import ycitss.cp.FirstProviderMetaData.UserTableMetaData;
import ycitss.sqlite3.db.DatabaseHelper;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class FirstContentProvider extends ContentProvider {

    // 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 {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(FirstProviderMetaData.AUTHORITY, "users",
                INCOMING_USER_COLLECTION);
        uriMatcher.addURI(FirstProviderMetaData.AUTHORITY, "users/#",
                INCOMING_USER_SINGLE);
    }

    public static HashMap<String, String> userProjectionMap;
    static {
        userProjectionMap = new HashMap<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) {
        // TODO Auto-generated method stub
        System.out.println("delete...");
        SQLiteDatabase db = dh.getWritableDatabase();
        int count;  
        switch (sUriMatcher.match(uri)) {  
        case INCOMING_USER_COLLECTION:  
            count = db.delete(UserTableMetaData.TABLE_NAME, selection, selectionArgs);  
            break;  
             
        case INCOMING_USER_SINGLE:  
            String _id = uri.getPathSegments().get(1);  
            count = db.delete(UserTableMetaData.TABLE_NAME, UserTableMetaData._ID + "=" + _id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);  
            break;  
             
        default:  
            throw new IllegalArgumentException("Unnown URI" + uri);  
        }  
        getContext().getContentResolver().notifyChange(uri, null);  
        return count;  
    }

    // 根据传入的URI,返回该URI所表示的数据类型
    @Override
    public String getType(Uri uri) {
        System.out.println("getType...");
        switch (uriMatcher.match(uri)) {
        case INCOMING_USER_COLLECTION:
            return UserTableMetaData.CONTENT_TYPE;
        case INCOMING_USER_SINGLE:
            return UserTableMetaData.CONTENT_TYPE_ITEM;
        default:
            throw new IllegalArgumentException("Unknow URI " + uri);
        }
    }

    /**
     * 该函数的返回值是一个Uri,这个Uri表示的是刚刚那使用这个函数所插入的数据
     * content://ycitss.cp.firstContentProivder/users/1
     */

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        System.out.println("insert...");
        SQLiteDatabase db = dh.getWritableDatabase();
        long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
        if (rowId > 0) {
            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创建的时候执行
    @Override
    public boolean onCreate() {
        dh = new DatabaseHelper(getContext(),
                FirstProviderMetaData.DATABASE_NAME);
        System.out.println("onCreate...");
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        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);
            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();
        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) {
        System.out.println("update...");
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
        int count;  
        switch (sUriMatcher.match(uri)) {  
        case INCOMING_USER_COLLECTION:  
            count = db.update(UserTableMetaData.TABLE_NAME, values, selection, selectionArgs);  
            break;  
          INCOMING_USER_SINGLE
        case NOTE_ID:  
            String _id = uri.getPathSegments().get(1);  
            count = db.update(UserTableMetaData.TABLE_NAME, values, UserTableMetaData._ID + "=" + _id + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);  
            break;  
             
        default:  
            throw new IllegalArgumentException("Unknow URI " + uri);  
        }  
         
        return count;  
    }

}

测试Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
insertButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                ContentValues values = new ContentValues();
                values.put(FirstProviderMetaData.UserTableMetaData.USER_NAME,
                        "zhangsan");
                Uri uri = getContentResolver().insert(
                        FirstProviderMetaData.UserTableMetaData.CONTENT_URI,
                        values);
                System.out.println("uri  ---> " + uri.toString());
            }
        });
        queryButton = (Button) findViewById(R.id.queryButton);
        queryButton.setOnClickListener(new View.OnClickListener() {

            @Override
            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(UserTableMetaData.USER_NAME)));
                }

            }
        });

当然还要记得配置Provider。

运行效果如下:

QQ截图20131114090410

除非注明,Coder文章均为原创,转载请以链接形式标明本文地址

本文地址:http://www.alonemonkey.com/android-contentprovider.html

本文链接:http://www.alonemonkey.com/android-contentprovider.html