ContentProvider作为安卓的四大组件之一,在看开发中用到的频率远不如其他三个,以至于我都把这个东西给忘了,最近由于工作原因,不得不重新拾起来总结一下,那么今天就来说说自定义ContentProvider吧。

今天的案例是这样的,我们有两个App,一个叫做cpHost,作为内容提供者;另外一个叫做cpTest,专门用来操作这个cpHost中的数据。我们的cpHost中有一个数据库,该数据库中有一个User表,我们通过内容提供者将这个User表共享出去,供其他App调用。下面我们就来看看怎么实现这样一个效果。

既然用到数据库存储用户表,那么毫无疑问我们需要一个DBHelper,如下:

  1. public class DBHelper extends SQLiteOpenHelper {
  2.  
  3. public static final String USERTABLE = "user_table";
  4.  
  5. public DBHelper(Context context, String name, CursorFactory factory, int version) {
  6. super(context, name, factory, version);
  7. }
  8.  
  9. @Override
  10. public void onCreate(SQLiteDatabase db) {
  11. db.execSQL("CREATE TABLE IF NOT EXISTS " + USERTABLE
  12. + "(_id INTEGER PRIMARY KEY AUTOINCREMENT,USERNAME TEXT,NICKNAME TEXT,GENDER TEXT,AGE TEXT);");
  13. }
  14.  
  15. @Override
  16. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  17.  
  18. }
  19.  
  20. }

我们创建一个用户表,然后在在MainActivity中将该表中的数据显示出来,这样方便我们后面看到操作效果,于是我们需要一个listview,看看MainActivity的布局文件:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5.  
  6. <ListView
  7. android:id="@+id/lv"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent" >
  10. </ListView>
  11.  
  12. </RelativeLayout>

MainActivity.java:

  1. public class MainActivity extends Activity {
  2.  
  3. private ListView lv;
  4. private DBHelper helper;
  5.  
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. lv = (ListView) this.findViewById(R.id.lv);
  11. }
  12.  
  13. public List<User> getData() {
  14. helper = new DBHelper(this, "lenve.db", null, 1);
  15. SQLiteDatabase db = helper.getWritableDatabase();
  16. List<User> list = new ArrayList<User>();
  17. Cursor c = db.rawQuery("SELECT * FROM " + DBHelper.USERTABLE, null);
  18. User u = null;
  19. while (c.moveToNext()) {
  20. String username = c.getString(c.getColumnIndex("USERNAME"));
  21. String nickname = c.getString(c.getColumnIndex("NICKNAME"));
  22. String gender = c.getString(c.getColumnIndex("GENDER"));
  23. String age = c.getString(c.getColumnIndex("AGE"));
  24. u = new User(username, nickname, gender, age);
  25. list.add(u);
  26. }
  27. c.close();
  28. return list;
  29. }
  30.  
  31. @Override
  32. protected void onResume() {
  33. super.onResume();
  34. lv.setAdapter(new MyAdapter(this, getData()));
  35. }
  36. }

这里之所以把给ListView设置Adapter的方法放在onResume()方法中执行,主要是为了测试方便,没有其他意思,那么我们在来看看UserBean:

  1. public class User {
  2.  
  3. private String username;
  4. private String nickname;
  5. private String gender;
  6. private String age;
  7.  
  8. public String getUsername() {
  9. return username;
  10. }
  11.  
  12. public void setUsername(String username) {
  13. this.username = username;
  14. }
  15.  
  16. public String getNickname() {
  17. return nickname;
  18. }
  19.  
  20. public void setNickname(String nickname) {
  21. this.nickname = nickname;
  22. }
  23.  
  24. public String getGender() {
  25. return gender;
  26. }
  27.  
  28. public void setGender(String gender) {
  29. this.gender = gender;
  30. }
  31.  
  32. public String getAge() {
  33. return age;
  34. }
  35.  
  36. public void setAge(String age) {
  37. this.age = age;
  38. }
  39.  
  40. public User(String username, String nickname, String gender, String age) {
  41. this.username = username;
  42. this.nickname = nickname;
  43. this.gender = gender;
  44. this.age = age;
  45. }
  46.  
  47. public User() {
  48. }
  49.  
  50. }

