第六章(内容提供器-Content Provider)

内容提供器(Content Provider)

内容提供器简介:

Content Provider主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。
内容提供器的用法一般有两种:
第一种是使用现有的内容提供器来读取和操作相应程序中的数据,例如系统自带应用联系人就提供了现有的内容提供器所以我们就能创建一个程序来读取联系人里面的数据。
第二种是创建自己的内容提供器给我们程序的数据提供外部访问接口,那么任何其他程序知道这个接口就可以对这部分数据进行访问了.
下面我们直接看一个第二种用法的一个例子。
在例子之前我们要知道这些知识:

  1. 对于每一个应用程序来说,如果想要访问内容提供器共享的数据,就一定要借助ContentResolver类,我们可以通过Context中的getContentResolver方法来获取ContentResolver类的实例,ContentResolver类中提供了一系列的的方法对数据进行CRUD操作
  2. 不同于SQLiteDatabase,ContentResolver中的增删改查方法都不是接收表名参数,而是用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立了唯一的标识符。
  3. 内容URI有两部分组成:authority和path。authority是用于对不同的应用程序作区分,为了避免冲突,都会采用程序包名的方式来进行命名,比如某个程序的包名为com.example.app,那么该程序对应的authority就可以命名为com.example.app.provider。path则是对同一应用程序中不同的表作区分的,通常会添加到authority后面,比如某个程序的数据库里存在的两张表table1和table2,这是就可以将path命名为/table1和/table2,然后将path和authority组合再加上协议头就是下面的标准写法了:
    content://com.example.app.provider/table1
    content://com.example.app.provider/table2
    以上是表示访问table1或table2表中的所有数据,我们还可以在内容URI后面加上id:
    content://con.example.app.provider/table1/1
    这样就表示我们期望访问的是table1表中id为1的数据
    内容URI就以上两种写法了,以路径结尾的就是期望访问表中的所有数据;以id结尾的就是期望访问表中拥有相应id的数据。
    我们还可以使用通配符的方式来匹配这两种格式的URI,下面会用到,规则如下:

下面是核心步骤

  1. 创建自己的内容提供器
    我们打开上一章中的DatabaseTest项目,通过内容提供器来给它加入外部访问接口,将Toast去掉,因为跨程序不能直接使用Toast。右击com.example.databasetestb包>New->Other->Content Provider,我们将内容提供器命名为DatabaseProvider,将authority指定为com.houchongmu.databasetest.provider勾选Exported和Enabled。这昂个属性前者表示是否允许外部程序访问我们的内容提供器,Enabled属性表示是否启用这个内容提供器,点击Finish。
  2. 接着修改DatabaseProvider
package com.example.houchongmu.databasetest;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

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.houchongmu.databasetest.provider";
    public static UriMatcher uriMatcher;
    private static 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);
    }

    public DatabaseProvider() {

    }

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


    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        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) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Uri uriReturn = null;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert("Book", null, values);//returns :long the row ID of the newly inserted row, or -1 if an error occurred
                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) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int updateRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                updateRows = db.update("Book", values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updateRows = db.update("Book", values, "id = ?", new String[]{bookId});
                break;
            case CATEGORY_DIR:
                updateRows = db.update("Category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updateRows = db.update("category", values, "id = ?", new String[]{categoryId});
                break;
            default:
                break;
        }
        return updateRows;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        int deleteRows = 0;
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                deleteRows = db.delete("Book", selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete("Book", "id = ?", new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete("Category", selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String category = uri.getPathSegments().get(1);
                deleteRows = db.delete("Category", "id = ?", new String[]{category});
                break;
        }
        return deleteRows;
    }

    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.houchongmu.databasetest.provider.book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.houchongmu.databasetest.provider.book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.houchongmu.databasetest.provider.category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.houchongmu.databasetest.provider.category";
            default:
                break;
        }
        return null;
    }


}

