Android学习笔记(七)内容提供器(Content Provider)

7.1 内容提供器

        内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时 还能保证被访数据的安全性。
        目前,使用内容提供器是 Android 实现跨程序共享数据的 标准方式
        内容提供器可以选择只对哪一部分数据进行共享,从而保证隐私数据的安全。
        内容提供器有两种用法:一种是 使用现有的内容提供器来读取和操作相应程序中的数据;另一种是 创建自己的内容提供器给我们程序的数据提供外部访问的接口

7.2 访问其他程序的数据

7.2.1 ContentResolver 的基本用法

(1) ContentResolver 类是访问内容提供器中共享数据的关键类,就类似于SQLiteDatabase类。可以通过Context的getContentResolver()方法来获取实例。其中提供了一系列的CRUD方法:insert()、update()、delete()、query(),只不过不能接收表名参数,而是一个内容 Uri对象
(2) 内容URI可以清楚的表达想要访问哪个程序中的哪张表的数据,给内容提供器中的数据建立了一个唯一标识,主要有两部分组成—— 权限(authority)路径(path)
  • 权限是为了对不同程序进行区分的,为了避免冲突,一般使用包名命名。(比如包名为com.example.app,那么就可以命名为com.example.app.provider)
  • 路径则是用于对同一程序中的不同表做区分的,通常添加到权限后面。
然后,还需要在头部加上协议声明,来标识这个字符串为内容URI,标准格式如下:
content://com.example.app.provider/table_name
最后将此字符串解析为Uri对象:
Uri uri = Uri.parse("content://com.example.app.provider/table_name")
示例代码如下:
//1.获取内容提供器(当前是在MainActivity中)
ContentResolver resolver = this.getContentResolver();
//2.提供Uri对象参数
Uri uri = Uri.parse("content://com.example.app.provider/table_name");
//3.查询(其他操作如增删改类似于SQLiteDatabase的方法)
Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);

7.2.2 读取系统联系人

(1)在activity_main.xml中加入一个ListView用来展示读取后的数据
(2)编写Activity中的代码:
public class MainActivity extends Activity {
    private ListView listView;
    ArrayAdapter adapter;
    List contactList = new ArrayList<>();

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

        //1.首先获取ListView控件的实例
        listView = (ListView) findViewById(R.id.listView);