还有一个MyAdapter:

  1. public class MyAdapter extends BaseAdapter {
  2.  
  3. private Context context;
  4. private List<User> list;
  5.  
  6. public MyAdapter(Context context, List<User> list) {
  7. this.context = context;
  8. this.list = list;
  9. }
  10.  
  11. @Override
  12. public int getCount() {
  13. return list.size();
  14. }
  15.  
  16. @Override
  17. public Object getItem(int position) {
  18. return list.get(position);
  19. }
  20.  
  21. @Override
  22. public long getItemId(int position) {
  23. return position;
  24. }
  25.  
  26. @Override
  27. public View getView(int position, View convertView, ViewGroup parent) {
  28. ViewHolder holder = null;
  29. if (convertView == null) {
  30. convertView = LayoutInflater.from(context).inflate(R.layout.lv_item, null);
  31. holder = new ViewHolder();
  32. holder.ageTv = (TextView) convertView.findViewById(R.id.age_tv);
  33. holder.nickNameTv = (TextView) convertView.findViewById(R.id.nickname_tv);
  34. holder.userNameTv = (TextView) convertView.findViewById(R.id.username_tv);
  35. holder.genderTv = (TextView) convertView.findViewById(R.id.gender_tv);
  36. convertView.setTag(holder);
  37. } else {
  38. holder = (ViewHolder) convertView.getTag();
  39. }
  40. User u = list.get(position);
  41. holder.ageTv.setText(u.getAge());
  42. holder.genderTv.setText(u.getGender());
  43. holder.nickNameTv.setText(u.getNickname());
  44. holder.userNameTv.setText(u.getUsername());
  45. return convertView;
  46. }
  47.  
  48. class ViewHolder {
  49. TextView userNameTv, nickNameTv, genderTv, ageTv;
  50. }
  51. }

这些都很简单的显示部分的代码,我就不再详细解释。说完这些我们终于可以介绍今天的核心内容了,那就是ContentProvider。我们自定义的ContentProvider首先要继承ContentProvider,继承ContentProvider之后,我们会看到有多个方法需要我们实现,分别onCreate()、query、getType、insert、delete、update,主要是这几个方法,通过方法名字我们都能看出这几个方法的含义,就是执行增删改查操作,其中onCreate在应用启动的时候会调用,我们可以在里边做一些初始化的操作,但是不宜做一些耗时过长的操作,否则会导致应用启动时间变长,造成不好的用户体验。在其他的方法中我们分别执行相应的增删改查操作即可。说到这里,我们不得不介绍ContentProvider中另外一个非常重要的东西,那就是Uri。
Uri是ContentResolver执行CRUD方法时的重要参数,我们可以从Uri中提取出我们要操作的数据对象,要操作哪一条数据等等信息,UriMatcher对象映射Uri的返回码,我们可以使用UriMatcher来方便的知道ContentResolver想要干什么。下面我们举例来说明一下:

  1. content://com.lenve.cphost.mycontentprovider/user/3

一般情况下,我们见到的Uri就是这样的,我们可以将Uri分为三部分,第一部分是固定内容,第二部分是authorities,也就是我们在清单文件中注册ContentProvider时的authorities参数,最后一部分表示数据源路径,可有可无,这些根据自己的项目需求随意定义即可,但是这里有一些约定俗成的规则,比如:

1.user多数情况下表示我们要操作的表名,因为一个ContentProvider可能涉及到多个表,通过这里来进行区分。

2.如果user之后没有参数,默认返回当前表中所有数据,或者是操作当前表中所有数据

3.user之后的3表示操作数据库的条件,可以是id,也可以是其他字段。

那么这么长一个字符串我们要怎么提取我们需要的数据呢?难道要用正则?其实不必,这里就用到我们前面说的UriMatcher,使用UriMatcher会自动对我们的Uri进行匹配,但是,在匹配之前我们要先定义一下匹配规则,如下:

  1. private static final String AUTHORITY = "com.lenve.cphost.mycontentprovider";
  2.  
  3. static {
  4.  
  5. matcher = new UriMatcher(UriMatcher.NO_MATCH);
  6. matcher.addURI(AUTHORITY, "user", 1);// 配置表
  7. matcher.addURI(AUTHORITY, "user/#", 2);// 匹配任何数字
  8. matcher.addURI(AUTHORITY, "user/*", 3);// 匹配任何文本
  9.  
  10. }

