zoukankan      html  css  js  c++  java
  • 基础总结篇之八:创建及调用自己的ContentProvider

    转自:http://blog.csdn.net/wellsoho/article/details/49494141

    若不能坚持到底,即使是朽木也不能折断;只要坚持不停地用刀刻,就算是金属玉石也可以雕出花饰。用今天的话来说就是:再容易的事情,没有锲而不舍的精神,都不可能做到;再难的事情,只要有坚持不懈的努力,都一定能够做到。希望我们在坚持理想的道路上都能够锲而不舍地雕刻自己的那块“金石”。

    今天我们来讲解一下如何创建及调用自己的ContentProvider。

    在前面两篇文章中我们分别讲了如何读写联系人和短消息,相信大家对于ContentProvider的操作方法已经有了一定程度的了解。在有些场合,除了操作ContentProvider之外,我们还有可能需要创建自己的ContentProvider,来提供信息共享的服务,这就要求我们很好的掌握ContentProvider的创建及使用技巧。下面我们就由表及里的逐步讲解每个步骤。

    在正式开始实例演示之前,我们先来了解以下两个知识点:

    授权:

    在Android中,每一个ContentProvider都会用类似于域名的字符串来注册自己,我们成为授权(authority)。这个唯一标识的字符串是此ContentProvider可提供的一组URI的基础,有了这个基础,才能够向外界提供信息的共享服务。

    授权是在AndroidManifest.xml中完成的,每一个ContentProvider必须在此声明并授权,方式如下:

    [html] view plaincopy
     
     
     
    1. <provider android:name=".SomeProvider"  
    2.     android:authorities="com.your-company.SomeProvider"/>  
    上面的<provider>元素指明了ContentProvider的提供者是“SomeProvider”这个类,并为其授权,授权的基础URI为“com.your-company.SomeProvider”。有了这个授权信息,系统可以准确的定位到具体的ContentProvider,从而使访问者能够获取到指定的信息。这和浏览Web页面的方式很相似,“SomeProvider”就像一台具体的服务器,而“com.your-company.SomeProvider”就像注册的域名,相信大家对这个概念并不陌生,由此联想一下就可以了解ContentProvider授权的作用了。(需要注意的是,除了Android内置应用程序之外,第三方程序应尽量使用以上方式的完全限定的授权名。)

    MIME类型:

    就像网站返回给定URL的MIME(Multipurpose Internet Mail Extensions,多用途Internet邮件扩展)类型一样(这使浏览器能够用正确的程序来查看内容),ContentProvider还负责返回给定URI的MIME类型。根据MIME类型规范,MIME类型包含两部分:类型和子类型。例如:text/html,text/css,text/xml等等。

    Android也遵循类似的约定来定义MIME类型。

    对于单条记录,MIME类型类似于:

    vnd.android.cursor.item/vnd.your-company.content-type

    而对于记录的集合,MIME类型类似于:

    vnd.android.cursor.dir/vnd.your-company.comtent-type

    其中的vnd表示这些类型和子类型具有非标准的、供应商特定的形式;content-type可以根据ContentProvider的功能来定,比如日记的ContentProvider可以为note,日程安排的ContentProvider可以为schedule,等等。

    了解了以上两个知识点之后,我们就结合实例来演示一下具体的过程。

    我们将会创建一个记录person信息的ContentProvider,实现对person的CRUD操作,访问者可以通过下面路径操作我们的ContentProvider:

    访问者可以通过“[BASE_URI]/persons”来操作person集合,也可以通过“[BASE_URI]/persons/#”的形式操作单个person。

    我们创建一个person的ContentProvider需要两个步骤:

    1.创建PersonProvider类:

    我们需要继承ContentProvider类,实现onCreate、query、insert、update、delete和getType这几个方法。具体代码如下:

    [java] view plaincopy
     
     
     
    1. package com.scott.provider;  
    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.sqlite.SQLiteDatabase;  
    9. import android.net.Uri;  
    10.   
    11. public class PersonProvider extends ContentProvider {  
    12.   
    13.     private static final UriMatcher matcher;  
    14.     private DBHelper helper;  
    15.     private SQLiteDatabase db;  
    16.       
    17.     private static final String AUTHORITY = "com.scott.provider.PersonProvider";  
    18.     private static final int PERSON_ALL = 0;  
    19.     private static final int PERSON_ONE = 1;  
    20.       
    21.     public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.scott.person";  
    22.     public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.scott.person";  
    23.       
    24.     //数据改变后立即重新查询  
    25.     private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/persons");  
    26.       
    27.     static {  
    28.         matcher = new UriMatcher(UriMatcher.NO_MATCH);  
    29.           
    30.         matcher.addURI(AUTHORITY, "persons", PERSON_ALL);   //匹配记录集合  
    31.         matcher.addURI(AUTHORITY, "persons/#", PERSON_ONE); //匹配单条记录  
    32.     }  
    33.       
    34.     @Override  
    35.     public boolean onCreate() {  
    36.         helper = new DBHelper(getContext());  
    37.         return true;  
    38.     }  
    39.   
    40.     @Override  
    41.     public String getType(Uri uri) {  
    42.         int match = matcher.match(uri);  
    43.         switch (match) {  
    44.         case PERSON_ALL:  
    45.             return CONTENT_TYPE;  
    46.         case PERSON_ONE:  
    47.             return CONTENT_ITEM_TYPE;  
    48.         default:  
    49.             throw new IllegalArgumentException("Unknown URI: " + uri);  
    50.         }  
    51.     }  
    52.       
    53.     @Override  
    54.     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {  
    55.         db = helper.getReadableDatabase();  
    56.         int match = matcher.match(uri);  
    57.         switch (match) {  
    58.         case PERSON_ALL:  
    59.             //doesn't need any code in my provider.  
    60.             break;  
    61.         case PERSON_ONE:  
    62.             long _id = ContentUris.parseId(uri);  
    63.             selection = "_id = ?";  
    64.             selectionArgs = new String[]{String.valueOf(_id)};  
    65.             break;  
    66.         default:  
    67.             throw new IllegalArgumentException("Unknown URI: " + uri);  
    68.         }  
    69.         return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);  
    70.     }  
    71.   
    72.     @Override  
    73.     public Uri insert(Uri uri, ContentValues values) {  
    74.         int match = matcher.match(uri);  
    75.         if (match != PERSON_ALL) {  
    76.             throw new IllegalArgumentException("Wrong URI: " + uri);  
    77.         }  
    78.         db = helper.getWritableDatabase();  
    79.         if (values == null) {  
    80.             values = new ContentValues();  
    81.             values.put("name", "no name");  
    82.             values.put("age", "1");  
    83.             values.put("info", "no info.");  
    84.         }  
    85.         long rowId = db.insert("person", null, values);  
    86.         if (rowId > 0) {  
    87.             notifyDataChanged();  
    88.             return ContentUris.withAppendedId(uri, rowId);  
    89.         }  
    90.         return null;  
    91.     }  
    92.   
    93.     @Override  
    94.     public int delete(Uri uri, String selection, String[] selectionArgs) {  
    95.         db = helper.getWritableDatabase();  
    96.         int match = matcher.match(uri);  
    97.         switch (match) {  
    98.         case PERSON_ALL:  
    99.             //doesn't need any code in my provider.  
    100.             break;  
    101.         case PERSON_ONE:  
    102.             long _id = ContentUris.parseId(uri);  
    103.             selection = "_id = ?";  
    104.             selectionArgs = new String[]{String.valueOf(_id)};  
    105.         }  
    106.         int count = db.delete("person", selection, selectionArgs);  
    107.         if (count > 0) {  
    108.             notifyDataChanged();  
    109.         }  
    110.         return count;  
    111.     }  
    112.   
    113.     @Override  
    114.     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {  
    115.         db = helper.getWritableDatabase();  
    116.         int match = matcher.match(uri);  
    117.         switch (match) {  
    118.         case PERSON_ALL:  
    119.             //doesn't need any code in my provider.  
    120.             break;  
    121.         case PERSON_ONE:  
    122.             long _id = ContentUris.parseId(uri);  
    123.             selection = "_id = ?";  
    124.             selectionArgs = new String[]{String.valueOf(_id)};  
    125.             break;  
    126.         default:  
    127.             throw new IllegalArgumentException("Unknown URI: " + uri);  
    128.         }  
    129.         int count = db.update("person", values, selection, selectionArgs);  
    130.         if (count > 0) {  
    131.             notifyDataChanged();  
    132.         }  
    133.         return count;  
    134.     }  
    135.   
    136.     //通知指定URI数据已改变  
    137.     private void notifyDataChanged() {  
    138.         getContext().getContentResolver().notifyChange(NOTIFY_URI, null);         
    139.     }  
    140. }  
    在PersonProvider中,我们定义了授权地址为“com.scott.provider.PersonProvider”,相信大家在前面也有所了解了。基于这个授权,我们使用了一个UriMatcher对其路径进行匹配,“[BASE_URI]/persons"和“[BASE_URI]/persons/#”这两种路径我们在上面也介绍过,分别对应记录集合和单个记录的操作。在query、insert、update和delete方法中我们根据UriMatcher匹配结果来判断该URI是操作记录集合还是单条记录,从而采取不同的处理方法。在getType方法中,我们会根据匹配的结果返回不同的MIME类型,这一步是不能缺少的,比如我们在query方法中有可能是查询全部集合,有可能是查询单条记录,那么我们返回的Cursor或是集合类型,或是单条记录,这个跟getType返回的MIME类型是一致的,就好像浏览网页一样,指定的url返回的信息是什么类型,那么浏览器就应该接收到对应的MIME类型。另外,我们注意到,上面代码中,在insert、update、delete方法中都调用了notifyDataChanged方法,这个方法中仅有的一步操作就是通知“[BASE_URI]/persons"的访问者,数据发生改变了,应该重新加载了。

    在我们的PersonProvider中,我们用到了Person、DBHelper类,代码如下:

    [java] view plaincopy
     
     
     
    1. package com.scott.provider;  
    2.   
    3. public class Person {  
    4.     public int _id;  
    5.     public String name;  
    6.     public int age;  
    7.     public String info;  
    8.       
    9.     public Person() {  
    10.     }  
    11.       
    12.     public Person(String name, int age, String info) {  
    13.         this.name = name;  
    14.         this.age = age;  
    15.         this.info = info;  
    16.     }  
    17. }  
    [java] view plaincopy
     
     
     
    1. package com.scott.provider;  
    2.   
    3. import android.content.Context;  
    4. import android.database.sqlite.SQLiteDatabase;  
    5. import android.database.sqlite.SQLiteOpenHelper;  
    6.   
    7. public class DBHelper extends SQLiteOpenHelper {  
    8.   
    9.     private static final String DATABASE_NAME = "provider.db";  
    10.     private static final int DATABASE_VERSION = 1;  
    11.       
    12.     public DBHelper(Context context) {  
    13.         super(context, DATABASE_NAME, null, DATABASE_VERSION);  
    14.     }  
    15.   
    16.     @Override  
    17.     public void onCreate(SQLiteDatabase db) {  
    18.         String sql = "CREATE TABLE IF NOT EXISTS person" +  
    19.                 "(_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age INTEGER, info TEXT)";  
    20.         db.execSQL(sql);  
    21.     }  
    22.   
    23.     @Override  
    24.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
    25.         db.execSQL("DROP TABLE IF EXISTS person");  
    26.         onCreate(db);  
    27.     }  
    28. }  

    最后,要想让这个ContentProvider生效,我们需要在AndroidManifest.xml中声明并为其授权,如下所示:

    [html] view plaincopy
     
     
     
    1. <provider android:name=".PersonProvider"  
    2.     android:authorities="com.scott.provider.PersonProvider"  
    3.     android:multiprocess="true"/>  
    其中,android:multiprocess代表是否允许多进程操作。另外我们也可以为其声明相应的权限,对应的属性是:android:permission。

    完成了ContentProvider后,下面我们来看一下访问者。这一步我们在MainActivity中完成,看下面代码:

    [java] view plaincopy
     
     
     
    1. package com.scott.provider;  
    2.   
    3. import java.util.ArrayList;  
    4.   
    5. import android.app.Activity;  
    6. import android.content.ContentResolver;  
    7. import android.content.ContentUris;  
    8. import android.content.ContentValues;  
    9. import android.database.Cursor;  
    10. import android.database.CursorWrapper;  
    11. import android.net.Uri;  
    12. import android.os.Bundle;  
    13. import android.os.Handler;  
    14. import android.os.Message;  
    15. import android.view.View;  
    16. import android.widget.ListView;  
    17. import android.widget.SimpleCursorAdapter;  
    18.   
    19.   
    20. public class MainActivity extends Activity {  
    21.      
    22.     private ContentResolver resolver;  
    23.     private ListView listView;  
    24.       
    25.     private static final String AUTHORITY = "com.scott.provider.PersonProvider";  
    26.     private static final Uri PERSON_ALL_URI = Uri.parse("content://" + AUTHORITY + "/persons");  
    27.       
    28.     private Handler handler = new Handler() {  
    29.         public void handleMessage(Message msg) {  
    30.             //update records.  
    31.             requery();  
    32.         };  
    33.     };  
    34.       
    35.     @Override  
    36.     public void onCreate(Bundle savedInstanceState) {  
    37.         super.onCreate(savedInstanceState);  
    38.         setContentView(R.layout.main);  
    39.   
    40.         resolver = getContentResolver();  
    41.         listView = (ListView) findViewById(R.id.listView);  
    42.           
    43.         //为PERSON_ALL_URI注册变化通知  
    44.         getContentResolver().registerContentObserver(PERSON_ALL_URI, true, new PersonObserver(handler));  
    45.     }  
    46.       
    47.     /** 
    48.      * 初始化 
    49.      * @param view 
    50.      */  
    51.     public void init(View view) {  
    52.         ArrayList<Person> persons = new ArrayList<Person>();  
    53.           
    54.         Person person1 = new Person("Ella", 22, "lively girl");  
    55.         Person person2 = new Person("Jenny", 22, "beautiful girl");  
    56.         Person person3 = new Person("Jessica", 23, "sexy girl");  
    57.         Person person4 = new Person("Kelly", 23, "hot baby");  
    58.         Person person5 = new Person("Jane", 25, "pretty woman");  
    59.           
    60.         persons.add(person1);  
    61.         persons.add(person2);  
    62.         persons.add(person3);  
    63.         persons.add(person4);  
    64.         persons.add(person5);  
    65.   
    66.         for (Person person : persons) {  
    67.             ContentValues values = new ContentValues();  
    68.             values.put("name", person.name);  
    69.             values.put("age", person.age);  
    70.             values.put("info", person.info);  
    71.             resolver.insert(PERSON_ALL_URI, values);  
    72.         }  
    73.     }  
    74.       
    75.     /** 
    76.      * 查询所有记录 
    77.      * @param view 
    78.      */  
    79.     public void query(View view) {  
    80. //      Uri personOneUri = ContentUris.withAppendedId(PERSON_ALL_URI, 1);查询_id为1的记录  
    81.         Cursor c = resolver.query(PERSON_ALL_URI, null, null, null, null);  
    82.           
    83.         CursorWrapper cursorWrapper = new CursorWrapper(c) {  
    84.               
    85.             @Override  
    86.             public String getString(int columnIndex) {  
    87.                 //将简介前加上年龄  
    88.                 if (getColumnName(columnIndex).equals("info")) {  
    89.                     int age = getInt(getColumnIndex("age"));  
    90.                     return age + " years old, " + super.getString(columnIndex);  
    91.                 }  
    92.                 return super.getString(columnIndex);  
    93.             }  
    94.         };  
    95.           
    96.         //Cursor须含有"_id"字段  
    97.         SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2,  
    98.                 cursorWrapper, new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2});  
    99.         listView.setAdapter(adapter);  
    100.           
    101.         startManagingCursor(cursorWrapper); //管理Cursor  
    102.     }  
    103.       
    104.     /** 
    105.      * 插入一条记录 
    106.      * @param view 
    107.      */  
    108.     public void insert(View view) {  
    109.         Person person = new Person("Alina", 26, "attractive lady");  
    110.         ContentValues values = new ContentValues();  
    111.         values.put("name", person.name);  
    112.         values.put("age", person.age);  
    113.         values.put("info", person.info);  
    114.         resolver.insert(PERSON_ALL_URI, values);  
    115.     }  
    116.       
    117.     /** 
    118.      * 更新一条记录 
    119.      * @param view 
    120.      */  
    121.     public void update(View view) {  
    122.         Person person = new Person();  
    123.         person.name = "Jane";  
    124.         person.age = 30;  
    125.         //将指定name的记录age字段更新为30  
    126.         ContentValues values = new ContentValues();  
    127.         values.put("age", person.age);  
    128.         resolver.update(PERSON_ALL_URI, values, "name = ?", new String[]{person.name});  
    129.           
    130.         //将_id为1的age更新为30  
    131. //      Uri updateUri = ContentUris.withAppendedId(PERSON_ALL_URI, 1);  
    132. //      resolver.update(updateUri, values, null, null);  
    133.     }  
    134.       
    135.     /** 
    136.      * 删除一条记录 
    137.      * @param view 
    138.      */  
    139.     public void delete(View view) {  
    140.         //删除_id为1的记录  
    141.         Uri delUri = ContentUris.withAppendedId(PERSON_ALL_URI, 1);  
    142.         resolver.delete(delUri, null, null);  
    143.           
    144.         //删除所有记录  
    145. //      resolver.delete(PERSON_ALL_URI, null, null);  
    146.     }  
    147.       
    148.     /** 
    149.      * 重新查询 
    150.      */  
    151.     private void requery() {  
    152.         //实际操作中可以查询集合信息后Adapter.notifyDataSetChanged();  
    153.         query(null);  
    154.     }  
    155. }  
    我们看到,在上面的代码中,分别对应每一种情况进行测试,相对较为简单。我们主要讲一下registerContentObserver这一环节。

    在前面的PersonProvider我们也提到,在数据更改后,会向指定的URI访问者发出通知,以便于更新查询记录。大家注意,仅仅是ContentProvider出力还不够,我们还需要在访问者中注册一个ContentObserver,才能够接收到这个通知。下面我们创建一个PersonObserver:

    [java] view plaincopy
     
     
     
    1. package com.scott.provider;  
    2.   
    3. import android.database.ContentObserver;  
    4. import android.os.Handler;  
    5. import android.os.Message;  
    6. import android.util.Log;  
    7.   
    8. public class PersonObserver extends ContentObserver {  
    9.   
    10.     public static final String TAG = "PersonObserver";  
    11.     private Handler handler;  
    12.       
    13.     public PersonObserver(Handler handler) {  
    14.         super(handler);  
    15.         this.handler = handler;  
    16.     }  
    17.       
    18.     @Override  
    19.     public void onChange(boolean selfChange) {  
    20.         super.onChange(selfChange);  
    21.         Log.i(TAG, "data changed, try to requery.");  
    22.         //向handler发送消息,更新查询记录  
    23.         Message msg = new Message();  
    24.         handler.sendMessage(msg);  
    25.     }  
    26. }  
    这样一来,当ContentProvider发来通知之后,我们就能立即接收到,从而向handler发送一条消息,重新查询记录,使我们能够看到最新的记录信息。

    最后,我们要在AndroidManifest.xml中为MainActivity添加MIME类型过滤器,告诉系统MainActivity可以处理的信息类型:

    [html] view plaincopy
     
     
     
    1. <!-- MIME类型 -->  
    2. <intent-filter>  
    3.     <data android:mimeType="vnd.android.cursor.dir/vnd.scott.person"/>  
    4. </intent-filter>  
    5. <intent-filter>  
    6.     <data android:mimeType="vnd.android.cursor.item/vnd.scott.person"/>  
    7. </intent-filter>  
    这样就完成了访问者的代码,我们来看一下效果:

    鉴于操作类型太多,我在这里就不再展示了,大家可以自己试一试。

    来源:http://blog.csdn.net/liuhe688/article/details/7050868

     
    0
  • 相关阅读:
    使用discourse 作为社区工具
    dremio schema 更新的问题
    go-mysql-server 新版本发布
    开发一个dremio user_agent 解析函数
    java 几个不错的user_agent 解析工具包
    Docker Swarm 高可用详解
    用Docker Swarm实现容器服务高可用
    Docker 1.12 :认识 Swarm 模式下的节点崩溃处理
    Linux下磁盘分区调整(在不使用LVM的情况下)
    docker-swarm----多机容器管理
  • 原文地址:https://www.cnblogs.com/mochaMM/p/5160945.html
Copyright © 2011-2022 走看看