Android四大组件之自定义Content Provider

今天在网上看到一篇文章。是关于金庸的《天龙八部》中段誉、乔峰、慕容复三个人,慕容复虽然会天下很多门派的功夫,却打不过段誉时灵时不灵的六脉神剑,我们再看看乔峰,只要使出降龙十八掌,便可以打败很多武林豪杰。我说出这个例子是想表达“专”和“广”的问题。其实工作三年多以来我也一直有这个毛病。总想什么都要会,到头来什么都不深的局面,就像慕容复。所以,我们应该是先在某一个领域很深入,然后由深入广。2015年我想最主要的经历都放在android研发上边,力求技术上更多的精进。参与更多的开源项目。啰嗦了这么多,只是发发感慨而已。言归正传吧。

其实,我在项目中使用Content Provider并不多,只不过想通过博客的方式记录一下我学习的笔记。以便在以后的工作上使用时,能够很快拾起。

比如我想给其他的应用程序提供数据,那么我完全可以通过URI方式给出,也就是Content Provider。因此,我们就需要自己定义一个类去继承 ContentProvider,那么我们就简单的介绍一下其中的方法吧。

package com.example.contentproviderdemo;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class MyProvider extends ContentProvider {

	@Override
	public boolean onCreate() {
		// 只有当第一次ContentResolver对象进行数据操作时,才会执行此方法.
		// 一般在此方法进行一些初始化的操作。初始化成功返回true,失败返回false
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		// 可以理解成对应的sql语句 select 列 ... from 表名  where 条件
		// uri 可以指定某一个表 
		// 返回一个游标
		return null;
	}

	@Override
	public String getType(Uri uri) {
		// 根据uri返回mime类型
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// 插入数据 对应sql语句  insert into 表(列...) values(值 ...)
		// 返回插入数据对应的URI
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// 删除数据 delete from 表 	where 条件
		// 返回删除的行数
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		// 更新数据 update 表 	set 列 = 值 .... where 条件
		// 返回更新数据的行数
		return 0;
	}

}

按照相应的要求重写方法即可。

那我们在回顾一下URI的格式吧。content://权限/路径,比如content://com.example.contentproviderdemo.provider/table1 其中table1表示表名称。还有就是URI中使用的通配符。

*:表示匹配任意长度的任意字符。比如:content://com.example.contentproviderdemo.provider/* 代表匹配任意表的任意数据。

#:表示匹配任意长度的数字。比如:content://com.example.contentproviderdemo.provider/table1/# 代表匹配table1表中某一个id的数据。

上边有一个getType方法需要重点介绍一下。Android中对URI对应MIME类型做了相应的规定。具体的MIME由三个部分组成的。

1. 必须是以vnd开头。

2. 如果内容URI是以路径结尾,后接android.cursor.dir/。如果是以id结尾的话,后接android.cursor.item/。

3.最后接上vnd.<权限>.<路径>

比如:content://com.example.contentproviderdemo.provider/table1对应的mime类型为:vnd.android.cursor.dir/vnd.com.example.contentproviderdemo.provider.table1。

content://com.example.contentproviderdemo.provider/table1/#对应的mime类型为:vnd.android.cursor.item/vnd.com.example.contentproviderdemo.provider.table1。


根据我以上介绍的内容,我们就很容易写出一个关于ContentProvider的代码模型了。如下:

package com.example.contentproviderdemo;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

public class MyProvider extends ContentProvider {
	
	/**
	 * 匹配table1表的所有行数据
	 */
	private static final int TABLE1_DIR = 0;
	/**
	 * 匹配table1表的某一个id的行数据
	 */
	private static final int TABLE1_ITEM = 1;
	
	private static UriMatcher mMatcher;
	
