Android 四大组件 (三) ContentProvider 使用简介

  在日常的开发中,对ContentProvider的接触比较少,总体的认识就是一个用于提供分享信息的渠道(可以是应用内部也可以是不同的App之间),它们通过唯一的uri进行通信。因为使用的不是特别多,所以这里也只是简单介绍一下ContentProvider的使用。主要有下面几点:

1. 创建自己的ContentProvider对外部提供数据;

2. 使用ContentResolver获取外部App提供的数据;

3. 操作系统的联系人;


相关知识简介

  Uri(Uniform Resource Identifier) 统一资源标识符

  在ContentProvider中Uri代表了要操作的数据的路径,有了Uri,系统就知道这个Uri对应的数据由哪个ContentProvider提供

  一般格式:content:// authority/标识(可选)


  UriMatcher类

  在使用Uri的时候你需要将Uri对应到具体对某个数据库的某种操作上去,UriMatcher类就是Android提供用于将Uri跟code(自定义的常量)对应起来的一个类,有了UriMatcher就     可以轻松地对应Uri跟code,然后根据不同的code执行不同的操作。


  Cursor类

  数据库游标,可以通过moveToNext等方法遍历元素。


一、创建ContentProvider对外提供服务

  这里使用对外提供联系人表为例(这里省略了具体的数据库表操作,因为我们主要是为了讲ContentProvider,我们只是简单地进行了Log操作证明别的应用调用到了数据库操作的部分)。

  创建ContentProvider的Authority(一般为包名,要保证Authority在系统中的唯一性!),这个Authority就是Uri中的authority;

private static final String AUTHORITY = "com.scut.jayme";

上面代码确定Authority为包名。


  新建一个ContentProvider类(这里就叫ContactProvider)

  ContactProvider要继承自ContentProvider并且实现其中的虚函数。

package com.scut.jayme;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;


/**
 * 提供数据的示例
 * 对外部提供用户的联系人表
 *
 * Created by jayme on 15/12/19.
 */
public class ContactProvider extends ContentProvider {
    private static final String AUTHORITY = "com.scut.jayme";
    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);

    public static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    public static final int USER_CONTACT = 0;
    public static final int USER_CONTACT_ID = 1;

    /**
     * 这里使用静态初始化块是为了让 sUriMatcher 及时得到初始化
     */
    static {
        /** UriMatcher可以将uri跟code建立关联,也可以把一个uri转化为一个code,code是自定义的用于区分操作的int变量 */
        sUriMatcher.addURI(AUTHORITY, "contact", USER_CONTACT);
        sUriMatcher.addURI(AUTHORITY, "contact/#", USER_CONTACT_ID); //#代表一个具体的数值
    }

    private static final String TAG = "contact";
    private static final String MIME_TYPE_ITEM = "com.scut.jayme.item/contact";
    private static final String MIME_TYPE_DIR = "com.scut.jayme.dir/contact";

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.i(TAG, "查询 contact表");

        /** 这里讲具体的Uri转换为code */
        int operationCode = sUriMatcher.match(uri);

        switch (operationCode){
            case USER_CONTACT:
                Log.i(TAG, "查询到了 contact表");
                break;

            case USER_CONTACT_ID:
                int id = Integer.parseInt(uri.getPathSegments().get(1));
                Log.i(TAG, "查询到了 contact表, id = " + id);
                break;
            
            default: Log.i(TAG, "Invalid request: " + uri);
        }
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        try {
            Integer.parseInt(uri.getPathSegments().get(1));
            return MIME_TYPE_ITEM;
        } catch (NumberFormatException ex) {
            return MIME_TYPE_DIR;
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {
        /** 这里讲具体的Uri转换为code */
        int operationCode = sUriMatcher.match(uri);

        switch (operationCode) {
            case USER_CONTACT:
                Log.d(TAG, "成功在 contact表 中插入一条数据");
                //Todo 执行插入数据库操作
                break;

            default:
                Log.e(TAG, "Invalid request: " + uri);
        }

        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, String where, String[] selectionArgs) {
        /** 这里讲具体的Uri转换为code */
        int operationCode = sUriMatcher.match(uri);

        //Todo 执行删除数据库操作
        switch (operationCode) {
            case USER_CONTACT:
                if (where == null) {
                    Log.d(TAG, "删除 contact表 中的所有元素");
                } else {
                    Log.d(TAG, "删除 contact表 重元素, where = " + where);
                }
                break;
            case USER_CONTACT_ID:
                int id = Integer.parseInt(uri.getPathSegments().get(1));
                Log.d(TAG, "删除 contact表 中元素, id = " + id);
                break;
            
            default:
                Log.e(TAG, "Invalid request: " + uri);
        }

        return 1;
    }

    @Override
    public int update(@NonNull Uri uri, ContentValues values, String where, String[] selectionArgs) {
        /** 这里讲具体的Uri转换为code */
        int operationCode = sUriMatcher.match(uri);

        //Todo 执行更新数据库操作
        switch (operationCode) {
            case USER_CONTACT:
                if (where == null) {
                    Log.e(TAG, "更新失败");
                } else {
                    Log.d(TAG, "更新成功, where = " + where);
                }
                break;
            case USER_CONTACT_ID:
                int id = Integer.parseInt(uri.getPathSegments().get(1));
                Log.d(TAG, "更新成功, id = " + id);
                break;
            
            default: 
                Log.e(TAG, "Invalid request: " + uri);
        }
        return 1;
    }
}