里面重写了六个方法,我们一个一个看

    1. onCreate方法:初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作,返回true表示内容提供器初始化成功,返回false表示失败。注意的是,只有当ContentResolver尝试访问我们程序中的数据时,内容提供器才会被初始化。这里利用SQLiteOpenHelper初始化了一个名为BookStore的数据库
    1. query方法:从内容提供器中查询数据。首先获取到SQLiteDatabase实例,接着使用uri参数来确定具体查询哪种表,projection参数用于确定查询哪些列,selection和selectionArgs参数用于约束查询哪几行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。这里有个细节额,调用了Uri对象的getPathSegments方法,它会将内容URI权限之后的部分以“/“符号进行分割,并将分割后的结果放入到一个字符串列表中,那这个列表的第0个位置就是路径,第一个位置就是存放的id了。 得到ud之后,再通过selection和selectionArgs参数进行约束就实现了查询单条数据 。
    1. UriMatcher类:我们注意到该方法里面有个switch语句,关于这个我们得从这个类里面的静态代码块讲起,静态代码块里面初始化了一个UriMatcher类,这个类可以轻松实现匹配内容URI的功能,构造函数里面传的是一个code值(the code to match for the root URI)。
      addURI方法接收三个参数,可以分别将authority、path和一个自定义code值传进去,当调用UriMatcher类的match方法时就可以将一个Uri对象传进去,返回值是某个能够匹配这个Uri对象对应的code值,利用这个值就可以判断出调用方期望访问的是哪张表中的数据了。然后执行相应的逻辑
    1. insert方法:
      刚方法同样先获取到SQLiteDatabase的实例,然后根据传入的Uri判断用户想往哪张表里面添加数据,然后再调用SQLiteDatabase的实例进行insert操作就可以了,这个返回的是新插入行的id。注意本类insert方法要求返回的是一个能够表示这条新增数据的URI,所以我们还得调用Uri.parse方法来讲一个内容URI解析成Uri对象,当然这个内容URI是以新增数据的id结尾的。其他的方法类似就不过多解释了。
    1. getType方法:
      这是内容提供器必须提供的一个方法,用于获取Uri对象所对应的MIME类型,一个URI所对应的MIME字符串主要由三个部分组成,Android对这三个部分做了一下格式规定:
  1. 接下来新建ProviderTest来访问DatabaseTest中的数据
    1. 在布局里面创建四个Button
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:onClick="add_data"
        android:text="添加Book数据"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:onClick="query_data"
        android:text="查询Book数据"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button"
        app:layout_constraintVertical_bias="0.0" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:onClick="updata_data"
        android:text="更新Book数据"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button2"
        app:layout_constraintVertical_bias="0.0" />

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:onClick="delete_data"
        android:text="删除Book数据"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button3"
        app:layout_constraintVertical_bias="0.0" />
</android.support.constraint.ConstraintLayout>
  • MainActivity
package com.example.houchongmu.providertest;

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    public static final String TAG = "MainActivity";
    private String newId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void add_data(View view) {
        Uri uri = Uri.parse("content://com.houchongmu.databasetest.provider/book");
        ContentValues values = new ContentValues();
        values.put("name", "假如给我三天光明");
        values.put("author", "海伦凯勒");
        values.put("price", 30);
        values.put("pages", 500);
        Uri newUri = getContentResolver().insert(uri, values);//返回的是一个新的Uri对象,包含的新增数据的id
        newId = newUri.getPathSegments().get(1);
    }

    public void query_data(View view) {
        Uri uri = Uri.parse("content://com.houchongmu.databasetest.provider/book");
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            do {
                String name = cursor.getString(cursor.getColumnIndex("name"));
                String author = cursor.getString(cursor.getColumnIndex("author"));
                double price = cursor.getDouble(cursor.getColumnIndex("price"));
                int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                Log.d(TAG, "query_data: " + name);
                Log.d(TAG, "query_data: " + author);
                Log.d(TAG, "query_data: " + price);
                Log.d(TAG, "query_data: " + pages);
            } while (cursor.moveToNext());
            cursor.close();
        }
    }

    public void updata_data(View view) {
        Uri uri = Uri.parse("content://com.houchongmu.databasetest.provider/book");
        ContentValues values = new ContentValues();
//        values.put("name", "少有人走的路");
//        values.put("author", "不知道");
//        values.put("pages", 400);
//        values.put("price", 12.34);
//        getContentResolver().update(uri, values, null, null);
//        values.clear();
        values.put("price", 19);
        getContentResolver().update(uri, values, "name = ?", new String[]{"假如给我三天光明"});

    }

    public void delete_data(View view) {
        Uri uri = Uri.parse("content://com.houchongmu.databasetest.provider/book/"+newId);
        int rows = getContentResolver().delete(uri, null, null);
        Log.d(TAG, "delete_data: 删除了" + rows + "行");
    }

}


以上内容结合上面自定义的DatabaseProvider类很容易理解,就不过多解释了。
newId是获取新插入数据的id,后面删除数据的Button就能删除这条插入的数据。
点击添加BOOK数据,然后再点击查询BOOK数据:

image

image

两个程序都能访问这条数据


image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容