	static{
		mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		// 添加匹配规则
		mMatcher.addURI("android.content.ContentProvider.provider", "table1", TABLE1_DIR);
		mMatcher.addURI("android.content.ContentProvider.provider", "table1/#", TABLE1_ITEM);
	}

	
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		
		switch (mMatcher.match(uri)) {
		case TABLE1_DIR:
			// 查询所有的table1的数据
			break;
		case TABLE1_ITEM:
			// 查询某一个id的数据
			break;
		default:
			break;
		}
		return null;
	}

	@Override
	public String getType(Uri uri) {
		if (mMatcher.match(uri) == TABLE1_DIR){
			return "vnd.android.cursor.dir/com.example.contentproviderdemo.provider.table1";
		}else if (mMatcher.match(uri) == TABLE1_ITEM){
			return "vnd.android.cursor.item/com.example.contentproviderdemo.provider.table1";
		}else{
			return null;
		}
	}

}
实例中仅仅是写出query方法,其实update、insert、delete都是差不多的。

介绍了这么多的基础知识,我们有必要实战一下,让自己的印象更加的深刻。具体工程包含两个,一个时contentprovider实现,另一个是测试代码。

1. 先看看PersonProvider代码:

package com.example.contentproviderdemo;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

public class PersonProvider extends ContentProvider {

	private DBHelper mHelper;
	
	private static final int PERSON_DIR = 0;
	private static final int PERSON_ITEM = 1;

	private static final String tag = "PersonProvider";
	
	private static UriMatcher mMatcher;
	
	static{
		// 添加匹配规则
		mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person", PERSON_DIR);
		mMatcher.addURI("com.example.contentproviderdemo.PersonProvider.provider", "person/#", PERSON_ITEM);
	}
	
	@Override
	public boolean onCreate() {
		mHelper = new DBHelper(getContext(), "test.db");
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		Cursor cursor = null;
		SQLiteDatabase db = mHelper.getReadableDatabase();
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			Log.d(tag, "PERSON_DIR");
			cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
			break;
		case PERSON_ITEM:
			Log.d(tag, "PERSON_ITEM");
			// 获取id.内部是把uri是"/"进行分割了,所以获取get(1)就是id值。
			String personID = uri.getPathSegments().get(1);
			cursor = db.query("person", projection, "_id = ?", new String[]{personID}, null, null, sortOrder);
			break;
		default:
			Log.d(tag, "Invalid uri");
			break;
		}
		return cursor;
	}

	@Override
	public String getType(Uri uri) {
		String result = null;

		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = "vnd.android.cursor.dir/com.example.contentproviderdemo.PersonProvider.provider.person";
			break;
		case PERSON_ITEM:
			result = "vnd.android.cursor.item/com.example.contentproviderdemo.PersonProvider.provider.person";
			break;
		default:
			break;
		}
		
		return result;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		Uri result = null;
		SQLiteDatabase db = mHelper.getWritableDatabase();
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			long id = db.insert("person", null, values);
			String uriString = "content://com.example.contentproviderdemo.PersonProvider.provider/person/" + id;
			result = Uri.parse(uriString);
			break;
		default:
			break;
		}
		return result;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		int result = 0;
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = db.delete("person", selection, selectionArgs);
			break;
		case PERSON_ITEM:
			String personID = uri.getPathSegments().get(1);
			result = db.delete("person", "_id = ?", new String[]{personID});
			break;
		default:
			break;
		}
		
		return result;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		int result = 0;
		
		switch (mMatcher.match(uri)) {
		case PERSON_DIR:
			result = db.update("person", values, selection, selectionArgs);
			break;
		case PERSON_ITEM:
			String personID = uri.getPathSegments().get(1);
			result =  db.update("person", values, "_id = ?", new String[]{personID});
			break;
		default:
			break;
		}
		
		return result;
	}

}

代码比较简单,关键地方还有注释,就不介绍了。要想让其它的进程能够访问,可以在AndroidManifest.xml中进行配置。

  <provider 
            android:name="com.example.contentproviderdemo.PersonProvider"
            android:authorities="com.example.contentproviderdemo.PersonProvider.provider"
            android:exported="true"
            ></provider>
节点exported的含义是,如果要用于其它进程中组件进行交互就得设置为TRUE,否则会报权限不允许的异常。

经过以上的讲解,我觉得是把contentprivider讲通了。


不知不觉写博客已经坚持一段时间了。以后还得坚持下去,通过博客见证自己的成长。

今天的代码的下载地址为:下载源码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值