Android学习笔记3 四大组件之Content Provider

2016.7.24 第一次更新:加入了自定义Content Provider的相关内容,完全解析内容提供者的用法。

Content Provider,内容提供者,相信大家对这个组件的名字都不陌生,可能是自己平时做的都是一些简单的App,所以对于Content Provider的使用并不是很多,也不是特别熟悉。但是这里还是对Content Provider作个简单的总结,不是很深入,但是希望能给包括我在内的初学者一点帮助,看完这篇博客能对这个组件有个总体上的了解。

一、Content Provider是什么
二、Content Provider简单使用
三、Content Provider自定义
四、Content Provider小结
五、源码链接

一、Content Provider是什么

首先我们可以思考一个问题,假如有一个类似通讯录的App,管理着手机上联系人数据,如果不允许手机上别的应用对它的联系人数据进行增删改查,那么这个App还会有人使用吗?答案肯定是否定的,那么这个应用怎么把数据访问接口提供给别的App同时又能保证别的App在访问数据时能有一定的限制呢,这时Content Provider就派上用场了。

组件Content Provider是为了在不同应用程序之间进行数据交换的的标准API。比如,一个应用有一些数据,它可以允许别的应用来访问这些数据,那么它可以通过ContentProvider来暴露访问这些数据的接口,当别的应用想要访问这些数据时可以通过ContentResolver来操作,包括增删改查等等。

下面的内容是官方文档上的几段话。

Content providers manage access to a structured set of data. They encapsulate the data, and provide mechanisms for defining data security. Content providers are the standard interface that connects data in one process with code running in another process.
内容提供者管理着结构化数据集合的访问。它封装好数据,并提供了用于定义数据安全性的机制。内容提供者是连接不同进程之间的数据的标准接口。


When you want to access data in a content provider, you use the ContentResolver to communicate with the provider as a client. The ContentResolver object communicates with the provider object, an instance of a class that implements ContentProvider. The provider object receives data requests from clients, performs the requested action, and returns the results.
当你想要访问一个内容提供者的数据时,这时你就作为客户端client使用解析者ContentResolver来与提供者交互,ContentResolver对象来与实现ContentProvider的类的实例进行交互,提供者接收客户端的数据请求后来执行请求并返回数据


Android itself includes content providers that manage data such as audio, video, images, and personal contact information. You can see some of them listed in the reference documentation for the android.provider package. With some restrictions, these providers are accessible to any Android application.
Android本身包含了一些内容提供者来管理诸如audio(音频),video(视频),images(图像)以及联系人信息等数据。我们可以在android.provider包内查看这些,这些提供者可以一定条件下可以被任何应用访问。

二、Content Provider简单使用

下面是一个简单的例子,我们可以通过ContentResolver内容解析者来访问系统里通讯录里面的联系人信息。

1.新建布局文件 layout_activity_cp.xml




    

2.新建Activity

public class ContentProviderActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //设置布局
        setContentView(R.layout.layout_activity_cp);

        //按钮点击事件
        Button btnReadContact = (Button) findViewById(R.id.btn_read_contact);
        btnReadContact.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Cursor mCursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
                        null, null, null, null);

                while (mCursor.moveToNext()) {

                    String name = mCursor.getString(mCursor.getColumnIndex
                            (ContactsContract.Contacts.DISPLAY_NAME));

                    Log.e("---contactname---", name);

                }

            }
        });

    }
}

3.清单文件 AndroidManifest.xml 里添加读取联系人的权限,同时别忘了要注册Activity。

 

这样,运行后查看输出结果如下图所示:

输出联系人姓名.png

这里我们可以看到,通讯里的联系人姓名都已经输出了,如果没有看到,记得看看你的通讯录里是不是没有添加任何联系人,哈哈。

上面的代码其实看着很简单,主要逻辑如下:

  1. 点击按钮后,使用方法getContentResolver()获取内容解析者。
  2. 使用query方法,ContactsContract.Contacts.CONTENT_URI表示查询联系人的数据,查询结果集合放在游标mCursor里。
  3. 循环遍历游标,ContactsContract.Contacts.DISPLAY_NAME表示联系人姓名,传给getColumnIndex即可得到姓名在查询结果mCursor里的索引,再调用getString根据索引即可获取联系人的姓名。

上面的代码仔细看看应该不会有难度,到这里我相信大家应该肯定都不会就此停止,已经输出了姓名,那么能否输出联系人的号码呢?答案是肯定的,不过较输出姓名来说更复杂一点。