我们一般习惯于在静态模块中初始化UriMatcher,我们可以向其中添加匹配规则,比如第6行,我们添加了匹配规则,如果如果Uri第三部分只用一个user,那么匹配结果为1,第7行,#表示任意数字,这句话表示如果Uri的第三部分是数字,那么匹配结果为2,第8行,*表示任意字符,user后还跟了第三个参数,那么匹配结果为3,我们以delete方法为例:

  1. @Override
  2. public int delete(Uri uri, String selection, String[] selectionArgs) {
  3. int code = matcher.match(uri);
  4. int result = 0;
  5. switch (code) {
  6. case UriMatcher.NO_MATCH:
  7. break;
  8. case 1:
  9. // 删除所有
  10. result = db.delete(DBHelper.USERTABLE, null, null);
  11. Log.d("qf", "删除所有数据!");
  12. break;
  13. case 2:
  14. // content://com.lenve.cphost.mycontentprovider/user/10
  15. // 按条件删除,id
  16. result = db.delete(DBHelper.USERTABLE, "_id=?", new String[] { ContentUris.parseId(uri) + "" });
  17. Log.d("qf", "根据删除一条数据");
  18. break;
  19. case 3:
  20. // content://com.lenve.cphost.mycontentprovider/user/zhangsan
  21. // uri.getPathSegments()拿到一个List<String>,里边的值分别是0-->user、1-->zhangsan
  22. result = db.delete(DBHelper.USERTABLE, "USERNAME=?", new String[] { uri.getPathSegments().get(1) });
  23. break;
  24. default:
  25. break;
  26. }
  27. return result;
  28. }

调用Uri中的matcher方法来进行匹配,系统会根据我们在静态模块中的定义来返回相应的匹配结果,根据不同的结果,执行不同的操作,那么我们有什么方法可以快速提取出Uri中的参数呢?

这里我们介绍两个方法,

1.比如我们的Uri是这样的:

  1. content://com.lenve.cphost.mycontentprovider/user/zhangsan

那么我们通过uri.getPathSegments()方法可以拿到一个List集合,该集合中放了两个字符串,第一个是user,第二个是zhangsan

2.比如我们的Uri是这样的:

  1. content://com.lenve.cphost.mycontentprovider/user/10

那么我们可以通过ContentUris.parseId(uri)方法获得10这个数字

以上两种方式基本已经可以解决我们遇到的所有问题了。说了这么多,现在给大家看看一个完整的我的自定义ContentProvider:

  1. public class MyContentProvider extends ContentProvider {
  2.  
  3. private SQLiteOpenHelper helper;
  4. private SQLiteDatabase db;
  5. private static UriMatcher matcher;
  6. private static final String AUTHORITY = "com.lenve.cphost.mycontentprovider";
  7.  
  8. static {
  9.  
  10. matcher = new UriMatcher(UriMatcher.NO_MATCH);
  11. matcher.addURI(AUTHORITY, "user", 1);// 配置表
  12. matcher.addURI(AUTHORITY, "user/#", 2);// 匹配任何数字
  13. matcher.addURI(AUTHORITY, "user/*", 3);// 匹配任何文本
  14.  
  15. }
  16.  
  17. @Override
  18. public boolean onCreate() {
  19. helper = new DBHelper(getContext(), "lenve.db", null, 1);
  20. db = helper.getWritableDatabase();
  21. Log.d("qf", "MyContentProvider--->onCreate()");
  22. return true;
  23. }
  24.  
  25. @Override
  26. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
  27. return null;
  28. }
  29.  
  30. @Override
  31. public String getType(Uri uri) {
  32. return null;
  33. }
  34.  
  35. @Override
  36. public Uri insert(Uri uri, ContentValues values) {
  37. db.insert(DBHelper.USERTABLE, null, values);
  38. return null;
  39. }
  40.  
  41. @Override
  42. public int delete(Uri uri, String selection, String[] selectionArgs) {
  43. int code = matcher.match(uri);
  44. int result = 0;
  45. switch (code) {
  46. case UriMatcher.NO_MATCH:
  47. break;
  48. case 1:
  49. // 删除所有
  50. result = db.delete(DBHelper.USERTABLE, null, null);
  51. Log.d("qf", "删除所有数据!");
  52. break;
  53. case 2:
  54. // content://com.lenve.cphost.mycontentprovider/user/10
  55. // 按条件删除,id
  56. result = db.delete(DBHelper.USERTABLE, "_id=?", new String[] { ContentUris.parseId(uri) + "" });
  57. Log.d("qf", "根据删除一条数据");
  58. break;
  59. case 3:
  60. // content://com.lenve.cphost.mycontentprovider/user/zhangsan
  61. // uri.getPathSegments()拿到一个List<String>,里边的值分别是0-->user、1-->zhangsan
  62. result = db.delete(DBHelper.USERTABLE, "USERNAME=?", new String[] { uri.getPathSegments().get(1) });
  63. break;
  64. default:
  65. break;
  66. }
  67. return result;
  68. }
  69.  
  70. @Override
  71. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
  72. int code = matcher.match(uri);
  73. int result = 0;
  74. switch (code) {
  75. case 1:
  76. result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);
  77. break;
  78. case 2:
  79. result = db.update(DBHelper.USERTABLE, values, "_id=" + ContentUris.parseId(uri) + " AND " + selection,
  80. selectionArgs);
  81. break;
  82. // 根据手动传参id来更新
  83. case 3:
  84. result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);
  85. break;
  86. }
  87. return result;
  88. }

