zoukankan      html  css  js  c++  java
  • 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、结果分析

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

      

  • 相关阅读:
    OutputCache 缓存key的创建 CreateOutputCachedItemKey
    Asp.net Web Api源码调试
    asp.net mvc源码分析DefaultModelBinder 自定义的普通数据类型的绑定和验证
    Asp.net web Api源码分析HttpParameterBinding
    Asp.net web Api源码分析HttpRequestMessage的创建
    asp.net mvc源码分析ActionResult篇 RazorView.RenderView
    Asp.Net MVC 项目预编译 View
    Asp.net Web.config文件读取路径你真的清楚吗?
    asp.net 动态创建TextBox控件 如何加载状态信息
    asp.net mvc源码分析BeginForm方法 和ClientValidationEnabled 属性
  • 原文地址:https://www.cnblogs.com/tgyf/p/4696288.html
Copyright © 2011-2022 走看看