游子的博客
慈母手中线,游子身上衣, 临行密密缝,意恐迟迟归, 谁言寸草心,报得三春晖。 数据读取中,请稍候......
posts - 337,  comments - 546,  trackbacks - 0

内容提供器

       在Android安全模型中,一个应用程序编写的文件无法被其他任何应用程序所读写。每个应用程序都有自己的Linux用户ID和数据目录,以及其受保护的内存空间。Android程序可以通过下面两种方式进行彼此间的通信。

       第一种是IPC(Inter-Process Communication,进程间通信):一个进程使用AIDL(Android Interface Definition Language,接口定义语言)和IBinder接口声明一个任意的API。调用该API时,将在进程间安全且有效地队参数进行编组。这项先进技术用于对后台Service线程进行远程过程调用。

       第二种就是ContentProvider:进程在系统中将他们本身注册为某些数据类型的提供者。请求该信息时,Android就会通过一个固定的API调用这些进程,以它们认为合适的方式查询或者修改内容。

       ContentProvider管理的任何信息部分都通过一个URI来寻址,这个URI类似于以下形式:content://authority/path/id

其中content://是标准要求的前缀。authority是提供者的名称,建议使用完全限定包名称,避免出现名称冲突。Path是提供者内部的一个虚拟目录,用于标识被请求的数据类型。Id是被请求的特定记录的主键,要请求获得具有特定类型的所有记录,可以省略此参数以及后面的斜杠。

       Android已经内置提供了几个提供者,包括:

content://browser;

content://contacts;

content://media;

content://settings。

下面我们就来演示如何使用内容提供器。

将HelloSQLite程序改为使用内容提供器的。对于HelloSQLite的提供者,下面都是有效的URI:

content://com.blueeagle.HelloSQLite/3 ——表示取id为3的数据

content://com.blueeagle.HelloSQLite/   ——表示取所有数据

 

首先需要向Contants.java添加两个常量:

    public static final String AUTHORITY = "com.blueeagle.HelloSQLite";

    public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);

 

接下来可以对HelloSQLite类做一些修改:

代码如下:

  1. package com.blueeagle;  
  2.   
  3.   
  4. import android.app.ListActivity;  
  5.   
  6. import android.content.ContentValues;  
  7.   
  8. import android.database.Cursor;  
  9.   
  10. import android.os.Bundle;  
  11.   
  12. import android.widget.SimpleCursorAdapter;  
  13.   
  14. import static com.blueeagle.Constants.TIME;  
  15.   
  16. import static com.blueeagle.Constants.TITLE;  
  17.   
  18. import static com.blueeagle.Constants.CONTENT_URI;  
  19.   
  20. import static android.provider.BaseColumns._ID;  
  21.   
  22.    
  23.   
  24. public class HelloSQLite extends ListActivity {  
  25.   
  26.     /** Called when the activity is first created. */  
  27.   
  28.     //private MySQLite mySqlite;  
  29.   
  30.     private static String[] FROM = {_ID,TIME,TITLE};  
  31.   
  32.     private static String ORDER_BY = TIME+" DESC";  
  33.   
  34.     //@Override  
  35.   
  36.     public void onCreate(Bundle savedInstanceState) {  
  37.   
  38.         super.onCreate(savedInstanceState);  
  39.   
  40.         setContentView(R.layout.main);  
  41.   
  42.           
  43.   
  44.         addItem("Hello,Android!");  
  45.   
  46.         Cursor cursor = getItems();  
  47.   
  48.         showItems(cursor);  
  49.   
  50.     }  
  51.   
  52.     private static int[] TO = {R.id.rowid,R.id.time,R.id.title};  
  53.   
  54.       
  55.   
  56. //显示查询结果  
  57.   
  58.     private void showItems(Cursor cursor) {  
  59.   
  60.        // TODO Auto-generated method stub  
  61.   
  62.     SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);  
  63.   
  64.     setListAdapter(adapter);  
  65.   
  66.     }  
  67.   
  68.     //使用ContentProvider时,geiItems方法也化简了  
  69.   
  70.     private Cursor getItems() {  
  71.   
  72.        return managedQuery(CONTENT_URI,FROM,null,null,ORDER_BY);  
  73.   
  74.     }  
  75.   
  76.     //此处使用Activity.managedQuery()方法,将内容URI,感兴趣的列表和应该使用的排序顺序传递给该方法。  
  77.   
  78.     private void addItem(String string) {  
  79.   
  80.        ContentValues values = new ContentValues();  
  81.   
  82.        values.put(TIME, System.currentTimeMillis());  
  83.   
  84.        values.put(TITLE, string);  
  85.   
  86.        getContentResolver().insert(CONTENT_URI, values);  
  87.   
  88.     }  
  89.   
  90.     //没有了对getWriteableDatabase()的调用,对insertOrThrow的调用替换成了getContentResolver().insert(CONTENT_URI, values)  
  91.   
  92.     //我们使用了一个URI而不是用数据库句柄。  
  93.   
  94. }  


 