由于时间关系,有几个方法没有实现,不过原理都是一样的,不多说。

所有这些都做完之后,别忘了在清单文件中注册ContentProvider,如下:

  1. <provider
  2. android:name="com.lenve.cphost.MyContentProvider"
  3. android:authorities="com.lenve.cphost.mycontentprovider"
  4. android:exported="true" />

这里解释一下第三个参数,设置为true表示允许其他App调用,设置为false表示不允许其他App调用。

这里说完,我们再看看怎么在cpTest这个App中操作这些数据:

核心代码如下:

  1. public class MainActivity extends Activity {
  2.  
  3. private ContentResolver cr;
  4. private ContentValues values;
  5.  
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. cr = getContentResolver();
  11. values = new ContentValues();
  12. }
  13.  
  14. public void onClickBtn(View v) {
  15. switch (v.getId()) {
  16. case R.id.add_btn:
  17. addData();
  18. break;
  19. case R.id.delete_btn:
  20. deleteData();
  21. break;
  22. case R.id.update_btn:
  23. updateData();
  24. break;
  25. case R.id.search_btn:
  26.  
  27. break;
  28.  
  29. default:
  30. break;
  31. }
  32. }
  33.  
  34. private void updateData() {
  35. // values.put("USERNAME", "lisi");
  36. // cr.update(Uri.parse("content://com.lenve.cphost.mycontentprovider/user"),
  37. // values, "_id=?",
  38. // new String[] { 4 + "" });
  39. values.put("USERNAME", "wangwu");
  40. cr.update(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/3"), values, "USERNAME=?",
  41. new String[] { "lisi" });
  42. }
  43.  
  44. private void deleteData() {
  45. // 根据id删除
  46. // cr.delete(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/1"),
  47. // "", new String[] {});
  48. // 根据username删除
  49. cr.delete(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/zhangsan"), "", new String[] {});
  50. }
  51.  
  52. private void addData() {
  53. values.put("USERNAME", "zhangsan");
  54. values.put("NICKNAME", "张三");
  55. values.put("AGE", "18");
  56. values.put("GENDER", "男");
  57. cr.insert(Uri.parse("content://com.lenve.cphost.mycontentprovider"), values);
  58. }
  59. }

好了,关于自定义ContentProvider就说这么多。

Demo下载http://download.csdn.net/detail/u012702547/9330445

