Android数据共享机制ContentProvider

一、简介
Android使用一种称为ContentProvider的概念来将数据抽象为服务,这种内容提供程序的理念看起来像启用了REST的数据提供程序。
要从ContentProvider检索数据或将数据保存到ContentProvider,需要使用一组类似REST的URI。例如,要从封装图书数据库的ContentProvider获取一组图书,需要使用以下形式的URI:
content://com.android.book.BookProvider/books
要从图书数据库获得指定图书(如编号为:88图书),则要使用以下类似URI:
content://com.android.book.BookProvider/books/88
设备上的任何应用程序都可以通过这些URI来访问或操作数据,所以在应用程序之间的数据共享上ContentProvider扮演着非常主要的角色。
二、Android内置的ContentProvider
Android中存在大量的内置ContentProvider,他们记录在SDK的android.provider包中,如:
Browser
Calllog
Contacts
  People
  Phones
  Photos
  Groups
MediaStore
  Audio
     Albums
     Artists
     Playlists
  Images
  Video
Settings
其中顶级项数据库,较低级的项是表。所以Browser、CAllog,MediaStore和Settings都是封装为ContentProvider的独立SQLite数据库.这些数据库通常具有扩展名.db。仅能从实现包访问,任何来自外部的访问都要通过ContentProvider接口。
三、ContentProvider架构
与网站一样,设备上的每个ContentProvider都会使用字符串注册本身,这个字符串类似网站域名,但称为:授权(Authority)。授权的注册则AndroidManifest.xml中进行。如:
<provider android:name="BookProvider" android:authorities="com.myCompany.BookProvider"/>
在进行了授权后,ContentProvider就拥有了以授权前缀开头的URI:
content://com.myCompany.BookProvider/
注意:Android内置的ContentProvider可能没有完全限定的授权名,只有在使用第三方提供的时才使用完全限定名。这也就是为什么有时候仅使用Contacts来引用联系人ContentProvider。
1、Android内容URI的结构
Android中的内容URI类似于Http URL,但他们以content://开头具有一下通用形式:
Content://*/*/
或
content://authority-name/path-segment1/path-segment2/etc......
2、Android MIME类型的结构
就像网站返回给指定URI的MIME类型一样,ContentProvider也负责返回给URI的MIME类型。MIME类型包括两部分:类型和子类型。如:
text/html
text/css
text/xml
application/pdf
application/vnd.ms-excel
类型和子类型对于他们所表示的内容必须是唯一的,且如果类型和子类型不是标准的,则需要在他们前面加上vnd。
3、使用URI读取数据
要从ContentProvider获取数据,需要使用该ContentProvider提供的URI。
如下:为从联系人提供程序获取单行联系人信息
Uri baseUri = Contacts.People.CONTENT_URI;
uri myPersonUri = baseUri.withAppendedPath(Contacts.People.CONTENT_URI,"80");
Cursor cur = manageQuery(myPersonUri,null,null,null);
在上面的基础上我们来看看,如何获取一个游标,这个游标包含来自contactsContentProvider的People表的一组特定的列。
String[] projection = new String[]{
    People._ID,
    People.NAME,
    People.NUMBER
};

Uri mContactsURi = Contacts.People.CONTENT_URI;
Cursor managedCursor = managedQuery(
    projection,
    null,
    Contacts.People.NAME + "ASC"
);
4、使用游标
在使用Android游标前,先了解一些关于游标的的知识。
游标是一个行集合;
读取数据之前,需要使用moveToFirst(),因为游标放在第一行之前;
需要知道列的名称和类型;
所有字段访问都基于列编号,所以必须首先将列名称转换为列编号;
游标可以随意移动;
可以使用游标获取行计数;
使用while循环导航游标:
if (cur.moveToFirst == false){
    return;
}
int nameColumnIndex = cur.getColumnIndex(People.NAME);
String name = cur.getString(nameColumnIndex);

while (cur.moveToNext()){
    //获取属性值
}
使用for循环导航游标
int nameColumn = cur.getColumnIndex(People.NAME);
int ploneColumn = cur.getColumnIndex(People.NUMBER);

