zoukankan      html  css  js  c++  java
  • Android--ContentProvider

    前言

      本篇博客讲讲ContentProvider,内容提供者。前面已经讲过了数据持久化,但是除了共享内存(SDCard)的数据外,其他包括SQLite、SharedPreferences都是仅限于被当前所创建的应用访问,而无法使它们的数据在应用程序之间交换数据,所以Android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API。虽然Android附带了需要有用的内容提供者,但是本片博客不涉及这方面的内容,而是专注讲解如何创建自己的ContentProvider,并在其他应用中如何调用。

    概述

      ContentProvider可以理解为一个Android应用对外开放的接口,只要是符合它所定义的Uri格式的请求,均可以正常访问执行操作。其他的Android应用可以使用ContentResolver对象通过与ContentProvider同名的方法请求执行,被执行的就是ContentProvider中的同名方法。所以ContentProvider很多对外可以访问的方法,在ContentResolver中均有同名的方法,是一一对应的,如图:

     

    Uri

      在Android中,Uri是一种比较常见的资源访问方式。而对于ContentProvider而言,Uri也是有固定格式的:

        <srandard_prefix>://<authority>/<data_path>/<id>

    • <srandard_prefix>:ContentProvider的srandard_prefix始终是content://。
    • <authority>:ContentProvider的名称。
    • <data_path>:请求的数据类型。
    • <id>:指定请求的特定数据。

    ContentProvider

      ContentProvider也是Android应用的四大组件之一,所以也需要在AndroidManifest.xml文件中进行配置。而且某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过这个接口来操作它的内部数据。

      Android附带了许多有用的ContentProvider,但是本篇博客不会涉及到这些内容的,以后有时间会再讲解。Android附带的ContentProvider包括:

    • Browser:存储如浏览器的信息。
    • CallLog:存储通话记录等信息。
    • Contacts:存储联系人等信息。
    • MediaStore:存储媒体文件的信息。
    • Settings:存储设备的设置和首选项信息。

      在Android中,如果要创建自己的内容提供者的时候,需要扩展抽象类ContentProvider,并重写其中定义的各种方法。然后在AndroidManifest.xml文件中注册该ContentProvider即可。

      ContentProvider是内容提供者,实现Android应用之间的数据交互,对于数据操作,无非也就是CRUD而已。下面是ContentProvider必须要实现的几个方法:

    • onCreate():初始化提供者。
    • query(Uri, String[], String, String[], String):查询数据,返回一个数据Cursor对象。
    • insert(Uri, ContentValues):插入一条数据。
    • update(Uri, ContentValues, String, String[]):根据条件更新数据。
    • delete(Uri, String, String[]):根据条件删除数据。
    • getType(Uri) 返回MIME类型对应内容的URI。

      除了onCreate()和getType()方法外,其他的均为CRUD操作,这些方法中,Uri参数为与ContentProvider匹配的请求Uri,剩下的参数可以参见SQLite的CRUD操作,基本一致,SQLite的内容在另外一篇博客中有讲解:Android--数据持久化之SQLite。、

      Tips:还有两个非常有意思的方法,必须要提一下,call()和bulkInsert()方法,使用call,理论上可以在ContentResolver中执行ContentProvider暴露出来的任何方法,而bulkInsert()方法用于插入多条数据。

      在ContentProvider的CRUD操作,均会传递一个Uri对象,通过这个对象来匹配对应的请求。那么如何确定一个Uri执行哪项操作呢?需要用到一个UriMatcher对象,这个对象用来帮助内容提供者匹配Uri。它所提供的方法非常简单,仅有两个:

    • void addURI(String authority,String path,int code):添加一个Uri匹配项,authority为AndroidManifest.xml中注册的ContentProvider中的authority属性;path为一个路径,可以设置通配符,#表示任意数字,*表示任意字符;code为自定义的一个Uri代码。
    • int match(Uri uri):匹配传递的Uri,返回addURI()传递的code参数。

      在创建好一个ContentProvider之后,还需要在AndroidManifest.xml文件中对ContentProvider进行配置,使用一个<provider.../>节点,一般只需要设置两个属性即可访问,一些额外的属性就是为了设置访问权限而存在的,后面会详细讲解:

    • android:name:provider的响应类。
    • android:authorities:Provider的唯一标识,用于Uri匹配,一般为ContentProvider类的全名。

      下面通过一个示例来讲解一下ContentProvider,在这个例子中,需要用到SQLite数据库来存储数据,定义了一个StudentDAO类,用于进行对SQLite的CRUD操作,这里就不提供数据访问的源码了,有兴趣的朋友可以在下载源码查看:

      ContentProvider实现:

    复制代码
      1 package com.example.contentproviderdemo;
      2 
      3 import com.example.dao.StudentDAO;
      4 import android.content.ContentProvider;
      5 import android.content.ContentUris;
      6 import android.content.ContentValues;
      7 import android.content.UriMatcher;
      8 import android.database.Cursor;
      9 import android.net.Uri;
     10 import android.os.Bundle;
     11 import android.util.Log;
     12 
     13 public class StudentProvider extends ContentProvider {
     14 
     15     private final String TAG = "main";
     16     private StudentDAO studentDao = null;
     17     private static final UriMatcher URI_MATCHER = new UriMatcher(
     18             UriMatcher.NO_MATCH);
     19     private static final int STUDENT = 1;
     20     private static final int STUDENTS = 2;
     21     static {
     22         //添加两个URI筛选
     23         URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",
     24                 "student", STUDENTS);
     25         //使用通配符#,匹配任意数字
     26         URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",
     27                 "student/#", STUDENT);        
     28     }
     29 
     30     public StudentProvider() {
     31 
     32     }    
     33     
     34     @Override
     35     public boolean onCreate() {
     36         // 初始化一个数据持久层
     37         studentDao = new StudentDAO(getContext());
     38         Log.i(TAG, "---->>onCreate()被调用");
     39         return true;
     40     }
     41 
     42     @Override
     43     public Uri insert(Uri uri, ContentValues values) {
     44         Uri resultUri = null;
     45         //解析Uri,返回Code
     46         int flag = URI_MATCHER.match(uri);
     47         if (flag == STUDENTS) {
     48             long id = studentDao.insertStudent(values);
     49             Log.i(TAG, "---->>插入成功, id="+id);
     50             resultUri = ContentUris.withAppendedId(uri, id);
     51         }
     52         return resultUri;
     53     }
     54 
     55     @Override
     56     public int delete(Uri uri, String selection, String[] selectionArgs) {
     57         int count = -1;
     58         try {
     59             int flag = URI_MATCHER.match(uri);
     60             switch (flag) {
     61             case STUDENT:
     62                 // delete from student where id=?
     63                 //单条数据,使用ContentUris工具类解析出结尾的Id
     64                 long id = ContentUris.parseId(uri);
     65                 String where_value = "id = ?";
     66                 String[] args = { String.valueOf(id) };
     67                 count = studentDao.deleteStudent(where_value, args);
     68                 break;
     69             case STUDENTS:
     70                 count = studentDao.deleteStudent(selection, selectionArgs);                
     71                 break;
     72             }
     73         } catch (Exception e) {
     74             e.printStackTrace();
     75         }
     76         Log.i(TAG, "---->>删除成功,count="+count);
     77         return count;
     78     }
     79 
     80     @Override
     81     public int update(Uri uri, ContentValues values, String selection,
     82             String[] selectionArgs) {
     83         int count = -1;
     84         try {            
     85             int flag = URI_MATCHER.match(uri);
     86             switch (flag) {
     87             case STUDENT:
     88                 long id = ContentUris.parseId(uri);
     89                 String where_value = " id = ?";
     90                 String[] args = { String.valueOf(id) };
     91                 count = studentDao.updateStudent(values, where_value, args);
     92                 break;
     93             case STUDENTS:
     94                 count = studentDao.updateStudent(values, selection,
     95                         selectionArgs);
     96                 break;
     97             }
     98         } catch (Exception e) {
     99             e.printStackTrace();
    100         }
    101         Log.i(TAG, "---->>更新成功,count="+count);
    102         return count;
    103     }
    104 
    105     @Override
    106     public Cursor query(Uri uri, String[] projection, String selection,
    107             String[] selectionArgs, String sortOrder) {
    108         Cursor cursor = null;
    109         try {
    110             int flag = URI_MATCHER.match(uri);
    111             switch (flag) {
    112             case STUDENT:
    113                 long id = ContentUris.parseId(uri);
    114                 String where_value = " id = ?";
    115                 String[] args = { String.valueOf(id) };
    116                 cursor = studentDao.queryStudents(where_value, args);
    117                 break;
    118             case STUDENTS:
    119                 cursor = studentDao.queryStudents(selection, selectionArgs);
    120                 break;
    121             }
    122         } catch (Exception e) {
    123             e.printStackTrace();
    124         }
    125         Log.i(TAG, "---->>查询成功,Count="+cursor.getCount());
    126         return cursor;
    127     }
    128 
    129 
    130     @Override
    131     public String getType(Uri uri) {
    132         int flag = URI_MATCHER.match(uri);
    133         String type = null;
    134         switch (flag) {
    135         case STUDENT:
    136             type = "vnd.android.cursor.item/student";
    137             Log.i(TAG, "----->>getType return item");
    138             break;
    139         case STUDENTS:
    140             type = "vnd.android.cursor.dir/students";
    141             Log.i(TAG, "----->>getType return dir");
    142             break;
    143         }
    144         return type;
    145     }
    146     @Override
    147     public Bundle call(String method, String arg, Bundle extras) {
    148         Log.i(TAG, "------>>"+method);
    149         Bundle bundle=new Bundle();
    150         bundle.putString("returnCall", "call被执行了");
    151         return bundle;
    152     }
    153 }
    复制代码

      在AndroidManifest.xml中<application>节点中增加:

    1         <provider
    2             android:name=".StudentProvider" 
    3             android:authorities="com.example.contentproviderdemo.StudentProvider" >
    4         </provider>

    ContentResolver

      ContentResolver,内容访问者。可以通过ContentResolver来操作ContentProvider所暴露处理的接口。一般使用Content.getContentResolver()方法获取ContentResolver对象。上面已经提到ContentResolver的很多方法与ContentProvider一一对应,所以它也存在insert、query、update、delete等方法。

      下面就通过一个简单的Demo来演示ContentResolver的使用,这里就不另外新建一个项目了,在原有项目上使用Android JUnit新建一个测试类,用于测试。在另外一个项目中的操作也是一样的。关于JUnit,不了解的朋友可以参见另外一篇博客:JUnit单元测试

      JUnit测试类:

    复制代码
      1 package com.example.contentproviderdemo;
      2 
      3 import android.content.ContentResolver;
      4 import android.content.ContentValues;
      5 import android.database.Cursor;
      6 import android.net.Uri;
      7 import android.os.Bundle;
      8 import android.test.AndroidTestCase;
      9 import android.util.Log;
     10 
     11 public class MyTest extends AndroidTestCase {
     12 
     13     public MyTest() {
     14         // TODO Auto-generated constructor stub
     15 
     16     }
     17 
     18     public void insert() {
     19         ContentResolver contentResolver = getContext().getContentResolver();
     20         Uri uri = Uri
     21                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");
     22         ContentValues values = new ContentValues();
     23         values.put("name", "Demo");
     24         values.put("address", "HK");
     25         Uri returnuir = contentResolver.insert(uri, values);
     26         Log.i("main", "-------------->" + returnuir.getPath());
     27     }
     28 
     29     public void delete() {
     30         ContentResolver contentResolver = getContext().getContentResolver();
     31         // 删除多行:content://com.example.contentproviderdemo.StudentProvider/student
     32         Uri uri = Uri
     33                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");
     34         contentResolver.delete(uri, null, null);
     35     }
     36 
     37     public void deletes() {
     38         ContentResolver contentResolver = getContext().getContentResolver();
     39         Uri uri = Uri
     40                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");
     41         String where = "address=?";
     42         String[] where_args = { "HK" };
     43         contentResolver.delete(uri, where, where_args);
     44     }
     45 
     46     public void update() {
     47         ContentResolver contentResolver = getContext().getContentResolver();
     48         Uri uri = Uri
     49                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");
     50         ContentValues values = new ContentValues();
     51         values.put("name", "李四");
     52         values.put("address", "上海");
     53         contentResolver.update(uri, values, null, null);
     54     }
     55 
     56     public void updates() {
     57         ContentResolver contentResolver = getContext().getContentResolver();
     58         Uri uri = Uri
     59                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");
     60         ContentValues values = new ContentValues();
     61         values.put("name", "王五");
     62         values.put("address", "深圳");
     63         String where = "address=?";
     64         String[] where_args = { "beijing" };
     65         contentResolver.update(uri, values, where, where_args);
     66     }
     67 
     68     public void query() {
     69         ContentResolver contentResolver = getContext().getContentResolver();
     70         Uri uri = Uri
     71                 .parse("content://com.example.contentproviderdemo.StudentProvider/student/2");
     72         Cursor cursor = contentResolver.query(uri, null, null, null, null);
     73         while (cursor.moveToNext()) {
     74             Log.i("main",
     75                     "-------------->"
     76                             + cursor.getString(cursor.getColumnIndex("name")));
     77         }
     78     }
     79 
     80     public void querys() {
     81         ContentResolver contentResolver = getContext().getContentResolver();
     82         Uri uri = Uri
     83                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");
     84         String where = "address=?";
     85         String[] where_args = { "深圳" };
     86         Cursor cursor = contentResolver.query(uri, null, where, where_args,
     87                 null);
     88         while (cursor.moveToNext()) {
     89             Log.i("main",
     90                     "-------------->"
     91                             + cursor.getString(cursor.getColumnIndex("name")));
     92         }
     93     }
     94 
     95     public void calltest() {
     96         ContentResolver contentResolver = getContext().getContentResolver();
     97         Uri uri = Uri
     98                 .parse("content://com.example.contentproviderdemo.StudentProvider/student");
     99         Bundle bundle = contentResolver.call(uri, "method", null, null);
    100         String returnCall = bundle.getString("returnCall");
    101         Log.i("main", "-------------->" + returnCall);
    102     }
    103 
    104 }
    复制代码

      效果演示:

      执行insert(),数据库在创建的时候存在初始数据:

      执行update():

       执行updates():

      执行query()

      执行delete()

    getType()中的MIME

      MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型。在ContentProvider中的getType方法,返回的就是一个MIME类型的字符串。如果支持需要使用ContentProvider来访问数据,就上面这个Demo,getType()完全可以只返回一个Null,并不影响效果,但是覆盖ContentProvider的getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,如果它返回的MIME type和activity在<intent filter>中定义的data的MIME type不一致,将造成activity无法启动。这就涉及到Intent和Intent-filter的内容了,以后有机会再说,这里不再详解。

      从官方文档了解到,getType返回的字符串,如果URI针对的是单条数据,则返回的字符串以vnd.android.cursor.item/开头;如果是多条数据,则以vnd.adroid.cursor.dir/开头。

    访问权限

      对于ContentProvider暴露出来的数据,应该是存储在自己应用内存中的数据,对于一些存储在外部存储器上的数据,并不能限制访问权限,使用ContentProvider就没有意义了。对于ContentProvider而言,有很多权限控制,可以在AndroidManifest.xml文件中对<provider>节点的属性进行配置,一般使用如下一些属性设置:

    • android:grantUriPermssions:临时许可标志。
    • android:permission:Provider读写权限。
    • android:readPermission:Provider的读权限。
    • android:writePermission:Provider的写权限。
    • android:enabled:标记允许系统启动Provider。
    • android:exported:标记允许其他应用程序使用这个Provider。
    • android:multiProcess:标记允许系统启动Provider相同的进程中调用客户端。

      源码下载

    总结

      以上就讲解了内容提供者的简单使用,关于内容提供者的内容涉及面很多,一篇博客无法一一说明,这里只是简单的定义一个内容提供者,并且说明如何操作它。这个博客的示例源码中,声明的内容提供者和内容访问者均在一个项目中,可能不太利于大家理解,但是在另外一个项目中的操作,与JUnit类中的操作一致,所以这里就没有另外创建一个项目去说明它,不过源码中有另外一个小项目,只是实现了Insert的操作,有兴趣的朋友对照看一下就会发现其实是一样的。

  • 相关阅读:
    R.java文件介绍
    Windows CE Notification API的使用方法
    Vue.js组件的重要选项
    Vue.js如何划分组件
    细数那些年我用过的前端开发工具
    前端几个常用简单的开发手册拿走不谢
    Bootstrap相关优质项目学习清单
    前端页面实现报警器提示音效果
    人工智能面试题86问,新手找工作必备!
    人工智能面试题86问,新手找工作必备!
  • 原文地址:https://www.cnblogs.com/shitaotao/p/7635762.html
Copyright © 2011-2022 走看看