ContentProvider基本使用初探

本文深入讲解Android中的ContentProvider,包括其工作原理、如何创建自定义ContentProvider以及如何使用ContentResolver进行数据交互。同时介绍了ContentProvider在Android应用间的通信中扮演的角色。

ContentProvider是Android中提供的专门用于进程间通信,它底层也是Binder,系统已经为我们做了封装。系统预置了很多ContentProvider,比如通讯录,日程表信息等应用。访问它们都是跨进程。
ContentProvider除了onCreate之外,其他五个方法都是在Binder的线程池中执行的。
ContentProvidr主要以表格形式组织数据,还支持文件数据等。
至于ContentProvider原理,以后学习了会分析
要实现自定义ContentProvider,只需要继承ContentProvider类实现下面6个方法就可以了;

//实现的六个方法都运行在ContentProvier所在进程中
public class BookProvider extends ContentProvider {
    //初始化的时候调用,可以进行数据库的创建和升级等操作
    //返回true,表示内容提供器初始化成功,
    //返回false,表示初始化失败
    //这是唯一一个运行在主线程里面,下面5个都运行在Binder的线程池中
    @Override
    public boolean onCreate() {
        return false;
    }
    //从内容提供器中查询数据,和Sqlite中的query差不多,除了第一个参数
    //uri用来确定查询那张表
    //projection用来确定查询那些列
    //selection和selectionArgs用来确定查询那些行
    //sortOrder用于对结果进行排序
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        return null;
    }
    //根据传入的URI来返回相应的MIME类型
    @Override
    public String getType(Uri uri) {
        return null;
    }
    //插入数据,和Sqlite中的insert差不多,除了第一个参数
    //uri来请确定插入那张表
    //valus,存放插入的值
    //返回一条用于表示这表记录的URI
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }
    //删除数据 和Sqlite中的delete差不多,除了第一个参数
    //uri表示删除哪一张表
    //selection和selectionArgs用来确定删除那些行
    //被删除的行数作为返回值返回
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }
    //更新数据 和Sqlite中的update差不多,除了第一个参数
    //uri表示那张表
    //返回更新的行数
    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        return 0;
    }
}

我们知道SQLite中CRUD中方法中第一个参数是Table表名,而在ContentProvider的CRUD中使用了Uri来代替
它到底表示什么?
其实它表示的是内容URI,内容URI给内容提供者创建数据提供了唯一标识,它由两部分组成:
权限(authority)路径(path)
权限:是用于区分不同的应用程序,一般使用包名的方式来命名,比如com.shion.app包名,那么对应的权限可以命名为com.shion.app.provider.
路径:用于区分同一个应用中不同的表,通常加到权限的后面。比如一个应用中存在一张表book
那么路径可以命名为/book.
权限和路径组成一个URI,为了体现出它是URI,我们还要在前面加上协议声明content://
所以上面内容URI最标准的格式写法是:
content://com.shion.app.provider/book
得到了内容URI字符串之后,可以把它解析成 Uri对象作为参数传到ContentProvider的增删改查的方法中。

//把URI字符串解析成Uri对象
Uri uri=Uri.parse("content://com.shion.app.provider/book");了

另外要访问ContentProvider,就要使用ContentResolver类,到时看代码就知道了;
下面实现一个简单自定义ContentProvider的Demo;从ContentProvider中查询图书信息;

服务端代码
先是创建一个数据库帮助类:

public class BookDataHelper extends SQLiteOpenHelper {

    public BookDataHelper(Context context, String name, int version) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table book(_id integer primary key autoincrement,name text,author text,publisher text);";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String sql = "drop table if exists book";
        db.execSQL(sql);
    }

}

接着实现ContentProvider:

public class BookProvider extends ContentProvider {
    private SQLiteOpenHelper dbHelper;
    private static final int BOOK_DIR = 0;
    private static final int BOOK_ITEM = 1;
    private static final String AUTHORITY = "com.shion.app.provider";
    private static UriMatcher sUriMatcher;