下面就是实现ContentProvider

       ContentProvider是一个类似于Activity的高级对象,需要向系统进行声明。因此,实现ContentProvider的第一步是将其添加到AndroidManifest.xml文件中的<activity>标签之前。

其中android:name是类名,android:authorities是在内容URI中使用的字符串。接下来创建我们自己的ContentProvider类,为继承类。ContentProvider创建后就会被调用:public boolean onCreate()ContentProvider类中有6个继承而来的方法,需要实现:

具体来说需要实现ContentProvider 类中的6个抽象方法。 
       Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):将查询的数据以Cursor 对象的形式返回。 
        Uri insert(Uri uri, ContentValues values):向 Content Provider中插入新数据记录,ContentValues 为数据记录的列名和列值映射。 
        int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新Content Provider中已存在的数据记录。

int delete(Uri uri, String selection, String[] selectionArgs):从Content Provider中删除数据记录。

        String getType(Uri uri):返回Content Provider中的数据( MIME )类型。

boolean onCreate():当 Content Provider 启动时被调用。

定义一个 URI 类型的静态常量,命名为CONTENT_URI。 必须为该常量对象定义一个唯一的URI字符串,一般的做法是将 ContentProvider子类的全称类名作为URI字符串。

定义每个字段的列名,如果采用的数据库存储系统为SQLite 数据库,数据表列名可以采用数据库中表的列名。不管数据表中有没有其他的唯一标识一个记录的字段,都应该定义一个"_id"字段 来唯一标识一个记录。模式使用 "INTEGER PRIMARY KEY AUTOINCREMENT" 自动更新 一般将这些列名字符串定义为静态常量, 如"_id"字段名定义为一个名为"_ID"  值为 "_id" 的静态字符串对象。

创建好的一个Content Provider必须在AndroidManifest.xml中声明。

        <provider android:name=".ItemsProvider"

              android:authorities="com.blueeagle" />

其中name属性为ContentProvider 子类的全称类名,authorities 属性唯一标识了一个ContentProvider。还可以通过 setReadPermission() 和 setWritePermission() 来设置其操作权限。当然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission属性来控制其权限。 
 注意:因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。

然后需要使用UriMatcher,用于匹配Uri。
用法如下: 
首先把需要匹配Uri路径全部给注册上:

对于Uri:

什么是URI?将其分为A,B,C,D 4个部分:

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"

B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 ;"content://hx.android.text.myprovider"

C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"

D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id

注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,例如匹配content://com.blueeagle路径,返回的匹配码为1。

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码 
UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//添加需要匹配的uri,如果匹配就会返回匹配码 
//如果match()方法匹配content://com.blueeagle路径,返回匹配码为1 
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite”, 1);

//如果match()方法匹配content://com.blueeagle/ ***路径,返回匹配码为2
//#号为通配符 
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite/#”, 2);

switch(sUriMatcher.match(Uri.parse("content://com.blueeagle /***"))) {
 case 1 break;
 case 2 break; 
default:
//不匹配 break; 
}

自定义的contentprovider如下:

