《第一行代码》笔记之 ContentProvider

本篇内容整理自郭霖的 《第一行代码》

访问其他应用中的数据——获取数据

ContentResolver 的基本用法

  • 访问内容提供器中的共享数据就必要借助 ContentResolver
  • 可以通过 Context 中的 getContentResolver() 方法获取实例
  • 提供了一些列的 CRUD 操作,类似 SQLiteDatabase。不同的是 CRUD 操作不提供表名,使用 Uri 参数代替
    • URI 权限:对于不同的应用程序做区分。一般采用程序包命名
    • URI 路径:同一程序中的不同的表做区分。
      标准的 URI 格式:content://com.example.app.provider(权限)/table1(路径)
      如果使用表名,系统将无法得知我们访问的是哪个应用程序中的表
  • 使用 Uri.parse() 方法,就可以将内容 URI 字符串解析为 Uri 对象
    Uri uri = Uri.parse("content://com.example.app.provider/table1");

使用 Uri 对象来查询 table1 表中的数据

    Cursor cursor = getContentResolver().query(
    uri, //指定某个应用的某张表
    projection, //指定查询的列名
    selection, //指定 where 的约束条件
    selectionArgs, //为 where 中的占位符提供具体的值
    sortOrder // 排序方式
);

查询完毕,逐条读取数据

    if(cursor != null){
    while(cursor.moveToNext()){
        String column1 = cursor.getString(cursor.getColumnIndex("column1"));
        int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    }
    cursor.close();
}
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);
ContentValues values = new ContentValues();
values.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[]{ "text", "1" });
getContentResolver().delete(uri,"column2 = ?", new String[]{ "1" });

读取系统联系人

MainActivity.class

public class MainActivity extends Activity {

    ListView contactsView;

    ArrayAdapter<String> adapter;

    List<String> contacstList = new ArrayList<String>();

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

        contactsView = (ListView) findViewById(R.id.contacts_view);
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contacstList);
        contactsView.setAdapter(adapter);
        readContacts();

    }

    private void readContacts() {
        Cursor cursor = null;
        try{
            //查询联系人
            cursor = getContentResolver().query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI, //获取手机通信录的 Uri
                    null,null,null,null
            );
            while (cursor.moveToNext()) {

                //获取联系人姓名
                String displayName = cursor.getString(cursor.getColumnIndex(
                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
                ));

                //获取联系人手机
                String number = cursor.getString(cursor.getColumnIndex(
                        ContactsContract.CommonDataKinds.Phone.NUMBER
                ));

                contacstList.add(displayName + "\n" + number);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(cursor != null){
                cursor.close();
            }
        }
    }
}

AndroidManifest.xml 中获取权限

 <uses-permission android:name="android.permission.READ_CONTACTS" />

创建自己的内容提供器——提供数据

  • 之前是如何在自己的程序中访问其他应用的数据。思路:获取该应用程序的 URI,借助 ContentResolver 进行 CRUD 操作
  • 现在来实现将数据提供出去,给其他应用访问,并保证安全性。

创建内容提供器的步骤

创建个类继承 ContentProvider 类并重写它的六个抽象方法

public class MyProvider extends ContentProvider {

    //初始化内容提供器的时候调用。完成对数据库的创建和升级操作。返回 true 表示初始化成功,false 表示失败。
    //ContentResolver 尝试访问本程序中的数据时,内容提供器才会被初始化
    @Override
    public boolean onCreate() {
        return false;
    }

    //从内容提供器中查询数据
    //1、Uri 确定查询哪一张表
    //2、projection 确定查询哪些列
    //3、selection 和 selectionArgs 参数约束哪些行
    //4、sortOrder 对结果进行排序
    //查询结果存放在 Cursor 对象中返回
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    //向内容提供器中添加一条数据
    //1、URI 参数来确定添加哪一张表
    //2、待添加的数据保存在 values 中
    //返回一个用于表示这条新记录的 URI
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    //1、URI确定删除哪一张表
    //2、selection,selectionArgs 来约束删除哪些行
    //被删除的行将作为返回值
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    //1、URI确定更新哪一张表
    //2、新数据保存在 values 中
    //3、selection,selectionArgs 来约束更新哪些行
    //受影响的行将作为返回值
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

    //根据传入内容的 URI 返回相应的 MIME 类型
    @Override
    public String getType(Uri uri) {
        return null;
    }
}
  • 每一个方法都有一个 URI参数,此参数是调用ContentResolver 的增删改查方法时传递过来的。而我们需要对 Uri 参数进行解析,从中分析出调用方期望访问的表和数据

  • URI后面可以加 id,以路径结尾表示期望访问该表中的所有数据,以 id 结尾表示期望访问该表中拥有相应 id 的数据

  • 通配符方式匹配 URI

  • *****:表示匹配任意长度的任意字符

    • content://com.example.app.provider/*
  • #:表示匹配任意长度的数字

    • content://com.example.app.provider/#
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);
// 将 uri 传入 uriMatcher
        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_DIR);
        uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);
    }
    ......
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 在 uriMatcher 中逐个匹配 uri,选取调用方期望访问的那一张表
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                // 查询 table1 表中的所有数据
                break;
            case TABLE1_ITEM:
                // 查询 table1 表中的单条数据
                break;
            case TABLE2_DIR:
                // 查询 table2 表中的所有数据
                break;
            case TABLE2_ITEM:
                // 查询 table2 表中的单条数据
                break;
            default:
                break;
        }
        ......
    }
    ......

