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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:gravity="center"
              android:orientation="vertical">

    <Button
        android:id="@+id/btn_read_contact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="读取联系人数据"/>
</LinearLayout>

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。

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

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

输出联系人姓名.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数据库保存数据。


    数据库管理类
  2. 新建继承自ContentProvider的子类,完成基本操作。


    部分截图

    添加操作 返回添加的记录的uri
  3. 在清单文件里声明provider。


    Paste_Image.png
  4. 数据访问


    函数 利用内容解析者添加数据

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

四、Content Provider小结

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

五、源码链接

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容