自定义ContentProvider的更多相关文章

  1. 自定义ContentProvider的一些细节探究

    1.   适用范围 对于什么情况下才会用到自定义的ContentProvider,官方文档的Dev Guide是这样描述的: 如果你想要提供以下的一种或几种特性的时候你才需要构造一个ContentPr ...

  2. Android组件之自定义ContentProvider

    Android的数据存储有五种方式Shared Preferences.网络存储.文件存储.外储存储.SQLite,一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他 ...

  3. Android 节日短信送祝福(功能篇:1-数据库操作类与自定义ContentProvider)

    首先,还是展示一下部分目录结构:  在节日短信送祝福的功能实现方面,为了能够方便直观展示实现过程,小编我以Java文件为基础,一个一个来展示,免得到时候这个java文件写点,一下又跳到另外一个java ...

  4. Android 程式开发:(二十)内容提供者 —— 20.6 自定义ContentProvider的使用

    现在,ContentProvider已经创建好了,可以去尝试使用一下. 1. 使用之前的工程,在布局文件main.xml中添加一些控件. <?xml version="1.0" ...

  5. 如何自定义一个优雅的ContentProvider

    最近在code review的时候发现很多人的provider定义的不是很好,写的很粗糙 以至于代码健壮性不够好,可读性也不强 但是你既然写了content provider 就是要给别人调用的,如果 ...

  6. Android探索之ContentProvider熟悉而又陌生的组件

    前言: 总结这篇文章之前我们先来回顾一下Android Sqlite数据库,参考文章:http://www.cnblogs.com/whoislcj/p/5506294.html,Android程序内 ...

  7. 解读ContentResolver和ContentProvider

    转自:http://cthhqu.blog.51cto.com/7598297/1281217 1. ContentProvider的概述 ContentProvider: (Official Def ...

  8. ContentProvider跨进程共享数据

    借用ContentResolver类访问ContentProvider中共享的数据.通过getContentResolver()方法获得该类的实例. ContentResolver中的方法:inser ...

  9. ContentProvider小结

    1.什么情况下需要使用ContentProvider 跨进程提供数据访问的接口,如果在同一个App下,没有必要使用此种方式 2.自定义ContentProvider public class MyCo ...

随机推荐

  1. oracle database resident connection pooling(驻留连接池)

    oracle在11g中引入了database resident connection pooling(DRCP).在此之前,我们可以使用dedicated 或者share 方式来链接数据库,dedic ...

  2. Python解决codeforces ---- 1

    第一题 1A A. Theatre Square time limit per test 2 seconds memory limit per test 64 megabytes input stan ...

  3. Java实现人民币大写精讲

    想要实现人民币大写,在发票等场景中使用?? 1234.56显示为:壹仟贰佰叁拾肆元伍角陆分,那就往下看看吧! 本程序可以实现 0 到 9999 9999 9999.994 以内的人民币大写转换,精确到 ...

  4. visual studio 2012更换皮肤、功能添加

    首先在vs2012的菜单:工具->扩展和更新,打开扩展和更新窗口,点击左侧“联机”,搜索栏里面输入Theme Editor.然后点击按钮,安装之后,在工具->选项->环境常规 面板上 ...

  5. easyui datagrid plunges 扩展 插件

      项目使用 springmvc4.x  spring4.x  hibernate4.x easyui 为了便于开发,扩展了easyui 的 datagrid 功能,下面直接贴上扩展代码: /** * ...

  6. 【转】NI语法 JNI参考 JNI函数大全

    原文网址:http://blog.sina.com.cn/s/blog_5de73d0b0101chk1.html 一.对照表 Java类型    本地类型         描述boolean     ...

  7. 关于I/O的那点事

    转载请著名作者和地址http://www.cnblogs.com/scotth/p/3645489.html 1.关于 IO (fopen出现的错误 errorCode 183) 相关知识点: < ...

  8. Go Hello World!

    有些事应该坚持去做 当你半途而废的时候意味着你又要重新开始.那么 Golang Hello world! Java Android 新手 学习 Golang  First Day ! go 语言下载: ...

  9. Apk修改利器:ApkToolkit v2.1

    作 者: Mzucore 时 间: 2013-05-10, 17:18:23 链 接: http://www.unpack.cn/thread-93058-1-1.html 下载地址:http://b ...

  10. [liu yanling]测试方法

    1.定义 是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例.该方法是一种重要的,常用的黑盒测试用例设计方法. 2.划分等价类 等价类 ...