Android应用间数据共享之ContentProvider

一般在android应用中,数据都是在本应用沙盒以内的,其余外部应用不可以访问,那么若是一个应用须要访问另一个应用的数据,怎么办呢?那就把另一个应用的数据公布出来,好比android中的通信录数据,这些数据是以ContentProvider方式提供与其余应用访问的。 android

    那么咱们也能够定义本身的ContentProvider来使跨应用共享数据。数据具体的存贮方式能够为数据库、文件,持久化或非持久化存储的其余形式。在这里咱们仍是使用sqlite数据库存贮数据吧。 sql

老规矩,先来点基础知识。 数据库

一.基础知识

1:URI是什么?统一资源标识符,用来标识某一资源的 ide

一般一个Uri主要由以三部分组成:scheme、Authority、path
     1.scheme:ContentProvider(内容提供者)的scheme已经由Android系统规定为:content://
     2.主机名(或Authority):用于惟一标识这个ContentProvider,外部调用者能够根据这个标识来找到它。
     3.路径(path):能够用来表示咱们要操做的数据,路径的构建应根据业务而定,以下
      要操做persion表中id为5的记录,能够构建这样的路径:/persion/5
      要操做persion表中id为20的记录的name字段, persion/name/10
      要操做persion表中的全部记录,能够构建这样的路径:/persion
 使用Uri类中的parse()方法获取Uri: Uri uri = Uri.parse("content://com.dongzi/persion")
工具

 上面Uri的scheme      content:// this

            Authority:    com.dongzi spa

            path:             /contact .net


二、UriMatcher、ContentUrist和ContentResolver code

Android系统提供了两个用于操做Uri的工具类:UriMatcher 和ContentUris server

UriMatcher:用于匹配Uri:

static final int CODES=2;
static final int CODE=1;
static final String AUTHORITY="com.dongzi"; //受权 
static final UriMatcher uriMatcher; //Uri匹配 
static {
//注册匹配的Uri以及返回码 
 uriMatcher=new UriMatcher(UriMatcher.NO_MATCH); //不匹配任何路径返回-1 
 uriMatcher.addURI(AUTHORITY, "persion", CODES); //匹配content://com.dongzi/persion 返回2  uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1 
 }
 

ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
•         withAppendedId(uri, id)用于为路径加上ID部分
•         parseId(uri)方法用于从路径中获取ID部分

//为Uri添加ID 
Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
ContentUris.withAppendedId(uri, 1234); //生成后的Uri为:content://com.dongzi/person/1234 //获取Uri后面的ID 
 long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234")); //获得ID为:1234
 

ContentResolver提供了以下主要方法:

 
@Override public int delete(Uri arg0, String arg1, String[] arg2) { //该方法用于供外部应用从ContentProvider删除数据。  return 0;
    }
@Override public String getType(Uri uri) { //该方法用于返回当前Url所表明数据的MIME类型。  return null;
    }
@Override public Uri insert(Uri uri, ContentValues values) { //该方法用于供外部应用往ContentProvider添加数据。  return null;
    }
@Override public boolean onCreate() { //在其它应用第一次访问它时被建立。  return false;
    }
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { //该方法用于供外部应用从ContentProvider中获取数据。  return null;
    }
    @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { //该方法用于供外部应用更新ContentProvider中的数据。  return 0;
    }
 

这里主要说下Url所表明数据的MIME类型:

若是操做的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,

例如:要获得全部person记录的Uri为content://com.dongzi/person,那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。

若是要操做的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,

例如:获得id为1234的person记录,Uri为content://com.dongzi/person/1234,那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"。

使用ContentResolver操做ContentProvider中的数据

当外部应用须要对ContentProvider中的数据进行添加、删除、修改和查 询操做时,可使用ContentResolver 类来完成,要获取ContentResolver 对象,可使用Activity提供的getContentResolver()方法。ContentResolver提供了ContentProvider对应的增、删、改、查方法。

监听ContentProvider中数据的变化

若是咱们须要获得数据变化通知,可使用ContentObserver对数据(数据采用uri描述)进行通知更改以及监听。

 
//通知内容以及发生改变,同时注册了内容监听,监听到内容变化,就调用onChange方法
        this.getContext().getContentResolver().notifyChange(uri, new ContentObserver(new Handler()){
            public void onChange(boolean selfChange) {
                  //此处能够进行相应的业务处理
               }
        });

二.实战

说了那么多,是时候了解ContentProvider的使用了,咱们这里采用sqlite存贮数据。固然,咱们直接采用联系人数据不是更好?

1:首先咱们定义DBHelper继承SQLiteOpenHelper,并建表。

 