代码中的注释解释了一些操作的原因,这样我们就已经写好了一个ContentProvider


在AndroidMainfest.xml文件中配置Provider


        
        

把上面这段代码加入到application下就可以了。


二、使用ContentResolver获取数据

要获取数据有两部,第一步是知道提供数据ContentProvider的Uri,然后就是从Activity中调用getContentResolver获取到一个ContentResolver对象,然后就可以对数据进行处理了,具体可以仔细看代码中的注释。

package com.jayme.aboutcontentresolver;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;

/**
 * 测试ContentResolver操作别的App提供的数据
 * 
 * Created by Jayme on 16/01/23
 */
public class MainActivity extends Activity {

    /** 数据来源的Uri */
    public static final Uri CONTACT_URI
            = Uri.parse("content://com.scut.jayme/contact");
    
    public static final Uri CONTACT_ITEM_URI
            = Uri.parse("content://com.scut.jayme/contact/1");

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

        /**
         * 查询 CONTACT表
         * 返回一个Cursor对象即可获取到表中数据
         * Todo: 利用返回的Cursor对象获取数据
         */
        Cursor cursor = getContentResolver().query(CONTACT_URI, null, null, null, null);

        /**
         * 往 CONTACT表中插入一条数据
         * ContentValues是一个保存键值对的对像
         * 因为是在测试,所以values也是随便写的,记得要传一个ContentValues对象即可
         */
        ContentValues values = new ContentValues(1);
        values.put("name", "Jayme");
        getContentResolver().insert(CONTACT_URI, values);

        /**
         * 删除 CONTACT表 中id为1的项
         */
        getContentResolver().delete(CONTACT_ITEM_URI, null, null);

        /**
         * 更新 CONTACT表中id为1的项
         */
        getContentResolver().update(CONTACT_ITEM_URI, values, null, null);
    }
}

如果你先运行了利用ContentProvider提供数据的App,然后再运行利用使用ContentResolver使用数据的App,你就可以在提供数据App的控制台中看到相应的输出,记住,一定要先启动提供数据的App。


三、操作Android提供的联系人表

Demo截图

Android 四大组件 (三) ContentProvider 使用简介_第1张图片


获取所有联系人的信息

首先需要获取到一个ContentResolver对象,这里现在onCreate中获取到了(也就是说把下面那句代码放到onCreate里)

mContentResolver = getContentResolver();

然后利用上面的Uri查询获得一个Cursor对象就可以获取到所有联系人的信息,这里展示方式为在Log中显示。

先获取到联系人的id跟name,然后再通过联系人的id进行了一次查询操作获取到了联系人的号码(因为号码可能有多个, 所以需要遍历),切记不要传错Uri!!!

    /**
     * 显示所有手机联系人
     */
    private void showContacts(){
        Cursor cursor = mContentResolver.query(Contacts.CONTENT_URI, null, null, null, null);
        /** 这里遍历可以获取到所有联系人的 id 跟 name */
        while(cursor != null && cursor.moveToNext()){
            String id = cursor.getString(cursor.getColumnIndex(Contacts._ID));
            String name = cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME));

            /** 再进行一次查询获取对应联系人的电话号码 */
            Cursor phoneCursor = mContentResolver.query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + "=" + id, null, null);
            StringBuilder sb = new StringBuilder("contact_id = ").append(id).append(" " + name);
            while(phoneCursor != null && phoneCursor.moveToNext()){
                sb.append(" " + phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER)));
            }

            /** 关闭Cursor对象 */
            if(null != phoneCursor){
                phoneCursor.close();
            }

            /** 显示信息 */
            Log.i(TAG, sb.toString());
        }

        /** 关闭Cursor对象 */
        if(null != cursor){
            cursor.close();
        }
    }

