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

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

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

public class DBHelper extends SQLiteOpenHelper {public static final String USERTABLE = "user_table";public DBHelper(Context context, String name, CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE IF NOT EXISTS " + USERTABLE+ "(_id INTEGER PRIMARY KEY AUTOINCREMENT,USERNAME TEXT,NICKNAME TEXT,GENDER TEXT,AGE TEXT);");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}

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

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent" ></ListView></RelativeLayout>

MainActivity.java:

public class MainActivity extends Activity {private ListView lv;private DBHelper helper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lv = (ListView) this.findViewById(R.id.lv);}public List<User> getData() {helper = new DBHelper(this, "lenve.db", null, 1);SQLiteDatabase db = helper.getWritableDatabase();List<User> list = new ArrayList<User>();Cursor c = db.rawQuery("SELECT * FROM " + DBHelper.USERTABLE, null);User u = null;while (c.moveToNext()) {String username = c.getString(c.getColumnIndex("USERNAME"));String nickname = c.getString(c.getColumnIndex("NICKNAME"));String gender = c.getString(c.getColumnIndex("GENDER"));String age = c.getString(c.getColumnIndex("AGE"));u = new User(username, nickname, gender, age);list.add(u);}c.close();return list;}@Overrideprotected void onResume() {super.onResume();lv.setAdapter(new MyAdapter(this, getData()));}
}

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

public class User {private String username;private String nickname;private String gender;private String age;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public User(String username, String nickname, String gender, String age) {this.username = username;this.nickname = nickname;this.gender = gender;this.age = age;}public User() {}}

还有一个MyAdapter:

public class MyAdapter extends BaseAdapter {private Context context;private List<User> list;public MyAdapter(Context context, List<User> list) {this.context = context;this.list = list;}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {convertView = LayoutInflater.from(context).inflate(R.layout.lv_item, null);holder = new ViewHolder();holder.ageTv = (TextView) convertView.findViewById(R.id.age_tv);holder.nickNameTv = (TextView) convertView.findViewById(R.id.nickname_tv);holder.userNameTv = (TextView) convertView.findViewById(R.id.username_tv);holder.genderTv = (TextView) convertView.findViewById(R.id.gender_tv);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}User u = list.get(position);holder.ageTv.setText(u.getAge());holder.genderTv.setText(u.getGender());holder.nickNameTv.setText(u.getNickname());holder.userNameTv.setText(u.getUsername());return convertView;}class ViewHolder {TextView userNameTv, nickNameTv, genderTv, ageTv;}
}

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

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

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

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

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

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

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

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

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

    @Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {int code = matcher.match(uri);int result = 0;switch (code) {case UriMatcher.NO_MATCH:break;case 1:// 删除所有result = db.delete(DBHelper.USERTABLE, null, null);Log.d("qf", "删除所有数据!");break;case 2:// content://com.lenve.cphost.mycontentprovider/user/10// 按条件删除,idresult = db.delete(DBHelper.USERTABLE, "_id=?", new String[] { ContentUris.parseId(uri) + "" });Log.d("qf", "根据删除一条数据");break;case 3:// content://com.lenve.cphost.mycontentprovider/user/zhangsan// uri.getPathSegments()拿到一个List<String>,里边的值分别是0-->user、1-->zhangsanresult = db.delete(DBHelper.USERTABLE, "USERNAME=?", new String[] { uri.getPathSegments().get(1) });break;default:break;}return result;}

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

这里我们介绍两个方法,

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

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

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

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

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

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

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

public class MyContentProvider extends ContentProvider {private SQLiteOpenHelper helper;private SQLiteDatabase db;private static UriMatcher matcher;private static final String AUTHORITY = "com.lenve.cphost.mycontentprovider";static {matcher = new UriMatcher(UriMatcher.NO_MATCH);matcher.addURI(AUTHORITY, "user", 1);// 配置表matcher.addURI(AUTHORITY, "user/#", 2);// 匹配任何数字matcher.addURI(AUTHORITY, "user/*", 3);// 匹配任何文本}@Overridepublic boolean onCreate() {helper = new DBHelper(getContext(), "lenve.db", null, 1);db = helper.getWritableDatabase();Log.d("qf", "MyContentProvider--->onCreate()");return true;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {return null;}@Overridepublic String getType(Uri uri) {return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {db.insert(DBHelper.USERTABLE, null, values);return null;}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {int code = matcher.match(uri);int result = 0;switch (code) {case UriMatcher.NO_MATCH:break;case 1:// 删除所有result = db.delete(DBHelper.USERTABLE, null, null);Log.d("qf", "删除所有数据!");break;case 2:// content://com.lenve.cphost.mycontentprovider/user/10// 按条件删除,idresult = db.delete(DBHelper.USERTABLE, "_id=?", new String[] { ContentUris.parseId(uri) + "" });Log.d("qf", "根据删除一条数据");break;case 3:// content://com.lenve.cphost.mycontentprovider/user/zhangsan// uri.getPathSegments()拿到一个List<String>,里边的值分别是0-->user、1-->zhangsanresult = db.delete(DBHelper.USERTABLE, "USERNAME=?", new String[] { uri.getPathSegments().get(1) });break;default:break;}return result;}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {int code = matcher.match(uri);int result = 0;switch (code) {case 1:result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);break;case 2:result = db.update(DBHelper.USERTABLE, values, "_id=" + ContentUris.parseId(uri) + " AND " + selection,selectionArgs);break;// 根据手动传参id来更新case 3:result = db.update(DBHelper.USERTABLE, values, selection, selectionArgs);break;}return result;}

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

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

        <providerandroid:name="com.lenve.cphost.MyContentProvider"android:authorities="com.lenve.cphost.mycontentprovider"android:exported="true" />

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

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

核心代码如下:

public class MainActivity extends Activity {private ContentResolver cr;private ContentValues values;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);cr = getContentResolver();values = new ContentValues();}public void onClickBtn(View v) {switch (v.getId()) {case R.id.add_btn:addData();break;case R.id.delete_btn:deleteData();break;case R.id.update_btn:updateData();break;case R.id.search_btn:break;default:break;}}private void updateData() {// values.put("USERNAME", "lisi");// cr.update(Uri.parse("content://com.lenve.cphost.mycontentprovider/user"),// values, "_id=?",// new String[] { 4 + "" });values.put("USERNAME", "wangwu");cr.update(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/3"), values, "USERNAME=?",new String[] { "lisi" });}private void deleteData() {// 根据id删除// cr.delete(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/1"),// "", new String[] {});// 根据username删除cr.delete(Uri.parse("content://com.lenve.cphost.mycontentprovider/user/zhangsan"), "", new String[] {});}private void addData() {values.put("USERNAME", "zhangsan");values.put("NICKNAME", "张三");values.put("AGE", "18");values.put("GENDER", "男");cr.insert(Uri.parse("content://com.lenve.cphost.mycontentprovider"), values);}
}

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

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

转载于:https://www.cnblogs.com/lenve/p/5865921.html

自定义ContentProvider相关推荐

  1. Android之自定义ContentProvider详解

    第一个版本  对android中MIME类型的理解 初始MIME类型,是在学习ContentProvider的时候. 当在创建自己的ContentProvider的时,需要从抽象类ContentPro ...

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

    http://blog.csdn.net/sadfishsc/article/details/7419573 1.   适用范围 对于什么情况下才会用到自定义的ContentProvider,官方文档 ...

  3. 四大组件之ContentProvider(二)-轻轻松松自定义ContentProvider

    更新时间 修改意见 2016-08-02 陈敏 第3节 自定义ContentProvider 自定义一个ContentProvider,需要 继承ContentProvider类重新创建一个类,并实现 ...

  4. Study on Android【五】--自定义ContentProvider的语义

    Android做到现在.开始感觉到Android确实还是太年轻.系统本身好不够成熟,相关文档更是少的可怜.在Android的旅途中到处是暗坑陷阱,掉进去摔得半死,还只能靠自己琢磨着爬出来. 想 在大部 ...

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

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

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

    郑重声明:[慕课网–中级教程]系列的博文均是根据慕课网上的Android开发中级教程相应的视频,在学习之后写的,文中所涉及的源码与视频中的无太大区别,除开个别地方可能有所改善或者加入了自己的想法.如果 ...

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

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

  8. 自定义ContentProvider实现跨进程数据共享

    最近要开发一个跨进程数据共享的功能,要实现这个功能,我脑海里第一个想到的是用SharedPreferences,但是发现在Android 7.0以上的版本Context.MODE_WORLD_READ ...

  9. 【Android 安全】DEX 加密 ( Application 替换 | 分析 ContentProvider 组件中调用 getApplication() 获取的 Application )

    文章目录 一. ContentProvider 创建过程分析 二. ActivityThread 中的 H 处理 BIND_APPLICATION 消息 三. ActivityThread 中的 ha ...

  10. ContentProvider再探——Document Provider

    概述 通过ContentProvider初探相信你已经知道如何去使用系统提供的ContentProvider或者自定义ContentProvider了, 已经基本满足日常开发的需求了,有趣的是,我在官 ...

最新文章

  1. mysql8.0取消授权_mysql8创建用户、删除用户、授权、取消授权
  2. 快速理解mysql主从,主主备份原理及实践
  3. Oracle 查询结果去重保留一项
  4. 第六章连接和分组查询
  5. Java8 Stream详解~归约(reduce)
  6. 【读书笔记】A Swift Tour
  7. linux和安卓交互,Android中webview和js之间的交互及注意事项
  8. php获取当前文件夹下所有图片大小,PHP获取文件夹大小函数用法实例
  9. android简单小游戏开发工具,傻瓜化开发Android小游戏
  10. 【聚类3】密度聚类+层次聚类
  11. 毕业设计 大学生心理健康管理平台
  12. Android项目中使用XUI框架的准备工作
  13. 深入理解计算机系统寄存器寻址讲解
  14. Netflix-Eureka服务注册与发现说明文档
  15. MAC 用命令查看IP
  16. My Fifty-Sixth Page - 子集Ⅱ - By Nicolas
  17. 基于树莓派的语音对话助手 百度机器人 适合入门
  18. 如何在VMware Workstation上安装Nutanix CE的解决方案
  19. 公众号的发文和留言都会显示城市了!来试试!
  20. Windows OpenGL 图像色调

热门文章

  1. DeepFake技术--DeepFakes 概述(一)(二)
  2. Python 手写数字识别实战分享
  3. 在线编程无法在sublime中使用input()和raw_input()的解决方法
  4. linux基础(二)——linux各文件夹含义和作用
  5. at shutdown 不起作用_at胎是什么胎
  6. latex 数字上标 指数怎么打
  7. php float转int 元转分
  8. JavaSE基础———对象数组和集合Collection
  9. Leetcode之最长公共前缀
  10. mysql慢查询ep_mysql 慢查询分析