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這個類,實現不一樣應用均可以訪問這些數據。

相關文章
相關標籤/搜索