在联系人中插入一条数据

  插入一条联系人的记录有两步,首先要通过RawContacts的Uri生成一条联系人纪录,然后再通过Data的Uri对刚刚生成记录进行信息的修改(依据RawContacts生成记录的Id)

    /**
     * 增加一条数据
     */
    private void addContact(String name, String phone){
        /** 先新建一条联系人数据并获取它对应的Id */
        ContentValues values = new ContentValues();
        Uri newContactUri = mContentResolver.insert(RawContacts.CONTENT_URI, values);
        long contactId = ContentUris.parseId(newContactUri);

        /** 添加不同信息使用的是同一个Uri,具体操作的区分是通过 MIMETYPE 来区别的 */
        
        /** 添加刚才新建联系人的姓名 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
        values.put(StructuredName.GIVEN_NAME, name);
        mContentResolver.insert(Data.CONTENT_URI, values);
        values.clear();

        /** 添加刚才新建联系人的电话 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
        values.put(Phone.NUMBER, phone);
        values.put(Phone.TYPE, Phone.TYPE_MOBILE);
        mContentResolver.insert(Data.CONTENT_URI, values);
        values.clear();

        /** 添加刚才新建联系人的邮箱 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
        values.put(Email.DATA, "[email protected]");
        values.put(Email.TYPE, Email.TYPE_WORK);
        mContentResolver.insert(Data.CONTENT_URI, values);
    }

查询电话对应的联系人

    /**
     * 查找电话对应联系人
     * @param phoneNumber 对应电话
     */
    private void queryContact(String phoneNumber){
        Uri uri = Uri.parse(Phone.CONTENT_URI + "/filter/" + phoneNumber);
        Cursor cursor = mContentResolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, null, null, null);
        if(null != cursor && cursor.moveToFirst()){
            showToast("电话 " + phoneNumber + " 对应联系人是 " + cursor.getString(0));
        }else{
            showToast("没有找到对应的联系人");
        }
        if(null != cursor){
            cursor.close();
        }
    }