ItemsProvider.java

  1. package com.blueeagle;  
  2.   
  3. import android.content.ContentProvider;  
  4.   
  5. import android.content.ContentUris;  
  6.   
  7. import android.content.ContentValues;  
  8.   
  9. import android.content.UriMatcher;  
  10.   
  11. import android.database.Cursor;  
  12.   
  13. import android.database.sqlite.SQLiteDatabase;  
  14.   
  15. import android.net.Uri;  
  16.   
  17. import android.text.TextUtils;  
  18.   
  19. import static com.blueeagle.Constants.CONTENT_URI;  
  20.   
  21. import static com.blueeagle.Constants.TABLE_NAME;  
  22.   
  23. import static com.blueeagle.Constants.AUTHORITY;  
  24.   
  25. import static android.provider.BaseColumns._ID;  
  26.   
  27.    
  28.   
  29. public class ItemsProvider extends ContentProvider {  
  30.   
  31.       
  32.   
  33.     private static final int ITEMS = 1;  
  34.   
  35.     private static final int ITEMS_ID = 2;  
  36.   
  37.        /** The MIME type of a directory of items */  
  38.   
  39.        private static final String CONTENT_TYPE  
  40.   
  41.           = "vnd.android.cursor.dir/vnd.com.blueeagle";  
  42.   
  43.        /** The MIME type of a single item */  
  44.   
  45.        private static final String CONTENT_ITEM_TYPE  
  46.   
  47.           = "vnd.android.cursor.item/vnd.com.blueeagle";  
  48.   
  49.          
  50.   
  51.        private MySQLite myDataBase ;  
  52.   
  53.        private UriMatcher myUriMatcher;  
  54.   
  55.          
  56.   
  57.        @Override  
  58.   
  59.        public boolean onCreate() {  
  60.   
  61.           myUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  62.   
  63.           myUriMatcher.addURI(AUTHORITY, "HelloSQLite", ITEMS);  
  64.   
  65.           myUriMatcher.addURI(AUTHORITY, "HelloSQLite/#", ITEMS_ID);  
  66.   
  67.           myDataBase = new MySQLite(getContext());  
  68.   
  69.           return true;  
  70.   
  71.        }  
  72.   
  73.     @Override  
  74.   
  75.     public int delete(Uri uri, String selection,  
  76.   
  77.        // TODO Auto-generated method stub  
  78.   
  79.         String[] selectionArgs) {  
  80.   
  81.             SQLiteDatabase db = myDataBase.getWritableDatabase();  
  82.   
  83.             int count;  
  84.   
  85.             switch (myUriMatcher.match(uri)) {  
  86.   
  87.             case ITEMS:  
  88.   
  89.                count = db.delete(TABLE_NAME, selection, selectionArgs);  
  90.   
  91.                break;  
  92.   
  93.             case ITEMS_ID:  
  94.   
  95.                long id = Long.parseLong(uri.getPathSegments().get(1));  
  96.   
  97.                count = db.delete(TABLE_NAME, appendRowId(selection, id),  
  98.   
  99.                      selectionArgs);  
  100.   
  101.                break;  
  102.   
  103.             default:  
  104.   
  105.                throw new IllegalArgumentException("Unknown URI " + uri);  
  106.   
  107.             }  
  108.   
  109.             // Notify any watchers of the change  
  110.   
  111.             getContext().getContentResolver().notifyChange(uri, null);  
  112.   
  113.             return count;  
  114.   
  115.     }  
  116.   
  117.     @Override  
  118.   
  119.     public String getType(Uri uri) {  
  120.   
  121.        // TODO Auto-generated method stub  
  122.   
  123.           switch (myUriMatcher.match(uri)) {  
  124.   
  125.           case ITEMS:  
  126.   
  127.              return CONTENT_TYPE;  
  128.   
  129.           case ITEMS_ID:  
  130.   
  131.              return CONTENT_ITEM_TYPE;  
  132.   
  133.           default:  
  134.   
  135.              throw new IllegalArgumentException("Unknown URI " + uri);  
  136.   
  137.           }  
  138.   
  139.     }  
  140.   
  141.     @Override  
  142.   
  143.     public Uri insert(Uri uri, ContentValues values) {  
  144.   
  145.        // TODO Auto-generated method stub  
  146.   
  147.           SQLiteDatabase db = myDataBase.getWritableDatabase();  
  148.   
  149.           // Validate the requested uri  
  150.   
  151.           if (myUriMatcher.match(uri) != ITEMS) {  
  152.   
  153.              throw new IllegalArgumentException("Unknown URI " + uri);  
  154.   
  155.           }  
  156.   
  157.           // Insert into database  
  158.   
  159.           long id = db.insertOrThrow(TABLE_NAME, null, values);  
  160.   
  161.           // Notify any watchers of the change  
  162.   
  163.           Uri newUri = ContentUris.withAppendedId(CONTENT_URI, id);  
  164.   
  165.           getContext().getContentResolver().notifyChange(newUri, null);  
  166.   
  167.           return newUri;  
  168.   
  169.     }  
  170.   
  171.     @Override  
  172.   
  173.     public Cursor query(Uri uri, String[] projection,  
  174.   
  175.              String selection, String[] selectionArgs, String orderBy) {  
  176.   
  177.        // TODO Auto-generated method stub  
  178.   
  179.           if (myUriMatcher.match(uri) == ITEMS_ID) {  
  180.   
  181.               long id = Long.parseLong(uri.getPathSegments().get(1));  
  182.   
  183.               selection = appendRowId(selection, id);  
  184.   
  185.           }  
  186.   
  187.               // Get the database and run the query  
  188.   
  189.               SQLiteDatabase db = myDataBase.getReadableDatabase();  
  190.   
  191.               Cursor cursor = db.query(TABLE_NAME, projection, selection,  
  192.   
  193.                     selectionArgs, null, null, orderBy);  
  194.   
  195.               // Tell the cursor what uri to watch, so it knows when its  
  196.   
  197.               // source data changes  
  198.   
  199.               cursor.setNotificationUri(getContext().getContentResolver(),  
  200.   
  201.                     uri);  
  202.   
  203.               return cursor;  
  204.   
  205.           }  
  206.   
  207.     private String appendRowId(String selection, long id) {  
  208.   
  209.        // TODO Auto-generated method stub  
  210.   
  211.           return _ID + "=" + id  
  212.   
  213.           + (!TextUtils.isEmpty(selection)  
  214.   
  215.                 ? " AND (" + selection + ')'  
  216.   
  217.                 : "");  
  218.   
  219.     }  
  220.   
  221.     @Override  
  222.   
  223.     public int update(Uri uri, ContentValues values, String selection,  
  224.   
  225.            String[] selectionArgs) {  
  226.   
  227.        // TODO Auto-generated method stub  
  228.   
  229.           SQLiteDatabase db = myDataBase.getWritableDatabase();  
  230.   
  231.           int count;  
  232.   
  233.           switch (myUriMatcher.match(uri)) {  
  234.   
  235.           case ITEMS:  
  236.   
  237.              count = db.update(TABLE_NAME, values, selection,  
  238.   
  239.                    selectionArgs);  
  240.   
  241.              break;  
  242.   
  243.           case ITEMS_ID:  
  244.   
  245.              long id = Long.parseLong(uri.getPathSegments().get(1));  
  246.   
  247.              count = db.update(TABLE_NAME, values, appendRowId(  
  248.   
  249.                    selection, id), selectionArgs);  
  250.   
  251.              break;  
  252.   
  253.           default:  
  254.   
  255.              throw new IllegalArgumentException("Unknown URI " + uri);  
  256.   
  257.           }  
  258.   
  259.           // Notify any watchers of the change  
  260.   
  261.           getContext().getContentResolver().notifyChange(uri, null);  
  262.   
  263.           return count;  
  264.   
  265.     }  
  266.   
  267. }  


