創(chuàng)建自己的內容提供器

一、創(chuàng)建內容提供器的步驟


  1. 新建 MyProvider 繼承自 ContentProvider鸟整。復寫幾個方法。
  2. 借助 UriMatcher 匹配內容 URI,得知傳入的 Uri 對象是想訪問哪張表中的哪條數(shù)據来屠。
  3. 在 MyProvider 的幾個方法中用 switch 判斷 uriMatcher.match(uri),根據不同 Uri 的目的震鹉,把目的定位到相應表或行俱笛。
  4. 明確目的后,具體靠對 SQLiteDatabase 進行操作完成增刪改查传趾。
  5. 記住要在 AndroidManifest.xml 里注冊迎膜。

1. 新建 MyProvider 繼承自 ContentProvider。復寫幾個方法浆兰。

public class MyProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
        String[] selectionArgs) {
        return 0;
    }

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

    @Override
    public String getType(Uri uri) {
        return null;
    }
}
1. onCreate()
  • 初始化內容提供器的時候調用磕仅,通常會在這里完成對數(shù)據庫的創(chuàng)建和升級等操作.
  • 返回 true 表示內容提供器初始化成功珊豹,返回 false 則表示失敗。
  • 注意榕订,只有當存在 ContentResolver 嘗試訪問我們程序中的數(shù)據時店茶,內容提供器才會被初始化。
2. query()
  • 從內容提供器中查詢數(shù)據劫恒。
  • 使用 uri 參數(shù)來確定查詢哪張表贩幻,projection 參數(shù)用于確定查詢哪些列,selection 和 selectionArgs 參數(shù)用于約束查詢哪些行兼贸,sortOrder 參數(shù)用于對結果進行排序段直。
  • 查詢的結果存放在 Cursor 對象中返回。
3. insert()
  • 向內容提供器中添加一條數(shù)據溶诞。
  • 使用 uri 參數(shù)來確定要添加到的表鸯檬,待添加的數(shù)據保存在 values 參數(shù)中。
  • 添加完成后螺垢,返回一個用于表示這條新記錄的 URI喧务。
4. update()
  • 更新內容提供器中已有的數(shù)據。
  • 使用 uri 參數(shù)來確定更新哪一張表中的數(shù)據枉圃,新數(shù)據保存在 values 參數(shù)中功茴,selection 和 selectionArgs 參數(shù)用于約束更新哪些行。
  • 受影響的行數(shù)將作為返回值返回孽亲。
5. delete()
  • 從內容提供器中刪除數(shù)據坎穿。
  • 使用 uri 參數(shù)來確定刪除哪一張表中的數(shù)據,selection 和 selectionArgs 參數(shù)用于約束刪除哪些行返劲。
  • 被刪除的行數(shù)將作為返回值返回玲昧。
6. getType()
  • 根據傳入的內容 URI 來返回相應的 MIME 類型
    一個內容 URI 所對應的 MIME 字符串主要由三部分組分:
  1. 必須以 vnd. 開頭篮绿。
  2. 如果內容 URI 以路徑結尾孵延,則后接 android.cursor.dir/,如果內容 URI 以 id 結尾亲配,則后接 android.cursor.item/尘应。
  3. 最后接上 vnd.<authority>.<path>。
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
vnd.android.cursor.item/vnd.com.example.app.provider.table1
  • 實現(xiàn) getType() 邏輯 :
public class MyProvider extends ContentProvider {
......
   @Override
   public String getType(Uri uri) {
       switch (uriMatcher.match(uri)) {
           case TABLE1_DIR:
               return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
           case TABLE1_ITEM:
               return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
           case TABLE2_DIR:
               return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
           case TABLE2_ITEM:
               return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
           default:
               break;
       }
       return null;
   }
}

2. 借助 UriMatcher 匹配內容 URI,得知傳入的 Uri 對象是想訪問哪張表中的哪條數(shù)據思灰。

  • 能夠匹配任意表的內容 URI:
content://com.example.app.provider/*

能夠匹配 table1 表中任意一行數(shù)據的內容 URI:

content://com.example.app.provider/table1/#
  • UriMatcher 中提供了一個 addURI() 方法玷犹,接收三個參數(shù),可以分別把權限官辈、路徑和一個自定義代碼傳進去箱舞。
  • 當調用 UriMatcher 的 match() 方法時,就可以將一個 Uri 對象傳入拳亿,返
    回值是某個能夠匹配這個 Uri 對象所對應的自定義代碼晴股。
  • 利用這個代碼,我們就可以判斷出調用方期望訪問的是哪張表或哪一行中的數(shù)據了肺魁。
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;

private static UriMatcher uriMatcher;

static {
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
    uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
    uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
    uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
}

3. 在 MyProvider 的幾個方法中用 switch 判斷 uriMatcher.match(uri)电湘,根據不同 Uri 的目的,把目的定位到相應表或行鹅经。

public class MyProvider extends ContentProvider {
    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;

    private static UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
        uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
        uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_ITEM);
        uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
    }
......
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
        switch (uriMatcher.match(uri)) {
            case TABLE1_DIR:
                // 查詢table1表中的所有數(shù)據
                break;
            case TABLE1_ITEM:
                // 查詢table1表中的單條數(shù)據
                break;
            case TABLE2_DIR:
                // 查詢table2表中的所有數(shù)據
                break;
            case TABLE2_ITEM:
                // 查詢table2表中的單條數(shù)據
                break;
            default:
                break;
        }
......
    }
......
}

安全問題:

所有的 CRUD 操作都一定要匹配到相應的內容 URI 格式才能進行的寂呛,而我們當然不可能向 UriMatcher 中添加隱私數(shù)據的 URI,所以這部分數(shù)據根本無法被外部程序訪問到瘾晃,安全問題也就不存在了贷痪。