删除对应姓名的联系人

    /**
     * 删除对应姓名的联系人
     * @param name 联系人姓名
     */
    private void deleteContact(String name){
        Cursor cursor = mContentResolver.query(RawContacts.CONTENT_URI, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
        if(null != cursor && cursor.moveToFirst()){
            //获取联系人对应Id
            int id = cursor.getInt(0);
            
            //根据id删除data中的相应数据
            mContentResolver.delete(RawContacts.CONTENT_URI, "display_name=?", new String[]{name});
            mContentResolver.delete(Data.CONTENT_URI, "raw_contact_id=?", new String[]{id + ""});
            showToast("delete " + name + " success");
        }
    }


最后贴出完整的MainActivity.java代码

package com.jayme.contacts;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener{

    private static final String TAG = "tag_contacts";
    
    private ContentResolver mContentResolver;

    private Button mShowContactsButton;
    private Button mInsertButton;
    private Button mQueryButton;
    private Button mDeleteButton;

    private EditText mInsertNameEditText;
    private EditText mInsertPhoneEditText;
    private EditText mQueryNameEditText;
    private EditText mDeleteNameEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContentResolver = getContentResolver();
        findViews();
        initListeners();
    }

    /**
     * 绑定控件
     */
    private void findViews(){
        mShowContactsButton = (Button) findViewById(R.id.main_btn_show_contacts);
        mInsertButton = (Button) findViewById(R.id.main_btn_add);
        mDeleteButton = (Button) findViewById(R.id.main_btn_delete);
        mQueryButton = (Button) findViewById(R.id.main_btn_query);
        mInsertNameEditText = (EditText) findViewById(R.id.main_et_add_name);
        mInsertPhoneEditText = (EditText) findViewById(R.id.main_et_add_phone);
        mQueryNameEditText = (EditText) findViewById(R.id.main_et_query_phone);
        mDeleteNameEditText = (EditText) findViewById(R.id.main_et_delete_name);
    }

    /**
     * 为按钮设置监听
     */
    private void initListeners(){
        mShowContactsButton.setOnClickListener(this);
        mDeleteButton.setOnClickListener(this);
        mInsertButton.setOnClickListener(this);
        mQueryButton.setOnClickListener(this);
    }
    
    /**
     * 显示所有手机联系人
     */
    private void showContacts(){
        Cursor cursor = mContentResolver.query(Contacts.CONTENT_URI, null, null, null, null);
        /** 这里遍历可以获取到所有联系人的 id 跟 name */
        while(cursor != null && cursor.moveToNext()){
            String id = cursor.getString(cursor.getColumnIndex(Contacts._ID));
            String name = cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME));

            /** 再进行一次查询获取对应联系人的电话号码 */
            Cursor phoneCursor = mContentResolver.query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + "=" + id, null, null);
            StringBuilder sb = new StringBuilder("contact_id = ").append(id).append(" " + name);
            while(phoneCursor != null && phoneCursor.moveToNext()){
                sb.append(" " + phoneCursor.getString(phoneCursor.getColumnIndex(Phone.NUMBER)));
            }

            /** 关闭Cursor对象 */
            if(null != phoneCursor){
                phoneCursor.close();
            }

            /** 显示信息 */
            Log.i(TAG, sb.toString());
        }

        /** 关闭Cursor对象 */
        if(null != cursor){
            cursor.close();
        }
    }

    /**
     * 查找电话对应联系人
     * @param phoneNumber 对应电话
     */
    private void queryContact(String phoneNumber){
        Uri uri = Uri.parse(Phone.CONTENT_URI + "/filter/" + phoneNumber);
        Cursor cursor = mContentResolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME}, null, null, null);
        if(null != cursor && cursor.moveToFirst()){
            showToast("电话 " + phoneNumber + " 对应联系人是 " + cursor.getString(0));
        }else{
            showToast("没有找到对应的联系人");
        }
        if(null != cursor){
            cursor.close();
        }
    }

    /**
     * 增加一条数据
     */
    private void addContact(String name, String phone){
        /** 先新建一条联系人数据并获取它对应的Id */
        ContentValues values = new ContentValues();
        Uri newContactUri = mContentResolver.insert(RawContacts.CONTENT_URI, values);
        long contactId = ContentUris.parseId(newContactUri);

        /** 添加不同信息使用的是同一个Uri,具体操作的区分是通过 MIMETYPE 来区别的 */

        /** 添加刚才新建联系人的姓名 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
        values.put(StructuredName.GIVEN_NAME, name);
        mContentResolver.insert(Data.CONTENT_URI, values);
        values.clear();

        /** 添加刚才新建联系人的电话 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
        values.put(Phone.NUMBER, phone);
        values.put(Phone.TYPE, Phone.TYPE_MOBILE);
        mContentResolver.insert(Data.CONTENT_URI, values);
        values.clear();

        /** 添加刚才新建联系人的邮箱 */
        values.put(Data.RAW_CONTACT_ID, contactId);
        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
        values.put(Email.DATA, "[email protected]");
        values.put(Email.TYPE, Email.TYPE_WORK);
        mContentResolver.insert(Data.CONTENT_URI, values);
    }

    /**
     * 删除对应姓名的联系人
     * @param name 联系人姓名
     */
    private void deleteContact(String name){
        Cursor cursor = mContentResolver.query(RawContacts.CONTENT_URI, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
        if(null != cursor && cursor.moveToFirst()){
            //获取联系人对应Id
            int id = cursor.getInt(0);

            //根据id删除data中的相应数据
            mContentResolver.delete(RawContacts.CONTENT_URI, "display_name=?", new String[]{name});
            mContentResolver.delete(Data.CONTENT_URI, "raw_contact_id=?", new String[]{id + ""});
            showToast("delete " + name + " success");
        }
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.main_btn_show_contacts:
                showToast("请在控制台查看联系人输出");
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        showContacts();
                    }
                }).start();
                break;

            case R.id.main_btn_add:
                String addName = mInsertNameEditText.getText().toString();
                String addPhone = mInsertPhoneEditText.getText().toString();
                if(!"".equals(addName) && !"".equals(addPhone)){
                    addContact(addName, addPhone);
                    showToast("新建联系人成功");
                }else {
                    showToast("联系人姓名跟手机均不能为空");
                }
                break;

            case R.id.main_btn_delete:
                String deleteName = mDeleteNameEditText.getText().toString();
                if(!"".equals(deleteName)){
                    deleteContact(deleteName);
                }else {
                    showToast("删除联系人不能为空");
                }
                break;

            case R.id.main_btn_query:
                String queryPhone = mQueryNameEditText.getText().toString();
                if(!"".equals(queryPhone)){
                    queryContact(queryPhone);
                }else{
                    showToast("查找联系人不能为空");
                }
                break;

            default:
                break;
        }
    }

    /**
     * 显示Toast信息
     * @param message 要显示的信息
     */
    protected void showToast(String message){
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

activity_main.xml代码




    
        
        
        


总结:由于本人用ContentProvider用的也很少,所以可能有些地方写的不是很准确,如果有错的话希望大家提出来,上面说的三个demo都可以去我的资源那里下载Android Studio源文件。






你可能感兴趣的:(Android)