Android四大组件之ContentProvider

一、简介


ContentProvider管理对结构化数据集的访问。它们封装数据并通过ContentResolver接口将其提供给应用程序,并提供用于定义数据安全性的机制。只有在需要在多个应用程序之间共享数据时,才需要内容提供程序。例如,联系人数据由多个应用程序使用,必须存储在内容提供程序中。如果不需要在多个应用程序之间共享数据,可以通过SQLiteDatabase直接使用数据库。

内容提供程序以一个或多个表(与在关系型数据库中找到的表类似)的形式将数据呈现给外部应用。

二、使用自定义ContentProvider


以《第一行代码》提供示例代码示范:

1. 先有个数据库

/**
 * 创建数据库辅助类
 */
public class MyDatabaseHelper extends SQLiteOpenHelper {
    // 创建 Book 表的 SQL
    public static final String CREATE_BOOK = "create table Book ("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "pages integer, "
            + "name text)";
    // 创建 Category 表的 SQL
    public static final String CREATE_CATEGORY = "create table Category ("
            + "id integer primary key autoincrement, "
            + "category_name text, "
            + "category_code integer)";

    private Context mContext;

    /**
     * 创建DBHelper
     */
    public MyDatabaseHelper(Context context, String name,SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    /**
     * 创建数据库和表
     * 当helper.getxxxDatabase()时会检测数据库不存在,回调此方法
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }

    /**
     * 数据库升级
     * 调用此helper的构造方法创建helper时,version增大就会回调此方法
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists Book");
        db.execSQL("drop table if exists Category");
        onCreate(db);
    }

}

2. 再来个内容提供者ContentProvider,提供数据

/**
 * 创建内容提供者
 */
public class DatabaseProvider extends ContentProvider {
    /** 当URI匹配时返回的code */
    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";

    /** URI匹配器 */
    private static UriMatcher uriMatcher;

    /** DBHelper */
    private MyDatabaseHelper dbHelper;

    /** 优先于构造方法,创建UriMatcher,添加期望匹配的内容URI */
    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);
        // 该URI表示可以匹配category表中的任意一行数据
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }

    /**
     * 创建ContentProvider
     */
    @Override
    public boolean onCreate() {
        // 同时创建DBHelper , 指定数据库名称为 BookStore.db,数据库版本为2
        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);
        return true;
    }

    /**
     * 查询数据
     * 必须重写,只有当ContentResolver尝试获取该程序数据时,才会调用
     * @param uri 指定某个应用下的某张表
     * @param projection 指定查询的列名
     * @param selection 指定where的约束条件
     * @param selectionArgs 为where中的占位符传递具体值
     * @param sortOrder 查询结果排序方式
     * @return
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        // 创建或打开数据库
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        // 根据ContentResolver传入的URI,匹配出要访问的内容
        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;
    }

    /**
     * 插入数据
     * 必须重写
     * @param uri
     * @param values 插入的数据
     * @return
     */
    @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);
                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;
    }

    /**
     * 更新数据
     * 必须重写
     * @param uri
     * @param values 需要更新的新数据
     * @param selection 指定where的约束条件
     * @param selectionArgs 为where中的占位符传递具体值
     * @return 受影响的行数
     */
    @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 });
                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;
    }

    /**
     * 删除数据
     * 必须重写
     * @param uri
     * @param selection
     * @param selectionArgs
     * @return 删除的行数
     */
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // 删除数据
        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;
    }

    /**
     * 根据传入的URI返回对应的 MIME TYPE
     * 必须重写
     * @param uri
     * @return
     */
    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case BOOK_DIR:
                // 表的 MIME 类型由3部分组成:
                // 1: 必须以vnd开头
                // 2: URI以路径结尾,衔接android.cursor.item/; URI以id结尾,衔接android.cursor.item/
                // 3: vnd.<name>.<type> 。 <name> 值应具有全局唯一性,<type> 值应在对应的 URI 模式中具有唯一性。
                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;
    }

}
// 必须在清单文件注册,authorities指定DatabaseProvider的URI的AUTHORITY 部分
<provider
    android:name=".DatabaseProvider"
    android:authorities="com.example.databasetest.provider"
    android:enabled="true"
    android:exported="true" />

3. 最后上ContentResolver,连接ContentProviderid,通过SQLiteOpenHelper,操作SQLiteDatabase

ContentProviderid是为了应用间数据共享,所以,我们在新建一个应用,在其中使用ContentResolver,共享上面那个应用中的ContentResolver提供的数据

public class ContentProviderActivity extends AppCompatActivity {
    private String newId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_content_provider);
        // 插入数据
        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 往Book表中插入数据
                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", 55.55);
                // 获取ContentResolver,插入数据,最终调用的事ContentProvider的insert,插入成功后返回这条新数据的URI
                Uri newUri = getContentResolver().insert(uri, values);
                // 获取这条新数据在表中的id
                newId = newUri.getPathSegments().get(1);
            }
        });


        // 查询数据
        Button queryData = (Button) findViewById(R.id.query_data);
        queryData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 查询Book表中的所有数据
                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("ContentProviderActivity", "book name is " + name);
                        Log.d("ContentProviderActivity", "book author is " + author);
                        Log.d("ContentProviderActivity", "book pages is " + pages);
                        Log.d("ContentProviderActivity", "book price is " + price);
                    }

                    // 记得关闭cursor,释放资源,避免内存泄漏
                    cursor.close();
                }
            }
        });

        // 更新数据
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 更新Book表中id为newId的那条数据
                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 View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 删除Book表中id未newId的那条数据
                Uri uri = Uri.parse("content://com.example.databasetest.provider/book/" + newId);
                getContentResolver().delete(uri, null, null);
            }
        });
    }

}

三、使用系统ContentProvider

1. 读取联系人

// 查询联系人数据
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor != 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));
    }
}
<!--获取读取联系人权限,危险权限-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>

2. 读取短信

Uri uri = Uri.parse("content://sms/");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
while (cursor.moveToNext()) {
    // 短信内容
    String body = cursor.getString(cursor.getColumnIndex("body"));
    // 接收人
    String address = cursor.getString(cursor.getColumnIndex("address"));
}
<!--获取读取短信权限,危险权限-->
<uses-permission android:name="android.permission.READ_SMS"/>

3. 读取音乐

Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);

while (cursor.moveToNext()) {
    String music_data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));//歌曲路径
    String musictitle = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));//歌曲名字
    String musicartist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));//歌曲歌手
    String musicalbum = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));//歌曲专辑
    String music_size = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));//歌曲大小
    String musicduration = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));//歌曲时长
}
<!--获取手机存储卡权限,危险权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

4. 读取图片

Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()) {
// 获取图片的名称
String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
// 获取图片的生成日期
byte[] data = cursor.getBlob(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
// 获取图片的详细信息
String desc = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DESCRIPTION));
}
<!--获取手机存储卡权限,危险权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

个人总结,水平有限,如果有错误,希望大家能给留言指正!如果对您有所帮助,可以帮忙点个赞!如果转载,希望可以标明文章出处!最后,非常感谢您的阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值