    {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
        sUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);

    }

    @Override
    public boolean onCreate() {
        dbHelper = new BookDataHelper(getContext(), "app.db", 1);
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
        case BOOK_DIR:
            return "vnd.android.cursor.dir/vnd.com.shion.app.provider.book";
        case BOOK_ITEM:
            return "vnd.android.cursor.item/vnd.com.shion.app.provider.book";
        default:
            break;
        }
        return null;
    }

    @Override
    public synchronized Cursor query(Uri uri, String[] projection,
            String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;
        switch (sUriMatcher.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;
        default:
            break;
        }
        return cursor;
    }

    @Override
    public synchronized Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        switch (sUriMatcher.match(uri)) {
        case BOOK_DIR:
        case BOOK_ITEM:
            long insertId = db.insert("book", null, values);
            Uri sUri = Uri.withAppendedPath(Uri.parse("content://"), AUTHORITY);
            return ContentUris.withAppendedId(sUri, insertId);
        default:
            break;
        }
        return null;
    }

    @Override
    public synchronized int delete(Uri uri, String selection,
            String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        switch (sUriMatcher.match(uri)) {
        case BOOK_DIR:
            return db.delete("book", selection, selectionArgs);
        case BOOK_ITEM:
            String bookId = uri.getPathSegments().get(1);
            return db.delete("book", "_id = ?", new String[] { bookId });
        default:
            break;
        }
        return 0;
    }

    @Override
    public synchronized int update(Uri uri, ContentValues values,
            String selection, String[] selectionArgs) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        switch (sUriMatcher.match(uri)) {
        case BOOK_DIR:
            return db.update("book", values, selection, selectionArgs);
        case BOOK_ITEM:
            String bookId = uri.getPathSegments().get(1);
            return db
                    .update("book", values, "_id = ?", new String[] { bookId });
        default:
            break;
        }
        return 0;
    }
}

补充一下:
1.“content://com.shion.app.provider/book”//这个内容URI表示想要访问book表
“content://com.shion.app.provider/book/1”//这个内容URI表示想要访问book表中id为1的数据
我们可以使用通配符来分别匹配他们
* : 表示匹配任意长度的的任意字符
# : 表示匹配任意长度的数字
匹配任意表可以这样写
“content://com.shion.app.provider/*”
匹配一个表中任意一行数据可以这样写
“content://com.shion.app.provider/book/#”
2. UriMatcher类可以轻松实现匹配内容URI的功能
比如下面这样就可以把权限一个自定义的常量绑定:

sUriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
sUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);

当下次调用 sUriMatcher.match(uri),返回自定义的常量实现匹配,可以判断访问的是book这张表还是Book这张表中的某一行数据;

    switch (sUriMatcher.match(uri)) {
        case BOOK_DIR:/*Book表*/
            break;
        case BOOK_ITEM:/*Book表中某行行数据*/
            break;
        default:
            break;
        }

3.getType方法的是MIME类型返回的值是系统要求的:

3.1. 开头必须是vnd
3.2.1 如果是路径结尾后面接android.cursor.dir/
3.2.2 如果是id结尾后面接android.cursor.item/
3.3 最后是vnd..
唯一区别就是dir和item 比如下面:
内容URI为:“content://com.shion.app.provider/book”所对应的MIME类型是:
“vnd.android.cursor.dir/vnd.com.shion.app.provider.book”//表示整张表的MIME
内容URI为:“content://com.shion.app.provider/book/1”所对应的MIME类型是
“vnd.android.cursor.item/vnd.com.shion.app.provider.book”//表示某一个条记录的MIME

4.在manifest中声明provider

         <provider
            android:name="com.shion.app.BookProvider"
            android:authorities="com.shion.app.provider"
            android:exported="true" >
        </provider>

android:exported 是Android中的四大组件 Activity,Service,Provider,Receiver 四大组件中都会有的一个属性。
它的主要作用是:是否支持其它应用调用当前组件。
默认值:如果包含有intent-filter 默认值为true; 没有intent-filter默认值为false。


下面贴上布局文件和MainActivity中相关实现代码:

客户端
布局文件activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp" >

    <Button
        android:id="@+id/btnInsertData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="插入" />

    <Button
        android:id="@+id/btnDeleteData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="删除" />

    <Button
        android:id="@+id/btnQueryData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询" />

    <Button
        android:id="@+id/btnUpdateData"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="更改" />

</LinearLayout>

java代码:

public class MainActivity extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btnInsertData).setOnClickListener(this);
        findViewById(R.id.btnDeleteData).setOnClickListener(this);
        findViewById(R.id.btnQueryData).setOnClickListener(this);
        findViewById(R.id.btnUpdateData).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btnInsertData:
            insert();
            break;
        case R.id.btnDeleteData:
            delete();
            break;
        case R.id.btnQueryData:
            query();
            break;
        case R.id.btnUpdateData:
            update();
            break;
        default:
            break;
        }
    }

    private Uri uri = Uri.parse("content://com.shion.app.provider");

    private void insert() {
        Uri inSertUri = Uri.withAppendedPath(uri, "book");
        ContentValues values = new ContentValues();
        values.put("name", "Android4编程入门经典");
        values.put("author", "wei-meng lee");
        values.put("publisher", "清华大学");
        getContentResolver().insert(inSertUri, values);
    }

    private void delete() {
        Uri deleteUri = Uri.withAppendedPath(uri, "book/1");
        getContentResolver().delete(deleteUri, null, null);
    }

    private void query() {
        Uri queryUri = Uri.withAppendedPath(uri, "book");
        Cursor c = getContentResolver().query(queryUri, null, null, null, null);
        if (c != null) {
            while (c.moveToNext()) {
                String name = c.getString(c.getColumnIndex("name"));
                String author = c.getString(c.getColumnIndex("author"));
                String publisher = c.getString(c.getColumnIndex("publisher"));
                Log.e("tag", "name: ===" + name);
                Log.e("tag", "author: ===" + author);
                Log.e("tag", "publisher: ===" + publisher);
            }
        }
    }

    private void update() {
        Uri updateUri = Uri.withAppendedPath(uri, "book");
        ContentValues values = new ContentValues();
        values.put("name", "大话数据结构");
        values.put("author", "程杰");
        values.put("publisher", "清华大学");
        getContentResolver().update(updateUri, values, "_id = ?",
                new String[] { "1" });
    }

}

另附一些系统应用常见的Uri:

关于联系人的一些URI
管理联系人的Uri:
ContactsContract.Contacts.CONTENT_URI
管理联系人的电话的Uri:
ContactsContract.CommonDataKinds.Phone.CONTENT_URI
管理联系人的Email的Uri:
ContactsContract.CommonDataKinds.Email.CONTENT_URI
(注:Contacts有两个表,分别是rawContact和Data,
rawContact记录了用户的id和name,
id栏名称为:ContactsContract.Contacts._ID,
name名称栏为ContactContract.Contracts.DISPLAY_NAME,
电话信息表的外键id为ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
电话号码栏名称为:ContactsContract.CommonDataKinds.Phone.NUMBER.
data表中Email地址栏名称为:
ContactsContract.CommonDataKinds.Email.DATA
其外键栏为:ContactsContract.CommonDataKinds.Email.CONTACT_ID)


关于多媒体的一些URI:
存储在sd卡上的音频文件:
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
存储在手机内部存储器上的音频文件:
MediaStore.Audio.Media.INTERNAL_CONTENT_URI
SD卡上的图片文件内容:
MediaStore.Audio.Images.EXTERNAL_CONTENT_URI
手机内部存储器上的图片:
MediaStore.Audio.Images.INTERNAL_CONTENT_URI
SD卡上的视频:
MediaStore.Audio.Video.EXTERNAL_CONTENT_URI
手机内部存储器上的视频:
MediaStore.Audio.Video.INTERNAL_CONTENT_URI
(注:图片的显示名栏:Media.DISPLAY_NAME,
图片的详细描述栏为:Media.DESCRIPTION
图片的保存位置:Media.DATA


关于短信URI:
Content://sms
发送箱中的短信URI:
Content://sms/outbox
收信箱中的短信URI:
Content://sms/sent
草稿中的短信URI:
Content://sms/draft

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值