• Android学习笔记37-使用Content Providers方式共享数据


    在Android中一共提供了5种数据存储方式,分别为:

      (1)Files:通过FileInputStream和FileOutputStream对文件进行操作。具体使用方法可以参阅博文《Android学习笔记34:使用文件存储数据》。

      (2)Shared Preferences:常用来存储键值对形式的数据,对系统配置信息进行保存。具体使用方法可以参阅博文《Android学习笔记35:使用Shared Preferences方式存储数据》。

      (3)Content Providers:数据共享,用于应用程序之间数据的访问。

      (4)SQLite:Android自带的轻量级关系型数据库,支持SQL语言,用来存储大量的数据,并且能够对数据进行使用、更新、维护等操作。具体使用方法可以参阅博文《Android学习笔记36:使用SQLite方式存储数据》。

      (5)Network:通过网络来存储和获取数据。

      本篇博文介绍第三种方式,通过Content Providers实现应用程序之间的数据共享。

    1.Content Providers简介

      在Android系统中,不存在一个公共的数据存储区供所有的应用程序访问,也就是说数据在各个应用程序中是私有的。那么,如何在一个应用程序中访问另一个应用程序中的数据,实现应用程序之间的数据共享呢?

      当然,你可以通过《Android学习笔记34:使用文件存储数据》一文中讲到的设置openFileOutput()方法中的第二个参数mode为Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE,让别的应用程序可以读写该应用程序中的文件。但是,使用这种方式的弊端也是显而易见的,不仅需要知道该文件的存储路径,而且会将该文件内容完全的暴露出去,对于内容提供者和内容访问者来说都是不方便和不安全的。

      为此,Android系统提供了Content Providers,用以方便安全的实现应用程序间的数据共享。

    1.1ContentResolver

      所有的Content Providers都会实现一些共同的接口,包括数据的查询、添加、更改和删除。在应用程序中,我们可以通过使用getContentResolver()方法来取得一个ContentResolver对象,然后就可以通过这个ContentResolver对象来操作你需要的Content Provider了。ContentResolver类提供的用来操作Content Provider的方法主要有insert()、delete()、update()和query()。

      通常,对于开发者而言,并不需要同Content Provider对象直接打交道。系统运行时,会将所有的ContentProvider对象实例化,对于每一种类型的ContentProvider只有一个实例。这个实例可以与在不同的程序或进程中的多个ContentResolver对象进行通信。而这些进程间的交互则是由ContentResolver和ContentProvider类进行处理的。

      对于Content Providers而言,最重要的就是数据存储结构和URI。

    1.2数据存储结构

      Content Providers将其存储的数据以数据表的形式提供给访问者,在数据表中,每一行为一条记录,每一列为具有特定类型和意义的数据。比如,联系人的Content Provider数据存储结构如图1所示。

    图1 Content Provider数据存储结构示例

      可以看出,每条记录都有一个_ID字段用来唯一的标识该记录,类似于数据库中的主键。

    1.3URI

      每一个Content Provider都对外提供一个能够唯一标识自己数据集的公开URI,如果一个Content Provider管理多个数据集,则需要为每一个数据集都分配一个独立的URI。

      Android规定,所有Content Provider的URI都必须以“content://”开头。通常,URI由3部分组成:“content://”、数据的路径、标识ID(可选)。

      比如,以下是系统提供的一些URI:

      (1)content://media/internal/images

      (2)content://contacts/people/2

      (3)content://contacts/people

      其中,(1)将返回设备上存储的所有图片;(2)将返回联系人信息中ID为5的联系人记录;(3)将返回设备上所有的联系人信息。

      每个ContentResolver对象都将URI作为其第一个参数,URI决定了ContentResolver将与哪一个Content Provider对话。

    2.获取Content Provider内容

      Android系统为一些常见的数据类型(如音频、视频、图像、通讯录联系人等)内置了一系列的Content Provider。以下就以通讯录联系人为例,讲讲如何获取Content Provider内容。

      首先,我们需要在模拟器中运行“联系人”应用程序程序,并在其中添加联系人。如图2所示。

    图2 添加联系人

      如图2所示,我在“联系人”应用程序程序中添加了两个联系人:李明和王磊。

      然后,我们需要创建一个自己的工程,该工程的主要功能就是得到持有联系人信息的Content Provider中的数据。这里,我在布局文件中定义了一个TextView控件,用来将获得的联系人数据显示出来,运行后的效果如图3所示。

    图3 获取Content Provider内容

      由图3可以看出,我们自己创建的应用程序确实从“联系人”应用程序程序中获得了数据(ID和Name字段),当然,如果你需要,你可以获取图1中的更多的字段信息。

      下面的代码给出了实现这一功能的一种方案。

    复制代码
     1     /*
     2      * Function  :    获取联系人列表信息
     3      * Author    :    博客园-依旧淡然
     4      */
     5     public String getResult() {
     6         
     7         String result = "";
     8         Uri uri = Uri.parse("content://contacts/people");        //联系人Content Provider的URI
     9         String[] columns = {People._ID, People.NAME};            //联系人的ID和Name
    10         
    11         ContentResolver contentResolver = getContentResolver();     //获取ContentResolver对象
    12         Cursor cursor = contentResolver.query(uri, columns, null, null, null);    //查询Content Provider
    13             int peopleId = cursor.getColumnIndex(People._ID);       //获得ID字段的列索引
    14             int peopleName = cursor.getColumnIndex(People.NAME);    //获得Name字段的列索引
    15             
    16         //遍历Cursor对象,提取数据
    17         for(cursor.moveToFirst(); (!cursor.isAfterLast()); cursor.moveToNext()) {
    18             result = result + cursor.getString(peopleId) + "		";
    19             result = result + cursor.getString(peopleName) + "	
    ";
    20         }
    21         cursor.close();
    22         return result;
    23     }
    复制代码

      通过以上的代码可以看出,要获取Content Provider内容,我们需要知道Content Provider的URI以及Content Provider的数据存储形式(字段名称和字段类型)。然后,我们便可以通过使用ContentResolver对象的query()方法对Content Provider进行查询了,查询的结果需要使用Cursor对象存储(有关Cursor的介绍可以参阅《Android学习笔记36:使用SQLite方式存储数据》一文)。最后遍历Cursor对象,取出各个字段的信息即可。

      因为该应用程序需要访问联系人信息,所以还需要在AndroidManifest.xml文件中加入相应的权限许可,具体如下:

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

      至此,我们便完成了获取联系人Content Provider内容的功能。

     

    3.提供Content Provider内容

      上面介绍了如何从别的应用程序中获取Content Provider内容,那么如何在自己的应用程序中提供Content Provider内容供别的应用程序访问呢?

      一般来说,让自己的数据被别的应用程序访问有两种方式:创建自己的Content Provider(即继承自Content Provider的子类),或者是将自己的数据加入到已有的Content Provider中去。将自己的数据加入到已有的Content Provider中去有一定的局限性,因为要保证自己的数据和现有的Content Provider数据类型相同,并且具有该Content Provider的写入权限。

      下面将说说如何创建一个自己的Content Provider,大致可以分为3个步骤。

    3.1建立数据的存储系统

      很显然,要将自己应用程序中的数据共享给他人,肯定需要建立自己的数据存储系统。当然了,选择什么样的存储系统(文件存储系统、SQLite数据库等)完全由开发者决定。

      在《Android学习笔记36:使用SQLite方式存储数据》一文中,我们搭建了一个简单的SQLite数据库系统。在该数据库中,我们新建了一张具有3个字段(studentId、studentName、studentAge)的表,用来存储学生信息。

      这里,我们就以该工程为例,讲讲如何将该工程中的学生信息通过Content Provider方式共享出去。

    3.2扩展Content Provider类

      在该工程中,我们需要新建了一个继承自ContentProvider的类。用来将要共享的数据进行包装并以ContentResolver对象和Cursor对象能够访问的形式对外展示。这里,我将这个类命名为了“StudentContentProvider”。

      在ContentProvider类中提供了6个抽象方法,分别为:

      (1)public abstract Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);

      (2)public abstract Uri insert (Uri uri, ContentValues values);

      (3)public abstract int update (Uri uri, ContentValues values, String selection, String[] selectionArgs);

      (4)public abstract int delete (Uri uri, String selection, String[] selectionArgs);

      (5)public abstract String getType (Uri uri);

      (6)public abstract boolean onCreate ();

      其中,query()方法用于将查询到的数据以Cursor对象的形式返回;insert()方法用于向Content Provider中插入新数据记录,该方法中的第二个参数ContentValues对象表示数据记录的列名和列值的映射;update()方法用于更新Content Provider中的已存在的数据记录;delete()方法用于从Content Provider中删除数据记录;getType()方法用于返回Content Provider中数据的(MIME)类型;onCreate()方法当Content Provider启动时被调用。

      以上的6个方法将会在ContentResolver对象中被调用,所以很好的实现这些抽象方法就会为ContentResolver提供一个完善的外部接口。

      当然了,你可以根据自己应用程序的需要,有选择的实现上述6个方法,比如,你可以只实现query()方法,这样别的应用程序就自能对你提供的Content Provider进行查询操作,而无法对你提供的Content Provider进行添加、删除等操作,从而保证了数据的安全性。

      如下的代码实现了query()方法。

    复制代码
    1     /*
    2      * Function  :    查询方法
    3      * Author    :    博客园-依旧淡然
    4      */
    5     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    6         db = mySQLiteOpenHelper.getWritableDatabase();
    7         Cursor cursor = db.query("tab_student", projection, selection, selectionArgs, null, null, sortOrder);
    8         return cursor;
    9     }
    复制代码

      可以看出,查询的核心其实还是调用SQLiteDatabase类提供的query()方法,将查询到的结果存储在Cursor对象中,最后直接将Cursor对象返回即可。

      此外,在继承自ContentProvider类的“StudentContentProvider”中,我们还需要做一件很重要的事,那就是指定Content Provider的URI。该URI必须是唯一的,不能和系统的URI相同,更不能与其他应用程序提供的Content Provider的URI相同。

      这里我定义了Content Provider的URI为“content://com.example.sqlite.studentProvider/student”。

    3.3声明Content Provider的权限

      创建好的Content Provider必须在应用程序的AndroidManifest.xml文件中进行声明,否则,该Content Provider对于Android系统是不可见的。

      具体的声明方式如下:

    1     <!-- 声明内容提供者 -->
    2     <provider                    
          android:name="com.example.android_datastorage_sqlite.provider.StudentContentProvider" 3 android:authorities="com.example.sqlite.studentProvider" > 4 </provider>

      其中,<provider></provider>标签位于<application></application>标签下。android:name属性用于指明StudentContentProvider的全称类名,android:authorities属性唯一的标识了一个Content Provider。

      至此,我们便在该工程中创建了自己的Content Provider,并提供了query()方法供别的应用程序查询该工程中的SQLite数据表。

    3.4验证

      在应用程序Android_DataStorage_SQLite的SQLite数据表中,我们添加了3条记录,如图4所示。


    图4 SQLite数据表信息

      新建一个应用程序Android_DataStorage_ContentProviders,用来获取自定义的Content Provider内容。获取自定义的Content Provider内容的方法,和前面讲的获取联系人应用程序的Content Provider内容的方法类似。使用自定义的Content Provider的URI,并查询数据表中相应的字段即可。可以看到查询到的信息如图5所示。

    图5 查询自定义的Content Provider内容

      可以看出,在应用程序Android_DataStorage_ContentProviders中确实访问到了应用程序Android_DataStorage_SQLite中的数据表信息,通过Content Provider方式实现了数据在应用程序之间的共享。

  • 相关阅读:
    第一节:SpringMVC概述
    SpringMVC【目录】
    Windows 系统快速查看文件MD5
    (error) ERR wrong number of arguments for 'hmset' command
    hive使用遇到的问题 cannot recognize input
    Overleaf支持的部分中文字体预览
    Understanding and Improving Fast Adversarial Training
    Django2实战示例 第十三章 上线
    Django2实战示例 第十二章 创建API
    Django2实战示例 第十一章 渲染和缓存课程内容
  • 原文地址:https://www.cnblogs.com/britalient/p/3173257.html
Copyright © 2020-2023  润新知