Android系统对于联系人内容提供者的管理有着一整套完备的规范,具体我也讲不清楚,但是我们可以简单记住一点,联系人的数据并不是存在一个Uri中,在刚才我们查询联系人的数据时其实结果集合并没有联系人的号码,联系人的手机号码的Uri为:ContactsContract.CommonDataKinds.Phone.CONTENT_URI。

修改上面的代码,如下:

public class ContentProviderActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //设置布局
        setContentView(R.layout.layout_activity_cp);

        //按钮点击事件
        Button btnReadContact = (Button) findViewById(R.id.btn_read_contact);
        btnReadContact.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //获得内容解析者
                ContentResolver resolver = getContentResolver();

                //查询联系人并把结果集合放到Cursor实例里
                Cursor mCursor = resolver.query(ContactsContract.Contacts.CONTENT_URI,
                        null, null, null, null);

                //从结果集合的第一项开始逐项访问
                while (mCursor.moveToNext()) {

                    //联系人姓名 ID 是否有号码
                    String conName;
                    String conPhoneId;
                    int conFlag;

                    //得到指定列名的索引
                    int idConName = mCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
                    int idConPhoneId = mCursor.getColumnIndex(ContactsContract.Contacts._ID);
                    int idConFlag = mCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);


                    //根据索引得到值
                    conName = mCursor.getString(idConName);
                    conPhoneId = mCursor.getString(idConPhoneId);
                    conFlag = mCursor.getInt(idConFlag);

                    Log.e("---ccc---", "姓名:" + conName + ",ID为" + conPhoneId + ",是否有号码" + conFlag);

                    //如果有手机号码
                    if (conFlag > 0) {

                        //查询联系人号码
                        Cursor mCursor2 = getContentResolver().query(
                                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                null,
                                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + conPhoneId,
                                null,
                                null);

                        //循环遍历查询到的号码
                        while (mCursor2.moveToNext()) {

                            String phoneNumber = mCursor2.getString(
                                    mCursor2.getColumnIndex(
                                            ContactsContract.CommonDataKinds.Phone.NUMBER));

                            Log.e("---ccc---", "号码为" + phoneNumber);


                        }
                    } else {
                        //如果没有号码
                        Log.e("---ccc---", "该联系人未添加手机号码!");
                    }
                }

            }
        });

    }
}

代码比较多,但是搞清楚逻辑的话,理解起来也很简单,我加上了详细的注释,这里再简单梳理一下:

  1. 首先是获取内容解析者查询系统联系人的数据得到结果。
  2. 对结果进行循环遍历。
  3. 之后定义了三个变量,联系人姓名、联系人ID、联系人是否有号码,根据列名获取索引,再根据索引获取值。这里,联系人ID之后在查询联系人号码时根据ID可以查询到号码。
  4. 之后如果联系人有号码,查询联系人号码的uri,并循环遍历输出结果,如果没有电话,输出提示。

三、Content Provider自定义

上面的内容主要是访问系统已经定义好的内容提供者中的数据,如果我们自己开发的应用需要共享数据给别的应用,允许别的应用访问,那么我们可以自己创建一个内容提供者,来控制外界的访问。这部分源码链接在文末。

1、步骤

  1. SQLite数据库表,用于存储数据。
  2. 创建一个继承自ContentProvider的子类,在其中完成数据操作。
  3. AndroidManifest.xml文件中声明provider
  4. 外部访问

2、用法解析

  1. 创建数据库。这里用SQLite数据库保存数据。


    Android学习笔记3 四大组件之Content Provider_第1张图片
    数据库管理类
  2. 新建继承自ContentProvider的子类,完成基本操作。


    Android学习笔记3 四大组件之Content Provider_第2张图片
    部分截图

    Android学习笔记3 四大组件之Content Provider_第3张图片
    添加操作 返回添加的记录的uri
  3. 在清单文件里声明provider。


    Android学习笔记3 四大组件之Content Provider_第4张图片
    Paste_Image.png
  4. 数据访问


    Android学习笔记3 四大组件之Content Provider_第5张图片
    函数 利用内容解析者添加数据

通过以上几步,就可以完成对数据共享了。更多详细的内容可以参考源码。

四、Content Provider小结

Content Provider主要涉及的几个名词有uri,ContentResolver,ContentProvider。一个应用想要对其它应用暴露一部分数据,可以指定这部分数据的标识,uri,并定义自己的子类ContentProvider来提供一整套数据接口,比如增删改查,别的应用访问这部分数据时利用内容解析者ContentResolver,之后委托给ContentProvider实现请求的操作完成对数据的访问。

五、源码链接

https://git.oschina.net/tanshicheng/DemoDataStorage.git

你可能感兴趣的:(Android学习笔记3 四大组件之Content Provider)