总结一下,创建一个新的内容提供器。

1.通过扩展抽象类ContentProvider可以创建新的内容提供器。重写onCreate方法来打开或者初始化将要使用这个新的提供器来提供底层数据源。

2.还应该提供那些用来返回指向这个提供器的完整的URI的公共静态CONTENT_URI变量。提供器之间的内容URI应该是唯一的,所以最好的做法是使URI路径以包名为基础。

定义一个内容提供器URI一般的形式为:

content://com.pakagename/datapath

例如:content://com.blueeagle/items

或者:content://com.blueeagle./items/3

内容URI可以表示为这两种形式中的任意一种形式。前面的一种URI表示对那种类型中所有的值的请求,后面附加一个/3的表示对一条记录的请求(这里请求的是记录3)。这两种形式来访问提供器都是可行的。

完成这项工作最简单的方式是使用一个UriMatcher。当通过内容解析器来访问内容提供器的时候,可以配置UriMathcer来解析URI以确定它们的形式。就像前文说的那样

posted on 2013-02-20 10:41 游子 阅读(2970) 评论(0)  编辑 收藏 引用 所属分类: 软件
只有注册用户登录后才能发表评论。

欢迎大家扔鸡蛋!送鲜花!

博客可以收入过千吗?

<2019年5月>
日一二三四五六28293012345678910111213141516171819
20212223242526272829303112345678

常用链接

留言簿(8)

随笔分类(314)

随笔档案(337)

文章分类(7)

文章档案(10)

相册

收藏夹(1)

其它

友情链接

数字电视

生活、旅游

自己的链接

计算机

搜索

  •  

积分与排名

  • 积分 - 407923
  • 排名 - 9

最新评论

阅读排行榜

评论排行榜