zoukankan      html  css  js  c++  java
  • ContentProvider使用总结

      近日来学习ContentProvider相关的知识,做了一个demo,想和网友分享下。

      首先说一点相关的知识:

      一:作用

      ContentProvider是不同应用程序共享数据的接口,跟共享数据的别的方法相比,ContentProvider更好地提供了数据共享接口的统一性。CP(ContentProvider的简称)通过一张或者多张表的形式向外部应用程序提供数据(就像关系型数据库中看见的表那样)。

      二:Content URIS

      URI(Uniform Resource Identifier)统一资源标示符,能表示provider中的数据。由三部分组成,分别是scheme,authority,path.它的scheme,Android系统规定为"content://",authority唯一表示了要访问的CP名字,而path表示了要访问的CP中的数据。在使用ContentResolver对象访问CP时,需要一个Uri参数。构造Uri时可能会用到三个类:

      Uri.Builder类可用来通过String构建Uri

      ContentUris.withAppendedId()可在Uri后面加一个id

      UriMatcher:在CP中这个类可帮你从接受到Uri中选择出要执行的ACTION

      三:MIME type

      MIME,多用途互联网邮件扩展,是一种互联网标准。它的作用是:服务器会通过MIME值来告诉客户端所传送的多媒体数据的类型。CP也是一种C/S架构,所以需要使用到这个值。它的格式为:type/subtype,比如text/html,代表所传输的文本为html的格式。在Android中MIME可提供两类值:统一的MIME数据类型和定制的MIME类型字符串。在CP中前者在传输文件时会用到,而后者更常用,发送结构化的数据,譬如SQLiteDatabase时会使用定制的MIME类型字符串。在android中后者有自己的规定:

      如果返回单行数据,type被设置为"vnd.android.cursor.item"

      如果返回多行数据,type被设置为"vnd.android.cursor.dir"

      而subtype部分由CP类自己设置。

      接下来进入正题,说代码环节,这篇代码是通过结构化数据来阐述CP的。

      一:客户端

      在客户端,是通过一个ContentResolver对象作为client和CP交互的。ContentResolver对象可调用CP中的同名方法,可提供“增删改查”的功能。

      Step1:在客户端的manifest文件申请要访问的CP的权限(该权限在CP端已向系统注册,下文会讲)

    1 <uses-permission android:name="com.example.cpserver.permission" />
    View Code

      Step1.在“增删改查”之前需要判断将要请求的Uri的有效性,代码如下:

     1 //判断所要使用的Provider是否有效
     2     private boolean checkValidProvider(Uri uri)
     3     { 
     4         ContentProviderClient client = getContentResolver().acquireContentProviderClient(uri);
     5         if(client == null)
     6         {
     7             System.out.println("provider is invalid!");
     8             return false;
     9         }
    10         else
    11         {
    12             client.release();
    13             return true;
    14         }
    15     }
    View Code

      Step3:"增删改查"功能,这些方法都是SQL中DDL语言的一个封装,具体每个方法的返回值,参数意义不展开讲了,否则很长,可留言询问。

     1 private int insert()
     2     {
     3         if(!checkValidProvider(Contract.CONTENT_URI))
     4             return -1;
     5         ContentValues values = new ContentValues();
     6         values.put(Contract.COLUMN_NAME_1, "小卫的春天");
     7         values.put(Contract.COLUMN_NAME_2, "翟卫华");
     8         values.put(Contract.COLUMN_NAME_3, "100");
     9         Uri uri = getContentResolver().insert(Contract.CONTENT_URI, values);
    10         String lastPath = uri.getLastPathSegment();
    11         if(TextUtils.isEmpty(lastPath)) 
    12         {
    13             System.out.println("insert failure!");
    14         } 
    15         else 
    16         {
    17             System.out.println("insert success! the id is " + lastPath);
    18         }
    19         return Integer.parseInt(lastPath);
    20     }
    21     
    22     //删除所有行
    23     private int delete()
    24     {
    25         if(!checkValidProvider(Contract.CONTENT_URI))
    26             return -1;
    27         int count = getContentResolver().delete(Contract.CONTENT_URI, null, null);
    28         return count;
    29     }
    30     
    31     //将所有行的数据进行一个修改
    32     private int update()
    33     {
    34         if(!checkValidProvider(Contract.CONTENT_URI))
    35             return -1;
    36         ContentValues values = new ContentValues();
    37         values.put(Contract.COLUMN_NAME_1,"小宝的春天");
    38         values.put(Contract.COLUMN_NAME_2, "翟小宝");
    39         values.put(Contract.COLUMN_NAME_3, "200");
    40         int count = getContentResolver().update(Contract.CONTENT_URI, values, null, null);
    41         if(count == 0)
    42         {
    43             System.out.println("update failed!");
    44         }
    45         return count;
    46     }
    47     
    48     //row:要查找的行号, "-1"代表查找多行
    49     private void query(int row)
    50     {
    51         if(!checkValidProvider(Contract.CONTENT_URI))
    52             return;
    53         Cursor cursor = null;
    54         if(row != -1)
    55         {
    56             
    57         }
    58         else
    59         {
    60             cursor = getContentResolver().query(Contract.CONTENT_URI,
    61                     new String[]{Contract.COLUMN_NAME_1,Contract.COLUMN_NAME_2,Contract.COLUMN_NAME_3},null, null, null);
    62         }
    63         if(cursor == null)
    64             System.out.println("query failure!");
    65         else
    66         {
    67             String strDisplay = getDataFromCursor(cursor);    
    68             tvDisplay.setText(strDisplay);
    69             cursor.close();
    70         }
    71     }
    View Code

      二:CP端

      在CP端要通过继承ContentProvider来实现。CP是Android的四大组件之一,比较重要,而且系统本身就能提供很多的ContentProvider供开发者使用,比如可通过CP请求道所有的图片,音频,视频等数据。在Android系统中CP默认是可被别的任何应用程序请求到的,如果你不设置权限加以限制的话。所以第一步应该设置一个permission字段,并把它添加到provider标签里面去。

     1 <permission 
     2     android:name="com.example.cpserver.permission"
     3     android:label="Example Data"
     4     android:protectionLevel="signature" />
     5 
     6 <provider
     7             android:name="provider.TestProvider"
     8             android:authorities="com.example.cpserver.provider"
     9             android:permission="com.example.cpserver.permission"
    10             android:exported="true"
    11             android:enabled="true" />
    View Code

      Step1:实现一个SQLiteOpenHelper作为Provider的数据存储库

     1 package db;
     2 
     3 import com.example.cpserver.Contract;
     4 
     5 import android.content.Context;
     6 import android.database.sqlite.SQLiteDatabase;
     7 import android.database.sqlite.SQLiteOpenHelper;
     8 /*
     9  * 实现一个SQLiteOpenHelper作为Provider的数据存储库
    10  */
    11 public final class MainDatabaseHelper extends SQLiteOpenHelper {
    12 
    13     //创建一张表的SQL语句
    14     private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    15     Contract.TABLE_NAME + "(" +                           
    16     " _ID INTEGER PRIMARY KEY, " +
    17     Contract.COLUMN_NAME_1 + " TEXT," +
    18     Contract.COLUMN_NAME_2 + " TEXT," +
    19     Contract.COLUMN_NAME_3 + " INTEGER )";
    20     
    21     public MainDatabaseHelper(Context context) 
    22     {
    23         super(context, Contract.DB_NAME, null, 1);
    24     }
    25 
    26     /*
    27      *当Provider设法打开数据存储库,并且数据库不存在时该方法被调用
    28      */
    29     @Override
    30     public void onCreate(SQLiteDatabase db) {
    31         // Creates the main table
    32         db.execSQL(SQL_CREATE_MAIN);
    33     }
    34 
    35     @Override
    36     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    37         // TODO Auto-generated method stub
    38         
    39     }
    40 }
    View Code

      Step2:实现ContentProvider的子类,并且扩展抽象方法,代码注释很详细。

      1 package provider;
      2 
      3 import com.example.cpserver.Contract;
      4 import db.MainDatabaseHelper;
      5 import android.content.ContentProvider;
      6 import android.content.ContentUris;
      7 import android.content.ContentValues;
      8 import android.content.UriMatcher;
      9 import android.database.Cursor;
     10 import android.database.SQLException;
     11 import android.database.sqlite.SQLiteDatabase;
     12 import android.net.Uri;
     13 import android.text.TextUtils;
     14 
     15 /*
     16  * 1.除了onCreate方法,别的方法都可能会被多线程调用,所以这些方法要设计成线程安全的
     17  * 2.在onCreate中避免耗时操作
     18  * 3.虽然以下方法都要被继承,但不必重写每个方法,除了getType
     19  */
     20 public class TestProvider extends ContentProvider 
     21 {
     22     //创建一个UriMatcher对象,该对象帮你从接受的URI中选择出要执行的动作
     23     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     24     private MainDatabaseHelper mOpenHelper;
     25     private SQLiteDatabase db = null;
     26     
     27     //调用addURI方法添加provider可以识别的所有URI类型
     28     static
     29     {
     30         sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME, 1);
     31         sUriMatcher.addURI(Contract.authority, Contract.TABLE_NAME+"/#", 2);
     32     }
     33     /*
     34      * 该方法作用:初始化该Provider
     35      * 注意:直到一个ContentResolver对象访问时,该方法才被调用
     36      * 这个方法里不应做耗时的操作,因为这样可能延缓Provider对别的应用程序的相应
     37      */
     38     @Override
     39     public boolean onCreate() {
     40         mOpenHelper = new MainDatabaseHelper(getContext());
     41         return true;
     42     }
     43 
     44     @Override
     45     public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) 
     46     {
     47         int type = sUriMatcher.match(uri);
     48         switch (type)
     49         {
     50         //多行请求的URI
     51         case 1:
     52             if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
     53             break;
     54         case 2:
     55             //单行请求的URI
     56             selection = selection + "_ID = " + uri.getLastPathSegment();
     57             break;
     58         default:
     59            //如果URI不匹配,做一些错误提示,返回null或者抛出异常
     60             throw new IllegalArgumentException("Unknown URI " + uri);
     61         }
     62         if(db == null)
     63             db = mOpenHelper.getWritableDatabase();
     64         Cursor cursor = db.query(Contract.TABLE_NAME, projection, selection, selectionArgs, null, null,sortOrder);
     65         return cursor;
     66     }
     67 
     68     //返回content URI相应的MIME 类型
     69     @Override
     70     public String getType(Uri uri) 
     71     {
     72         int type = sUriMatcher.match(uri);
     73         switch (type) 
     74         {
     75         case 1:
     76             return Contract.CONTENT_TYPE;
     77         case 2:
     78             return Contract.CONTENT_ITEM_TYPE;
     79         default:
     80             throw new IllegalArgumentException("Unknown URI " + uri);
     81         }
     82     }
     83 
     84     @Override
     85     public Uri insert(Uri uri, ContentValues initialValues) 
     86     {
     87         int type = sUriMatcher.match(uri);
     88         if(type != 1)
     89             throw new IllegalArgumentException("Unknown URI " + uri);
     90         
     91         //创建一个可写数据库,将调用MainDatabaseHelper的onCreate方法,如果数据库还不存在的话
     92         if(db == null)
     93             db = mOpenHelper.getWritableDatabase();
     94         
     95         //确保所有的域都被设置
     96         ContentValues values;
     97         if (initialValues != null) 
     98             values = new ContentValues(initialValues);
     99         else 
    100             values = new ContentValues();
    101         if (values.containsKey(Contract.COLUMN_NAME_1) == false) {
    102             values.put(Contract.COLUMN_NAME_1, "");
    103         }
    104         if (values.containsKey(Contract.COLUMN_NAME_2) == false) {
    105             values.put(Contract.COLUMN_NAME_2, "");
    106         }
    107         if (values.containsKey(Contract.COLUMN_NAME_3) == false) {
    108             values.put(Contract.COLUMN_NAME_3, "");
    109         }
    110         
    111         long rowId = db.insert(Contract.TABLE_NAME,null, values);
    112         if(rowId > 0)
    113         {
    114             Uri noteUri = ContentUris.withAppendedId(Contract.CONTENT_URI, rowId);
    115             getContext().getContentResolver().notifyChange(noteUri, null);
    116             return noteUri;
    117         }
    118         throw new SQLException("Failed to insert row into " + uri);
    119     }
    120 
    121     @Override
    122     public int delete(Uri uri, String selection, String[] selectionArgs) 
    123     {
    124         if(db == null)
    125             db = mOpenHelper.getWritableDatabase();
    126         int type = sUriMatcher.match(uri);
    127         int count;
    128         switch (type)
    129         {
    130             case 1:
    131                 count = db.delete(Contract.TABLE_NAME, selection, selectionArgs);
    132                 break;
    133             case 2:
    134                 String noteId = uri.getLastPathSegment();
    135                 count = db.delete(Contract.TABLE_NAME, "_ID" + "=" + noteId +
    136                         (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
    137                 break;
    138             default:
    139                 throw new IllegalArgumentException("Unknown URI " + uri);
    140         }
    141         getContext().getContentResolver().notifyChange(uri, null);
    142         return count;
    143     }
    144 
    145     @Override
    146     public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) 
    147     {
    148         if(db == null)
    149             db = mOpenHelper.getWritableDatabase();
    150         int type = sUriMatcher.match(uri);
    151         int count;
    152         switch (type)
    153         {
    154             case 1:
    155                 count = db.update(Contract.TABLE_NAME, values, selection, selectionArgs);
    156                 break;
    157             case 2:
    158                 String noteId = uri.getLastPathSegment();
    159                 count = db.update(Contract.TABLE_NAME, values,"_ID" + "=" + noteId +
    160                         (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
    161                 break;
    162             default:
    163                 throw new IllegalArgumentException("Unknown URI " + uri);
    164         }
    165         getContext().getContentResolver().notifyChange(uri, null);
    166         return count;
    167     }
    168 
    169     //如果Provider提供file数据,要用这个方法返回MIME类型
    170     @Override
    171     public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
    172         // TODO Auto-generated method stub
    173         return super.getStreamTypes(uri, mimeTypeFilter);
    174     }
    175 
    176 }
    View Code

     

      我认为CP之所以不那么容易理解,是因为它涉及到的东西较多,还涉及到计算机网络中的的URI,MIME等概念,确实不是那么容易,作为码农的我们只能去啃了,无别的办法。总结一下,CP机制中主要涉及到这么几点知识:permission权限,uri,MIME,使用UriMatcher来匹配uri的类型,还要掌握一些基本的SQL语言等。关于使用CP来实现File分享,又是很长的一个篇幅,等得空再研究吧。我的项目用百度云做个下载链接吧,有需求的朋友可拿去用。欢迎留言交流。

    http://pan.baidu.com/s/1gdkPia3

     

      Author:Andy Zhai

      2014-02-11    20:44:00

      

      

  • 相关阅读:
    2.SpringBoot之返回json数据
    1.SpringBoot之Helloword 快速搭建一个web项目
    Jquery获取radio单选按钮的value与后面的文字
    JS点击事件的重叠处理(多个点击事件出现冲突)
    table中的td等长(不随内容大小变化)
    使用ocupload和POI一键上传Excel并解析导入数据库
    将博客搬至CSDN
    【解决方案】SSL证书报错:X509_check_private_key:key values mismatch
    如何设置开机自动重启脚本
    [Linux命令] Top命令详解
  • 原文地址:https://www.cnblogs.com/carmanloneliness/p/3545018.html
Copyright © 2011-2022 走看看