package com.dongzi;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

    static final String DB_NAME = "dongzi.db";
    static final int DB_VERSION = 1;
    static final String TABLE="persion";
    static final String TABLE_COLUMN_NAME="name";
    static final String TABLE_COLUMN_PHONE="phone";
    static final String CREATE_TABLE = "create table persion(id integer primary key autoincrement,name varchar(40) phone varchar(40))";
    static final String DRPO_TABLE="drop table if exists persion";
    public DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
     //这里实际上是比较版本,而后升级数据库的,好比说是增长一个字段,或者删除一个字段,或者增长表
     db.execSQL(DRPO_TABLE);
     onCreate(db);
    }

}

 

2:而后定义MyContentProvider继承ContentProvider,而且在类加载时候初始化UriMatcher匹配,以及受权AUTHORITY,同时,这个ContentProvider须要在AndroidManifest.xml中进行注册,并添加受权。

代码以下:

 

package com.dongzi;

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.net.Uri;
import android.text.TextUtils;

public class MyContentProvider extends ContentProvider {
   
    DBHelper dbHelper=null;
    //MIME类型
    static final String PERSIONS_TYPE="vnd.android.cursor.dir/person";
    static final String PERSION_ITEM_TYPE="vnd.android.cursor.item/person";
    //返回码
    static final int CODES=2;
    static final int CODE=1;
    //受权
    static final String AUTHORITY="com.dongzi";          //受权
    static final UriMatcher uriMatcher;                  //Uri匹配
    static {                                             //注册匹配的Uri以及返回码
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);  //不匹配任何路径返回-1
        uriMatcher.addURI(AUTHORITY, "persion", CODES);  //匹配content://com.dongzi/persion 返回2
        uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1
    }
   
    private void init(){
        //为Uri添加ID
        Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
        ContentUris.withAppendedId(uri, 1234);
        //生成后的Uri为:content://com.dongzi/person/1234
       
        //获取Uri后面的ID
        long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234"));
        //获得ID为:1234
    }
   
   
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //该方法用于供外部应用从ContentProvider删除数据。
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        int count=0;
        switch(uriMatcher.match(uri)){
        case CODES:
          count = db.delete(DBHelper.DB_NAME, selection, selectionArgs);
          break;
        case CODE:
            // 下面的方法用于从URI中解析出id,对这样的路径content://com.dongzi/persion/1234
            // 进行解析,返回值为10
            long id = ContentUris.parseId(uri);
            String where = "id=" + id;// 删除指定id的记录
            where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
            count = db.delete(DBHelper.DB_NAME, where, selectionArgs);
            break;
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
       
        }
        db.close();
        return count;
    }
    @Override
    public String getType(Uri uri) {
        //该方法用于返回当前Url所表明数据的MIME类型。
        switch(uriMatcher.match(uri)){
        case CODES:
         return PERSIONS_TYPE;               //这里CODES表明集合,故返回的是集合类型的MIME
        case CODE:
             return PERSION_ITEM_TYPE;
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
        }
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //该方法用于供外部应用往ContentProvider添加数据。
        SQLiteDatabase db= dbHelper.getWritableDatabase();
        long id=0;
        //匹配Uri
        switch(uriMatcher.match(uri)){
         //返回码
        case CODES:
            id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是记录的行号,主键为int,实际上就是主键值
            return ContentUris.withAppendedId(uri, id);
        case CODE:
            id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是记录的行号,主键为int,实际上就是主键值
            String path = uri.toString();
            return Uri.parse(path.substring(0, path.lastIndexOf("/"))+id); // 替换掉id
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
        }
    }
    @Override
    public boolean onCreate() {
        //在其它应用第一次访问它时被建立。
        dbHelper =new DBHelper(getContext());
        return false;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         //该方法用于供外部应用从ContentProvider中获取数据。
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        Cursor cursor=null;
        switch(uriMatcher.match(uri)){
        case CODES:
            cursor=db.query(DBHelper.DB_NAME, projection, selection, selectionArgs, null, null, sortOrder);
            break;
        case CODE:
               //下面的方法用于从URI中解析出id,对这样的路径content://com.dongzi/persion/1234
                // 进行解析,返回值为10
                long id = ContentUris.parseId(uri);
                String where = "id=" + id;// 获取指定id的记录
                where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
                cursor=db.query(DBHelper.DB_NAME, projection, where, selectionArgs, null, null, sortOrder);
                break;
            default:break;
        }
        return cursor;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //该方法用于供外部应用更新ContentProvider中的数据。
        return 0;
    }

}

若是咱们基本了解了上述说的基础知识,那么这些代码不难看懂,其实也很是简单,不直接操做DBHelper类,而是经过ContentProvider间接操做,而操做ContentProvider又是太经过
ContentResolver这个类,实现不一样应用均可以访问这些数据。

相关文章
相关标签/搜索