        //2.设置它的适配器
        adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, contactList);
        listView.setAdapter(adapter);

        //3.读取联系人数据
        readContacts();
    }

    private void readContacts() {
        Cursor cursor = null;
        try {
            //查询数据(ContactsContract.CommonDataKinds.Phone类已经做好了Uri封装)
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
            while (cursor.moveToNext()) {
                //获取联系人姓名(对应DISPLAY_NAME常量)
                String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                //获取联系人手机(对应NUMBER常量)
                String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                //添加至集合中
                contactList.add(name + "\n" + phone);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
}
(3)读取联系人同样要在AndroidManifest.xml中声明权限
< uses-permission android :name= "android.permission.READ_CONTACTS" />
Android学习笔记(七)内容提供器(Content Provider)_第1张图片

7.3 创建自己的内容提供器

7.3.1 创建内容提供器

(1)要使用内容提供器, 需要自定义一个类且继承自ContentProvider,并重写其中的6个方法,如下:
public class MyProvider extends ContentProvider {
    /**
     * 初始化内容提供器的时候调用,通常会在这里完成对数据库的创建和升级等操作
     * 注意:只有当存在 ContentResolver 尝试访问我们程序中的数据时,内容提供器才会被初始化。
     *
     * @return true表示内容提供器初始化成功,false表示失败
     */
    @Override
    public boolean onCreate() {
        return false;
    }

    /**
     * 从内容提供器中查询数据
     *
     * @param uri           Uri对象来确定查询哪张表
     * @param projection    哪些列
     * @param selection     筛选条件
     * @param selectionArgs 筛选条件值
     * @param sortOrder     排序
     * @return Cursor对象
     */
    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    /**
     * 根据传入的内容 URI 来返回相应的 MIME 类型
     *
     * @param uri Uri对象
     * @return MIME类型字符串
     */
    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    /**
     * 向内容提供器中添加一条数据
     *
     * @param uri    Uri对象
     * @param values ContentValues对象
     * @return 一个用于表示这条新记录的 URI
     */
    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    /**
     * 从内容提供器中删除数据
     *
     * @param uri           Uri对象
     * @param selection     筛选条件
     * @param selectionArgs 筛选条件值
     * @return 被删除的行数
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    /**
     * 更新内容提供器中已有的数据
     *
     * @param uri           Uri对象
     * @param values        ContentValues对象
     * @param selection     筛选条件
     * @param selectionArgs 筛选条件值
     * @return 受影响的行数
     */
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}
(2)需要对Uri参数进行解析,分析出调用方期望访问的表和数据
内容URI格式主要有两种:
  • 以路径结尾,如content://com.example.app.provider/table1,表示期望访问该程序table1表中的数据
  • 以id结尾,如content://com.example.app.provider/table1/1,表示期望访问该程序table1表中id为1的数据

所以,用通配符的方式来匹配这两种格式的Uri:
content://com.example.app.provider/*
content://com.example.app.provider/table1/#

ps:
*:匹配任意长度的任意字符
#:匹配任意长度的数字

(3)通过UriMatcher类来匹配内容URI。
        UriMatcher类提供了 addUri()方法,接收三个参数( 权限,路径,自定义代码), 当调用 UriMatcher 的 match()方法时,就可以将一个 Uri 对象传入,返回值是某个能够匹配这个 Uri 对象所对应的自定义代码,利用这个代码,我们就可以判断出调用方期望访问的是哪张表中的数据了。
//自定义代码
public static final int TABLE1_DIR = 0;//查询table1的所有数据代码
public static final int TABLE1_ITEM = 1;//查询table1的单条数据代码
public static final int TABLE2_DIR = 2;//查询table2的所有数据代码
public static final int TABLE2_ITEM = 3;//查询table2的单条数据代码
private static UriMatcher uriMatcher;

static {
    //创建UriMatcher的实例
    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);
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    Cursor cursor = null;
    switch (uriMatcher.match(uri)) {
        case TABLE1_DIR:
            //去查询table1的全部数据
            break;
        case TABLE1_ITEM:
            //去查询table1的单条数据
            break;
        case TABLE2_DIR:
            //去查询table2的所有数据
            break;
        case TABLE2_ITEM:
            //去查询table2的单条数据
            break;
    }
    return cursor;
}
Uri对象调用如下方法即可获取路径和id:
/**
 * getPathSegments()方法会将内容URI权限后面的字符串以'/'分割,并存放在一个列表中
 * 所以此时0下标即为路径,1下标即为id
 */
String path = uri.getPathSegments().get(0);
(4)完善getType()方法
        getType()方法用于获取 Uri 对象所对应的 MIME 类型。一个内容 URI 所对应的 MIME字符串主要由三部分组分,Android 对这三个部分做了如下格式规定:
1. 必须以 vnd 开头。
2. 如果内容 URI 以路径结尾,则后接 android.cursor.dir/,如果内容 URI 以 id 结尾,则后接 android.cursor.item/。
3. 最后接上 vnd..
如:
content://com.example.app.provider/table1 对应MIME类型为:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
content://com.example.app.provider/table1/1 对应MIME类型:vnd.android.cursor.item/vnd. com.example.app.provider.table1
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;
}
(5)在AndroidManifest.xml中注册(application标签下)
< provider
android :name= ".MyProvider"
android :authorities= "com.jastar.databasetest.provider" > provider >

7.3.2 简单CRUD示例

public class DatabaseProvider extends ContentProvider {
    //表名
    private static final String TABLE_BOOK = "book";
    private static final String TABLE_CATEGORY = "Category";

    //自定义代码
    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.jastar.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;
    }

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

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                cursor = db.query(TABLE_BOOK, projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                //获取id
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query(TABLE_BOOK, projection, "id=?", new String[]{bookId}, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                cursor = db.query(TABLE_CATEGORY, projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query(TABLE_CATEGORY, projection, "id=?", new String[]{categoryId}, null, null, sortOrder);
                break;
        }

        return cursor;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Uri returnUri = null;

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
            case BOOK_ITEM:
                long newBookId = db.insert(TABLE_BOOK, null, values);
                returnUri = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
                break;
            case CATEGORY_DIR:
            case CATEGORY_ITEM:
                long cateId = db.insert(TABLE_CATEGORY, null, values);
                returnUri = Uri.parse("content://" + AUTHORITY + "/Category/" + cateId);
                break;
        }

        return returnUri;
    }

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

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                deleteRows = db.delete(TABLE_BOOK, null, null);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deleteRows = db.delete(TABLE_BOOK, "id=?", new String[]{bookId});
                break;
            case CATEGORY_DIR:
                deleteRows = db.delete(TABLE_CATEGORY, null, null);
                break;
            case CATEGORY_ITEM:
                String cateId = uri.getPathSegments().get(1);
                deleteRows = db.delete(TABLE_CATEGORY, "id=?", new String[]{cateId});
                break;
        }

        return deleteRows;
    }

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

        SQLiteDatabase db = dbHelper.getWritableDatabase();
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                updatedRows = db.update(TABLE_BOOK, values, selection, selectionArgs);
                break;
            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updatedRows = db.update(TABLE_BOOK, values, "id = ?", new String[]
                        {bookId});
                break;
            case CATEGORY_DIR:
                updatedRows = db.update(TABLE_CATEGORY, values, selection,
                        selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update(TABLE_CATEGORY, values, "id = ?", new String[]
                        {categoryId});
                break;
            default:
                break;
        }

        return updatedRows;
    }
}

你可能感兴趣的:(#,Android学习笔记)