上述代码只是用 query() 作为范例。insert()update()delete() 这个方法类似:1、它们都会携带 Uri 参数 2、利用 UriMatcher 的 match() 方法判断出调用方期望访问的是哪一张表,再对该表中的数据进行相应的操作就可以了。

  • 除此之外还有个 getType() 方法。用于获取 Uri 对象所对应的 MIME 类型。MIME 字符串由三部分构成:
  1. 必须以vnd 开头
  2. 如果内容以 URI 路径结尾,则后接 android.cursor.dir/,如果内容 URI 以 id 结尾,则后接 android.cursor.item/。
  3. 最后接上 vnd.< authority >.< path >

列如
URIcontent://com.example.app.provider/table1
MIMEvnd.android.cursor.dir/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;
    }

综合实例

实现跨程序的数据共享

  • 程序对外开放内容

MyProvider.java

public class MyProvider 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.czx.sunorig.myapplication";

    private MyDatabaseHelper dbHelper;

    private static UriMatcher uriMatcher;

    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);
    }
    //初始化内容提供器的时候调用。完成对数据库的创建和升级操作。返回 true 表示初始化成功,false 表示失败。
    //ContentResolver 尝试访问本程序中的数据时,内容提供器才会被初始化
    @Override
    public boolean onCreate() {
        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        return true;
    }

    //从内容提供器中查询数据
    //1、Uri 确定查询哪一张表
    //2、projection 确定查询哪些列
    //3、selection 和 selectionArgs 参数约束哪些行
    //4、sortOrder 对结果进行排序
    //查询结果存放在 Cursor 对象中返回
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;

        switch (uriMatcher.match(uri)){
            case BOOK_DIR:
                // 查询 book 表中的所有数据
                cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case BOOK_ITEM:
                // 查询 book 表中的单条数据
                // getPathSegments() 方法将 URI 权限之后的部分以 “/” 符号进行分隔,并把分割结果放入一个字符串列表中,列表第0个位置是路径,第1个位置是 id
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("book", projection, "id = ?", new String[]{ bookId }, null, null, sortOrder);
                break;
            case CATEGORY_DIR:
                // 查询 category 表中的所有数据
                cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
                break;
            case CATEGORY_ITEM:
                // 查询 category 表中的单条数据
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("category", projection, "id = ?", new String[]{ categoryId }, null, null, sortOrder);
                break;
            default:
                break;
        }
        return cursor;
    }

    //向内容提供器中添加一条数据
    //1、URI 参数来确定添加哪一张表
    //2、待添加的数据保存在 values 中
    //返回一个用于表示这条新记录的 URI
    @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); //返回新添记录的行号,与主键id无关
                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;
    }

    //1、URI确定更新哪一张表
    //2、新数据保存在 values 中
    //3、selection,selectionArgs 来约束更新哪些行
    //受影响的行将作为返回值
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        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 });
            case CATEGORY_DIR:
                updatedRows = db.update("category", values, selection, selectionArgs);
                break;
            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updatedRows = db.update("Book", values, "id = ?", new String[]{ categoryId });
            default:
                break;
        }
        return updatedRows;
    }

    //1、URI确定删除哪一张表
    //2、selection,selectionArgs 来约束删除哪些行
    //被删除的行将作为返回值
    @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 categoryId = uri.getPathSegments().get(1);
                deleteRows = db.delete("category", "id = ?", new String[]{ categoryId });
                break;
        }
        return deleteRows;
    }

    //根据传入内容的 URI 返回相应的 MIME 类型
    @Override
    public String getType(Uri uri) {
        switch(uriMatcher.match(uri)){
            case BOOK_DIR:
                return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".book";
            case BOOK_ITEM:
                return "vnd.android.cursor.item/vnd." + AUTHORITY + ".book";
            case CATEGORY_DIR:
                return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".category";
            case CATEGORY_ITEM:
                return "vnd.android.cursor.item/vnd." + AUTHORITY + ".category";
            default:
                break;
        }
        return null;
    }
}

MyDatabaseHelper.java

public class MyDatabaseHelper extends SQLiteOpenHelper {

    public static final String CREATE_BOOK = "create table book("
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages integer,"
            + "name text)";

    public static final String CREATE_CATEGORY = "create table category("
            + "id integer primary key autoincrement,"
            + "book_id integer)";
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

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

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

AndroidManifest.xml

        <provider
            android:authorities="com.czx.sunorig.myapplication"
            android:name="com.czx.sunorig.myapplication.MyProvider"
            android:exported="true">

        </provider>
  • 另一个程序访问内容:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button add_data;

    private Button query_data;

    private Button update_data;

    private Button delete_data;

    private String newId;

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

        add_data = (Button) findViewById(R.id.add_data);
        query_data = (Button) findViewById(R.id.query_data);
        update_data = (Button) findViewById(R.id.update_data);
        delete_data = (Button) findViewById(R.id.delete_data);

        add_data.setOnClickListener(this);
        query_data.setOnClickListener(this);
        update_data.setOnClickListener(this);
        delete_data.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.add_data:
                addData();
                break;
            case R.id.query_data:
                queryData();
                break;
            case R.id.update_data:
                updateData();
                break;
            case R.id.delete_data:
                deleteData();
                break;
            default:
                break;
        }
    }

    private void queryData() {
        Uri uri = Uri.parse("content://com.czx.sunorig.myapplication/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();
        }

    }

    private void addData() {
        Uri uri = Uri.parse("content://com.czx.sunorig.myapplication/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);

    }

    private void updateData() {
        Uri uri = Uri.parse("content://com.czx.sunorig.myapplication/book");

        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);
    }

    private void deleteData() {
        Uri uri = Uri.parse("content://com.czx.sunorig.myapplication/book" + newId);
        getContentResolver().delete(uri, null, null);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值