for(cur.moveToFirst();!cur.isAfterLast();cur.moveToNext()){
    String name = cur.getString(nameColumn);
    String phoneNumber = cur.getString(phoneColumn);
}
5、使用Where子句
ContentProvider提供两种方式来传递where子句:
通过URI;
String noteUri = "content://com.google.provider.NotePad.notes.80";
Cursor managedCursor = managedQuery(noteUri,
    projection,//返回的列
    null,//where子句
    Contacts.People.NAME + "ASC"//排序方式
);
通过String子句与一组可替换的字符串数组参数的组合。
String noteUri = "content://com.google.provider.NotePad.notes";
Cursor managedCursor = managedQuery(noteUri,
    projection,//返回的列
    "_id=?",//where子句
    new String[]{80},
    Contacts.People.NAME + "ASC"//排序方式
);
6、插入记录
android使用ContentValues来保存将插入的单一记录的值,要插入记录,由android.content.ContentResolver使用URI插入记录。下面是个例子:
ContentValues values = new ContentValues();
values.put("title","new Note");
values.put("note","this is a new Note");
ContentResolver cr = activity.getContentResolver();
Uri uri = cr.insert(Notepad.Notes.CONTENT_URI,values);
7、更新和删除
更新类似插入,方法签名为:
int numberOfRowsUpdated = activity.getContentResolver().update(
   Uri uri,
   ContentValues values,
   String whereClause,
   String[] selectionArgs
);
类似的删除的方法为:
int numberOfRowsDeleted = activity.getContentResolver().delete(
   Uri uri,
   String whereClause,
   String[] selectionArgs
);
8、下面我们自己实现一个ContentProvider,并进行CRUD操作;
我们将按如下流程来实现:
(1)计划数据库、URI、列名称,创建元数据类来定义所有的这些元数据元素的常量;
(2)扩展抽象类ContentProvider
(3)实现方法:query、insert、update、delete和getType
(4)在描述文件中注册提供程序
具体代码如下:
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
</LinearLayout>
元数据类:(NotePad.java)
public class NotePad {
    //授权
    public static final String AUTHORITY = "com.google.provider.NotePad";
    private NotePad(){
        
    }
    public static final class Notes implements BaseColumns{
        private Notes(){
            
        }
        //uri = "content://" + AUTHORITY + "/notes"
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
        
        //新的MiMe类型-多个
        public static final String CONTENT_ITEM = "vnd.android.cursor.dir/vnd.google.note";
        //新的MiMe类型-单个
        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.note";
        
        //排序方式
        public static final String DEFAULT_SORT_ORDER = "modified DESC";
        
        //字段
        public static final String TITLE = "title";
        public static final String NOTE = "note";
        public static final String CREATEDDATE = "created";
        public static final String MODIFIEDDATE = "modified";
    }
}
扩展ContentProvider类(NotePadProvider.java)
public class NotePadProvider extends ContentProvider{
    private static final String TAG = "NotePadProvider";
    
    //数据库名
    private static final String DATABASE_NAME = "notepad.db";
    private static final int DATABASE_VERSION = 2;
    
    //表名
    private static final String TABLE_NAME = "notes";
    private static HashMap<String,String> noteProjectionMap;
    private static final int NOTES = 1;
    private static final int NOTE_ID = 2;
    
    //uri匹配器
    private static final  UriMatcher urimatcher;
    private DatabaseHelper openHelper;
    private static final String CREATE_TABLE = "CREATE TABLE "
                                                    + TABLE_NAME
                                                    + "(" +Notes._ID
                                                    + " INTEGER PRIMARY KEY,"
                                                    + Notes.TITLE + " TEXT,"
                                                    + Notes.NOTE + " TEXT,"
                                                    + Notes.CREATEDDATE + " INTEGER,"
                                                    + Notes.MODIFIEDDATE + " INTEGER" + ")";
    static{
        urimatcher = new UriMatcher(UriMatcher.NO_MATCH);
        urimatcher.addURI(NotePad.AUTHORITY, "notes",NOTES);
        urimatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
        
        //列别名
        noteProjectionMap = new HashMap<String,String>();
        noteProjectionMap.put(Notes._ID,Notes._ID);
        noteProjectionMap.put(Notes.TITLE, Notes.TITLE);
        noteProjectionMap.put(Notes.NOTE, Notes.NOTE);
        noteProjectionMap.put(Notes.CREATEDDATE, Notes.CREATEDDATE);
        noteProjectionMap.put(Notes.MODIFIEDDATE,Notes.MODIFIEDDATE);
    }
    
