• ContentProvider数据访问详解


    ContentProvider数据访问详解

     

      Android官方指出的数据存储方式总共有五种:Shared Preferences、网络存储、文件存储、外储存储、SQLite,这些存储方式一般都只是在一个单独的应用程序中实现数据的共享,而对于需要操作其他应用程序中的数据时(如媒体库、通讯录等),可能就需要借助ContentProvider了。

     

    1ContentProvider

      ContentProvider为存储和获取数据提供了统一的接口,使用表的形式来对数据进行封装,使得开发者在后续的开发过程中不用关心数据存储的细节。使用ContentProvider可以在不同的应用程序之间共享数据,Android为常见的数据类型提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。

      总的来说,利用ContentProvider来实现共享数据的好处是统一了数据的访问方式。

     

    2URIUniform Resource Identifier

      URI为系统中的每一个资源赋予一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,用于表示ContentProvider所提供的数据。 Android所提供的ContentProvider都位于android.provider包中, 可以将URI分为A、B、C、D 4个部分来理解。如对于content://com.wang.provider.myprovider/tablename/id:

      a、标准前缀——content://,用来说明一个Content Provider控制这些数据;

      b、URI的标识——com.wang.provider.myprovider,用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities属性中说明,一般是定义该ContentProvider的包.类的名称;

      c、路径——tablename,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;

      d、记录ID——id,如果URI中包含表示需要获取的记录的ID,则返回该id对应的数据,如果没有ID,就表示返回全部;

      对于第三部分路径(path)做进一步的解释,用来表示要操作的数据,构建时应根据实际项目需求而定。如:

          a、操作tablename表中id为11的记录,构建路径:/tablename/11;

          b、操作tablename表中id为11的记录的name字段:tablename/11/name;

          c、操作tablename表中的所有记录:/tablename;

          d、操作来自文件、xml或网络等其他存储方式的数据,如要操作xml文件中tablename节点下name字段:/ tablename/name;

          e、若需要将一个字符串转换成Uri,可以使用Uri类中的parse()方法,如:

    1 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");

     

    3、UriMatcher

      Uri代表要操作的数据,在开发过程中对数据进行获取时需要解析Uri,Android提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的基本概念和使用方法,对一个Android开发者来说是一项必要的技能。

      UriMatcher类用于匹配Uri,它的使用步骤如下:

      a、将需要匹配的Uri路径进行注册,代码如下:

    1 //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
    2 UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    3 //如果match()方法匹配“content://com.wang.provider.myprovider/tablename”路径,返回匹配码为1
    4 sMatcher.addURI("content://com.wang.provider.myprovider", " tablename ", 1);
    5  //如果match()方法匹配content://com.wang.provider.myprovider/tablename/11路径,返回匹配码为2
    6 sMatcher.addURI("com.wang.provider.myprovider", "tablename/#", 2);

      注意,添加第二个URI时,路径后面的id采用了通配符形式“#”,表示只要前面三个部分都匹配上了就OK。

      b、注册完需要匹配的Uri后,可以使用sMatcher.match(Uri)方法对输入的Uri进行匹配,如果匹配就返回对应的匹配码,匹配码为调用addURI()方法时传入的第三个参数。

     1 switch (sMatcher.match(Uri.parse("content://com.zhang.provider.yourprovider/tablename/100"))) {
     2    case 1:
     3      //match 1, todo something
     4      break;
     5    case 2
     6      //match 2, todo something
     7      break;
     8    default:
     9      //match nothing, todo something
    10      break;
    11 }

    4ContentUris

      ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:withAppendedId(Uri uri, long id)和parseId(Uri uri)。

      withAppendedId(Uri uri, long id)用于为路径加上ID部分:

    1 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
    2 Uri resultUri = ContentUris.withAppendedId(uri, 10);

          parseId(Uri uri)则从路径中获取ID部分:

    1 Uri uri = Uri.parse("content://com.zhang.provider.myprovider/tablename/10")
    2 long personid = ContentUris.parseId(uri);

     

    5ContentProvider数据共享

      ContentProvider类主要方法的介绍:

      public boolean onCreate(),在ContentProvider创建后就会被调用,而ContentProvider是在其它应用第一次访问它时被创建;

      public Uri insert(Uri uri, ContentValues values),供外部应用向ContentProvider添加数据;

      public int delete(Uri uri, String selection, String[] selectionArgs),供外部应用从ContentProvider删除数据;

      public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),供外部应用更新ContentProvider中的数据;

      public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),供外部应用从ContentProvider中获取数据;

      public String getType(Uri uri),返回当前Uri所代表数据的MIME类型;

      如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,如要得到所有tablename记录的Uri为content://com.wang.provider.myprovider/tablename,那么返回的MIME类型字符串应该为:vnd.android.cursor.dir/table。

      如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,如得到id为10的tablename记录,Uri为content://com.wang.provider.myprovider/tablename/10,那么返回的MIME类型字符串为:vnd.android.cursor.item/tablename 。

     

    6、ContentResolver操作数据

      当外部应用需要对ContentProvider中的数据进行添加、删除、修改及查询操作时,可以使用ContentResolver 类来完成。而要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。

      ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

      public Uri insert(Uri uri, ContentValues values),往ContentProvider添加数据;

      public int delete(Uri uri, String selection, String[] selectionArgs),从ContentProvider删除数据;

      public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs),更新ContentProvider中的数据;

      public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder),从ContentProvider中获取数据;

      这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,其实和ContentProvider里面的方法是一样的。他们所对应的数据,最终会被传到我们在之前程序里面定义的那个ContentProvider类的方法,假设给定的是:Uri.parse("content://com.wang.provider.myprovider/tablename/10"),那么将会对主机名为com.wang.provider.myprovider的ContentProvider进行操作,操作的数据为tablename表中id为10的记录。

          使用ContentResolver对ContentProvider中的数据进行操作的代码如下:

     1 ContentResolver resolver = getContentResolver();
     2 Uri uri = Uri.parse("content://com.wang.provider.myprovider/tablename");
     3 //添加一条记录
     4 ContentValues values = new ContentValues();
     5 values.put("name", "wang1");
     6 values.put("age", 28);
     7 resolver.insert(uri, values); 
     8 //获取tablename表中所有记录
     9 Cursor cursor = resolver.query(uri, null, null, null, "tablename data");
    10 while(cursor.moveToNext()){
    11    Log.i("ContentTest", "tablename_id="+ cursor.getInt(0)+ ", name="+ cursor.getString(1));
    12 }
    13 //把id为1的记录的name字段值更改新为zhang1
    14 ContentValues updateValues = new ContentValues();
    15 updateValues.put("name", "zhang1");
    16 Uri updateIdUri = ContentUris.withAppendedId(uri, 2);
    17 resolver.update(updateIdUri, updateValues, null, null);
    18 //删除id为2的记录,即字段age
    19 Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
    20 resolver.delete(deleteIdUri, null, null);

    7、监听数据变化

      如果ContentProvider的访问者需要知道数据发生的变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者。只给出类中监听部分的代码:

    1 public class MyProvider extends ContentProvider {
    2    public Uri insert(Uri uri, ContentValues values) {
    3       db.insert("tablename", "tablenameid", values);
    4       getContext().getContentResolver().notifyChange(uri, null);
    5    }
    6 }

      而访问者必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:

     1 getContentResolver().registerContentObserver(Uri.parse("content://com.ljq.providers.personprovider/person"),
     2        true, new PersonObserver(new Handler()));
     3 public class PersonObserver extends ContentObserver{
     4    public PersonObserver(Handler handler) {
     5       super(handler);
     6    }
     7    public void onChange(boolean selfChange) {
     8       //to do something
     9    }
    10 }

    8、完整项目代码

      a、对于实际测试中的Uri——com.wang.provider.myprovider,需要在AndroidManifest.xml文件中进行说明:

    1 <provider android:name="MyProvider" android:authorities="com.wang.provider.myprovider" />

      b、定义Profile类,其主要负责定义Uri各组成部分名称对应的String常量:

     1 package com.wang.testcontentprovider;
     2 
     3 import android.net.Uri;  
     4 
     5 public class Profile {  
     6       
     7     /** 
     8      * 表格名称 
     9      */  
    10     public static final String TABLE_NAME = "tablename";  
    11       
    12     /** 
    13      * 列表一,_ID,自动增加 
    14      */  
    15     public static final String COLUMN_ID = "_id";  
    16       
    17     /** 
    18      * 列表二,名称 
    19      */  
    20     public static final String COLUMN_NAME = "name";  
    21        
    22     public static final String AUTOHORITY = "com.wang.provider.myprovider";  
    23     public static final int ITEM = 1;  
    24     public static final int ITEM_ID = 2;  
    25        
    26     public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.wang.tablename";  
    27     public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.wang.tablename";  
    28        
    29     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTOHORITY + "/tablename");  
    30 }  

      c、实现存放数据的DBHelper类,继承自SQLiteOpenHelper类,完成数据库的一些常规操作:

     1 package com.wang.testcontentprovider;
     2 
     3 import android.content.Context;  
     4 import android.database.SQLException;  
     5 import android.database.sqlite.SQLiteDatabase;  
     6 import android.database.sqlite.SQLiteOpenHelper;  
     7   
     8 public class DBHelper extends SQLiteOpenHelper {  
     9   
    10     /** 
    11      * 数据库名称 
    12      */  
    13     private static final String DATABASE_NAME = "test.db";    
    14       
    15     /** 
    16      * 数据库版本 
    17      */  
    18     private static final int DATABASE_VERSION = 1;    
    19   
    20     public DBHelper(Context context) {  
    21         super(context, DATABASE_NAME, null, DATABASE_VERSION);  
    22     }  
    23   
    24     @Override  
    25     public void onCreate(SQLiteDatabase db)  throws SQLException {  
    26         //创建表格  
    27         db.execSQL("CREATE TABLE IF NOT EXISTS "+ Profile.TABLE_NAME + "("+ Profile.COLUMN_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," + Profile.COLUMN_NAME +" VARCHAR NOT NULL);");  
    28     }  
    29   
    30     @Override  
    31     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)  throws SQLException {  
    32         //删除并创建表格  
    33         db.execSQL("DROP TABLE IF EXISTS "+ Profile.TABLE_NAME+";");  
    34         onCreate(db);  
    35     }  
    36 }  

      d、MyProvider类的实现,主要负责向ContentProvider中添加数据

     1 package com.wang.testcontentprovider;
     2 
     3 import android.content.ContentProvider;    
     4 import android.content.ContentUris;    
     5 import android.content.ContentValues;    
     6 import android.content.UriMatcher;    
     7 import android.database.Cursor;    
     8 import android.database.SQLException;    
     9 import android.database.sqlite.SQLiteDatabase;    
    10 import android.net.Uri;    
    11     
    12 public class MyProvider extends ContentProvider {    
    13     
    14     DBHelper mDbHelper = null;    
    15     SQLiteDatabase db = null;    
    16     
    17     private static final UriMatcher mMatcher;    
    18     static{    
    19         mMatcher = new UriMatcher(UriMatcher.NO_MATCH);    
    20         mMatcher.addURI(Profile.AUTOHORITY,Profile.TABLE_NAME, Profile.ITEM);    
    21         mMatcher.addURI(Profile.AUTOHORITY, Profile.TABLE_NAME+"/#", Profile.ITEM_ID);    
    22     }    
    23     
    24     @Override    
    25     public int delete(Uri uri, String selection, String[] selectionArgs) {    
    26         // TODO Auto-generated method stub    
    27         return 0;    
    28     }    
    29     
    30     @Override    
    31     public String getType(Uri uri) {    
    32         switch (mMatcher.match(uri)) {    
    33         case Profile.ITEM:    
    34             return Profile.CONTENT_TYPE;    
    35         case Profile.ITEM_ID:    
    36             return Profile.CONTENT_ITEM_TYPE;    
    37         default:    
    38             throw new IllegalArgumentException("Unknown URI"+uri);    
    39         }    
    40     }    
    41     
    42     @Override    
    43     public Uri insert(Uri uri, ContentValues values) {    
    44         // TODO Auto-generated method stub    
    45         long rowId;    
    46         if(mMatcher.match(uri)!=Profile.ITEM){    
    47             throw new IllegalArgumentException("Unknown URI"+uri);    
    48         }    
    49         rowId = db.insert(Profile.TABLE_NAME,null,values);    
    50         if(rowId>0){    
    51             Uri noteUri=ContentUris.withAppendedId(Profile.CONTENT_URI, rowId);    
    52             getContext().getContentResolver().notifyChange(noteUri, null);    
    53             return noteUri;    
    54         }    
    55     
    56         throw new SQLException("Failed to insert row into " + uri);    
    57     }    
    58     
    59     @Override    
    60     public boolean onCreate() {    
    61         // TODO Auto-generated method stub    
    62         mDbHelper = new DBHelper(getContext());    
    63     
    64         db = mDbHelper.getReadableDatabase();    
    65     
    66         return true;    
    67     }    
    68     
    69     @Override    
    70     public Cursor query(Uri uri, String[] projection, String selection,    
    71             String[] selectionArgs, String sortOrder) {    
    72         // TODO Auto-generated method stub    
    73         Cursor c = null;    
    74         switch (mMatcher.match(uri)) {    
    75         case Profile.ITEM:    
    76             c =  db.query(Profile.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);    
    77             break;    
    78         case Profile.ITEM_ID:    
    79             c = db.query(Profile.TABLE_NAME, projection,Profile.COLUMN_ID + "="+uri.getLastPathSegment(), selectionArgs, null, null, sortOrder);    
    80             break;    
    81         default:    
    82             throw new IllegalArgumentException("Unknown URI"+uri);    
    83         }    
    84     
    85         c.setNotificationUri(getContext().getContentResolver(), uri);    
    86         return c;    
    87     }    
    88     
    89     @Override    
    90     public int update(Uri uri, ContentValues values, String selection,    
    91             String[] selectionArgs) {    
    92         // TODO Auto-generated method stub    
    93         return 0;    
    94     }    
    95     
    96 }

      e、作为主程序的MainActivity类,对ContentProvider中的数据进行获取,并在界面上进行简单的显示:

     1 package com.wang.testcontentprovider;
     2 
     3 import android.support.v7.app.ActionBarActivity;
     4 import android.os.Bundle;
     5 import android.view.Menu;
     6 import android.view.MenuItem;
     7 
     8 import android.app.ListActivity;  
     9 import android.content.ContentResolver;  
    10 import android.content.ContentUris;  
    11 import android.content.ContentValues;  
    12 import android.database.Cursor;  
    13 import android.database.SQLException;  
    14 import android.database.sqlite.SQLiteDatabase;  
    15 import android.net.Uri;  
    16 import android.widget.SimpleCursorAdapter;  
    17   
    18 public class MainActivity extends ListActivity {  
    19     private SimpleCursorAdapter adapter= null;  
    20     private Cursor mCursor = null;  
    21     private ContentResolver mContentResolver = null;  
    22   
    23     @Override  
    24     public void onCreate(Bundle savedInstanceState) {  
    25         super.onCreate(savedInstanceState);  
    26         initData();  
    27         initAdapter();  
    28     }  
    29   
    30     public void initData(){  
    31         mContentResolver = getContentResolver();  
    32         for (int i = 0; i < 100; i++) {  
    33             ContentValues values = new ContentValues();  
    34             values.put(Profile.COLUMN_NAME, "Wang "+i);  
    35             mContentResolver.insert(Profile.CONTENT_URI, values);  
    36         }  
    37     }  
    38   
    39     public void initAdapter(){  
    40         mCursor = mContentResolver.query(Profile.CONTENT_URI, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, null, null, null);  
    41   
    42         startManagingCursor(mCursor);  
    43   
    44         //设置adapter  
    45         adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, mCursor, new String[]{Profile.COLUMN_ID,Profile.COLUMN_NAME}, new int[]{android.R.id.text1,android.R.id.text2});  
    46         setListAdapter(adapter);  
    47     }  
    48   
    49     @Override  
    50     public boolean onCreateOptionsMenu(Menu menu) {  
    51         getMenuInflater().inflate(R.menu.main, menu);  
    52         return true;  
    53     }  
    54   
    55 }  

    9、结果分析

      贴上一张不太美观的结果图:

      

  • 相关阅读:
    一道题串联lambda表达式、链式编程、函数式接口、Stream流式计算
    JUC-8锁问题
    JUC-Java中的生产者消费者问题
    Jenkins配置
    kubernetes的配置
    容器技术之Docker的网站搭建
    CentOS 使用yum安装ELK环境命令详解
    golang学习笔记——context库
    golang学习笔记——sync库
    golang学习笔记——select
  • 原文地址:https://www.cnblogs.com/tgyf/p/4696288.html
Copyright © 2020-2023  润新知