4. 明確目的后,具體靠對 SQLiteDatabase 進行操作完成增刪改查蹦误。

public class DatabaseProvider extends ContentProvider {
    public static final int BOOK_DIR = 0;
    public static final int BOOK_ITEM = 1;
    public static final int CATEGORY_DIR = 2;
    public static final int CATEGORY_ITEM = 3;
    public static final String AUTHORITY = "com.example.databasetest.provider";
    private static UriMatcher uriMatcher;
    private MyDatabaseHelper dbHelper;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    @Override
    public boolean onCreate() {dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
        String[] selectionArgs, String sortOrder) {
        // 查詢數(shù)據
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                cursor = db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("Book", projection, "id = ?", new String[]{ bookId }, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("Category", projection, "id = ?", new String[] { categoryId }, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // 添加數(shù)據
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long newCategoryId = db.insert("Category", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
                break;
            default:
                break;
        }
        return uriReturn;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // 更新數(shù)據
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updatedRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                updatedRows = db.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1); 
                updatedRows = db.update("Book", values, "id = ?", new String[] { bookId });
                break;
            case CATEGORY_DIR:
                updatedRows = db.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("Category", values, "id = ?", new String[] { categoryId });
                break;
            default:
                break;
        }
        return updatedRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 刪除數(shù)據
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deletedRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                deletedRows = db.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Book", "id = ?", new String[] { bookId });
                break;
            case CATEGORY_DIR:
                deletedRows = db.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Category", "id = ?", new String[] { categoryId });
                break;
            default:
                break;
        }
        return deletedRows;
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.databasetest. provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.databasetest. provider.category";
        }
        return null;
    }
}
  • getPathSegments() 方法
    它會將內容 URI 權限之后的部分以 “/” 符號進行分割劫拢,并把分割后的結果放入到一個字符串列表中,這個列表的第 0 個位置存放的就是路徑强胰,第 1 個位置存放的就是 id舱沧。

5. 記住要在 AndroidManifest.xml 里注冊。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.databasetest"
    android:versionCode="1"
    android:versionName="1.0" >
......
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
......
        <provider
            android:name="com.example.databasetest.DatabaseProvider"
            android:authorities="com.example.databasetest.provider" >
        </provider>

    </application>
</manifest>

二偶洋、使用自己創(chuàng)建的內容提供器


public class MainActivity extends Activity {
    private String newId;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 添加數(shù)據
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                ContentValues values = new ContentValues();
                values.put("name", "A Clash of Kings");
                values.put("author", "George Martin");
                values.put("pages", 1040);
                values.put("price", 22.85);
                Uri newUri = getContentResolver().insert(uri, values);
                newId = newUri.getPathSegments().get(1);
            }
        });
        Button queryData = (Button) findViewById(R.id.query_data);
        queryData.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 查詢數(shù)據
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
                Cursor cursor = getContentResolver().query(uri, null, null, null, null);
                if (cursor != null) {
                    while (cursor.moveToNext()) {
                        String name = cursor.getString(cursor.getColumnIndex("name"));
                        String author = cursor.getString(cursor.getColumnIndex("author"));
                        int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                        double price = cursor.getDouble(cursor.
                        getColumnIndex("price"));
                        Log.d("MainActivity", "book name is " + name);
                        Log.d("MainActivity", "book author is " + author);
                        Log.d("MainActivity", "book pages is " + pages);
                        Log.d("MainActivity", "book price is " + price);
                    }
                    cursor.close();
                }
            }
        });
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // 更新數(shù)據
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
                ContentValues values = new ContentValues();
                values.put("name", "A Storm of Swords");
                values.put("pages", 1216);
                values.put("price", 24.05);
                getContentResolver().update(uri, values, null, null);
            }
        });
        Button deleteData = (Button) findViewById(R.id.delete_data);
        deleteData.setOnClickListener(new OnClickListener() {
            @Override
                public void onClick(View v) {
                // 刪除數(shù)據
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
                getContentResolver().delete(uri, null, null);
            }
        });
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末熟吏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子玄窝,更是在濱河造成了極大的恐慌牵寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哆料,死亡現(xiàn)場離奇詭異缸剪,居然都是意外死亡,警方通過查閱死者的電腦和手機东亦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門杏节,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人典阵,你說我怎么就攤上這事奋渔。” “怎么了壮啊?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵嫉鲸,是天一觀的道長。 經常有香客問我歹啼,道長玄渗,這世上最難降的妖魔是什么座菠? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮藤树,結果婚禮上浴滴,老公的妹妹穿的比我還像新娘。我一直安慰自己岁钓,他們只是感情好升略,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屡限,像睡著了一般品嚣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钧大,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天翰撑,我揣著相機與錄音,去河邊找鬼啊央。 笑死额嘿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的劣挫。 我是一名探鬼主播册养,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼压固!你這毒婦竟也來了球拦?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤帐我,失蹤者是張志新(化名)和其女友劉穎坎炼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拦键,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谣光,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了芬为。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萄金。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖媚朦,靈堂內的尸體忽然破棺而出氧敢,到底是詐尸還是另有隱情,我是刑警寧澤询张,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布孙乖,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏唯袄。R本人自食惡果不足惜弯屈,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恋拷。 院中可真熱鬧季俩,春花似錦、人聲如沸梅掠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阎抒。三九已至,卻和暖如春消痛,著一層夾襖步出監(jiān)牢的瞬間且叁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工秩伞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逞带,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓纱新,卻偏偏與公主長得像展氓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子脸爱,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容