    private static class DatabaseHelper extends SQLiteOpenHelper{
        public DatabaseHelper(Context context){
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        //创建表
        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i(TAG, CREATE_TABLE);
            db.execSQL(CREATE_TABLE);
        }
        //更新数据库
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("drop table if exists notes");
            onCreate(db);
        }
        
    }
    //删除数据
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        
        return 0;
    }
    //获取MIME类型
    @Override
    public String getType(Uri uri) {
        switch (urimatcher.match(uri)){
            case NOTES:
                return Notes.CONTENT_ITEM;
            case NOTE_ID:
                return Notes.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("Unknow Uri" + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        if (urimatcher.match(uri) != NOTES){
            throw new IllegalArgumentException("Unknow Uri" + uri);
        }
        ContentValues contentValues;
        if (null != values){
            contentValues = new ContentValues(values);
        }else{
            contentValues = new ContentValues();
        }
        Long now = Long.valueOf(System.currentTimeMillis());
        if (!contentValues.containsKey(NotePad.Notes.CREATEDDATE)){
            contentValues.put(NotePad.Notes.CREATEDDATE, now);
        }
        if (contentValues.containsKey(NotePad.Notes.MODIFIEDDATE) == false){
            contentValues.put(NotePad.Notes.MODIFIEDDATE, now);
        }
        if (contentValues.containsKey(NotePad.Notes.TITLE) == false){
            Resources r = Resources.getSystem();
            contentValues.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled));
        }
        if (contentValues.containsKey(NotePad.Notes.NOTE) == false){
            contentValues.put(NotePad.Notes.NOTE, "");
        }
        SQLiteDatabase db = openHelper.getWritableDatabase();
        long rowId = db.insert(TABLE_NAME, Notes.NOTE, contentValues);
        System.out.println(rowId);
        if (rowId > 0){
            Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
            getContext().getContentResolver().notifyChange(noteUri, null);
            return noteUri;
        }
        throw new SQLException("insert row into failed " + uri);
    }

    @Override
    public boolean onCreate() {
        openHelper = new DatabaseHelper(getContext());
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        switch (urimatcher.match(uri)){
            case NOTES:
                qb.setTables(TABLE_NAME);
                qb.setProjectionMap(noteProjectionMap);
                break;
            case NOTE_ID:
                qb.setTables(TABLE_NAME);
                qb.setProjectionMap(noteProjectionMap);
                qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1));
                break;
            default:
                throw new IllegalArgumentException("Unknow Uri" + uri);
        }
        String orderBy;
        if (TextUtils.isEmpty(sortOrder)){
            orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
        }else{
            orderBy = sortOrder;
        }
        SQLiteDatabase db = openHelper.getReadableDatabase();
        Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
        cursor.setNotificationUri(getContext().getContentResolver(),uri);
        return cursor;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        return 0;
    }
}
活动类(ContentProviderTest.java)
public class ContentProviderTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        //插入数据
        ContentValues contentValues = new ContentValues();
        contentValues.put(NotePad.Notes.TITLE, "title1");
        contentValues.put(NotePad.Notes.NOTE, "NOTENOTE1");
        getContentResolver().insert(NotePad.Notes.CONTENT_URI, contentValues);
        contentValues.clear();
        contentValues.put(NotePad.Notes.TITLE, "title2");
        contentValues.put(NotePad.Notes.NOTE, "NOTENOTE2");
        getContentResolver().insert(NotePad.Notes.CONTENT_URI, contentValues);
        displayNote();
    }
    private void displayNote(){
        String[] columns = new String[]{NotePad.Notes._ID
                ,NotePad.Notes.TITLE
                ,NotePad.Notes.NOTE
                ,NotePad.Notes.CREATEDDATE
                ,NotePad.Notes.MODIFIEDDATE};
        Uri myUri = NotePad.Notes.CONTENT_URI;
        Cursor cursor = managedQuery(myUri, columns, null, null, null);
        if (cursor.moveToFirst()){
            String id = null;
            String title = null;
            do{
                id = cursor.getString(cursor.getColumnIndex(NotePad.Notes._ID));
                title = cursor.getString(cursor.getColumnIndex(NotePad.Notes.TITLE));
                Toast toast = Toast.makeText(this, "TITLE:" + id + "NOTES" + title, Toast.LENGTH_SHORT);
                toast.setGravity(Gravity.TOP|Gravity.CENTER, 0, 40);
                toast.show();
            }while(cursor.moveToNext());
        }
    }
}
在描述文件中注册提供程序:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test.contentProvider"
      android:versionCode="1"
      android:versionName="1.0">

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <provider android:name="NotePadProvider"
                  android:authorities="com.google.provider.NotePad"/>
        <activity android:name=".ContentProviderTest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <data android:mimeType="vnd.android.cursor.dir/vnd.google.note"></data>
            </intent-filter>
            <intent-filter>
                <data android:mimeType="vnd.android.cursor.item/vnd.google.note"></data>
            </intent-filter>
        </activity>

    </application>
</manifest>

运行结果如下图所示:



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值