ContentProvider内容提供者(一)

作为四大组件之一的ContentProvider,其作用是对外共享数据,相对文件和数据库存储来说,它的好处是统一了数据访问方式。

先来看看基本的介绍:

Class Overview

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.

When a request is made via a ContentResolver the system inspects the authority of the given URI and passes the request to the content provider registered with the authority. The content provider can interpret the rest of the URI however it wants. The UriMatcher class is helpful for parsing URIs.

The primary methods that need to be implemented are:

  • onCreate() which is called to initialize the provider
  • query(Uri, String[], String, String[], String) which returns data to the caller
  • insert(Uri, ContentValues) which inserts new data into the content provider
  • update(Uri, ContentValues, String, String[]) which updates existing data in the content provider
  • delete(Uri, String, String[]) which deletes data from the content provider
  • getType(Uri) which returns the MIME type of data in the content provider

Data access methods (such as insert(Uri, ContentValues) and update(Uri, ContentValues, String, String[])) may be called from many threads at once, and must be thread-safe. Other methods (such as onCreate()) are only called from the application main thread, and must avoid performing lengthy operations. See the method descriptions for their expected thread behavior.

Requests to ContentResolver are automatically forwarded to the appropriate ContentProvider instance, so subclasses don't have to worry about the details of cross-process calls.

要实现对外数据共享,该类必须继承一个抽象类:ContentProvider,并且实现它的以上6个方法,在onCreate()方法中作初始化操作,在getType中返回Uri的MIME类型,其他四个方法分别来完成对数据的添删改查操作。

这里有个Uri匹配类:UriMatcher,它的基本介绍如下:

Class Overview

Utility class to aid in matching URIs in content providers.

To use this class, build up a tree of UriMatcher objects. For example:

    private static final int PEOPLE = 1;     private static final int PEOPLE_ID = 2;     private static final int PEOPLE_PHONES = 3;     private static final int PEOPLE_PHONES_ID = 4;     private static final int PEOPLE_CONTACTMETHODS = 7;     private static final int PEOPLE_CONTACTMETHODS_ID = 8;     private static final int DELETED_PEOPLE = 20;     private static final int PHONES = 9;     private static final int PHONES_ID = 10;     private static final int PHONES_FILTER = 14;     private static final int CONTACTMETHODS = 18;     private static final int CONTACTMETHODS_ID = 19;     private static final int CALLS = 11;     private static final int CALLS_ID = 12;     private static final int CALLS_FILTER = 15;     private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);     static     {         sURIMatcher.addURI("contacts", "people", PEOPLE);         sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID);         sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES);         sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID);         sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS);         sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);         sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE);         sURIMatcher.addURI("contacts", "phones", PHONES);         sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER);         sURIMatcher.addURI("contacts", "phones/#", PHONES_ID);         sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS);         sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID);         sURIMatcher.addURI("call_log", "calls", CALLS);         sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER);         sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);     }

Then when you need to match against a URI, call match(Uri), providing the URL that you have been given. You can use the result to build a query, return a type, insert or delete a row, or whatever you need, without duplicating all of the if-else logic that you would otherwise need. For example:

    public String getType(Uri url)     {         int match = sURIMatcher.match(url);         switch (match)         {             case PEOPLE:                 return "vnd.android.cursor.dir/person";             case PEOPLE_ID:                 return "vnd.android.cursor.item/person"; ... snip ...                 return "vnd.android.cursor.dir/snail-mail";             case PEOPLE_ADDRESS_ID:                 return "vnd.android.cursor.item/snail-mail";             default:                 return null;         }     }
instead of:
    public String getType(Uri url)     {         List pathSegments = url.getPathSegments();         if (pathSegments.size() >= 2) {             if ("people".equals(pathSegments.get(1))) {                 if (pathSegments.size() == 2) {                     return "vnd.android.cursor.dir/person";                 } else if (pathSegments.size() == 3) {                     return "vnd.android.cursor.item/person"; ... snip ...                     return "vnd.android.cursor.dir/snail-mail";                 } else if (pathSegments.size() == 3) {                     return "vnd.android.cursor.item/snail-mail";                 }             }         }         return null;     }

Public Methods
void addURI(String authority, String path, int code)
Add a URI to match, and the code to return when this URI is matched.
int match(Uri uri)
Try to match against the path in a url.

其他应用可以通过ContentResolver的实例来访问内容提供者的数据,它的基本介绍:

java.lang.Object
   ↳ android.content.ContentResolver
Known Direct Subclasses

Class Overview

This class provides applications access to the content model.

通过它的insert()、delete()、update()、query()来对内容提供者数据进行添删改查操作。


下面通过一个实例来看一下如何使用ContentProvider进行数据共享

我们需要建立两个project:Android_ContentProvider和Android_ContentProvider_Test,分别作为内容提供者和对数据访问者。

在前者中,我们定义一个自己的PersonProvider,提供对person表的数据操作,在后者中,我们通过Junit Test 的test方法来完成数据操作测试。

Android_ContentProvider的目录结构:

ContentProvider内容提供者(一)_第1张图片

这里的布局文件和Ui等没有意义,我们重点看一下DBOpenHelper和PersonProvider两个类,分别是数据库工具类和自定义内容提供者,

DBOpenHelper.java代码如下:

package com.wujay.provider.service;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper{

	/**
	 * 构造函数
	 * @param context
	 */
	public DBOpenHelper(Context context) {
		super(context, "wujay.db", null, 2);
	}

	/* 
	 * 初始化
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("create table person(personId integer primary key autoincrement, name varchar(20)," +
				"phone varchar(12) null,amount varchar(20) null)");

	}

	/* 
	 * 更新操作
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("alter table person add amount integer null");

	}


}

在这个类中实现数据库的创建以及更新操作。

PersonProvider.java代码如下:

package com.wujay.provider.db;

import com.wujay.provider.service.DBOpenHelper;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class PersonProvider extends ContentProvider {
	private DBOpenHelper dbOpenHelper;
	private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
	private static int PERSONS = 1; 	//对所有数据操作
	private static int PERSON = 2; 		//操作指定的数据
	
	/* 
	 * 静态代码块
	 */
	static{
		MATCHER.addURI("com.wujay.providers.personprovider", "person", PERSONS);
		MATCHER.addURI("com.wujay.providers.personprovider", "person/#", PERSON);
	}
	
	/* 
	 * 初始化
	 */
	@Override
	public boolean onCreate() {
		dbOpenHelper = new DBOpenHelper(getContext());
		return false;
	}

	/* 
	 * 根据条件查询
	 */
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
		int num = 0;
		switch (MATCHER.match(uri)) {
		case 1:
			return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
		case 2:
			long rowId = ContentUris.parseId(uri);
			String where = "personId=" + rowId;
			if(selection!=null && "".equals(selection.trim())){
				where += " and "+selection;
			}
			return db.query("person", projection, where, selectionArgs, null, null, sortOrder);
		default:
			throw new IllegalArgumentException("this is a unknown URI:"+uri);
		}
	}

	/* 
	 * 用于返回当前Uri所代表的数据的MIME类型
	 */
	@Override
	public String getType(Uri uri) {
		switch (MATCHER.match(uri)) {
		case 1:
			return "vnd.android.cursor.dir/person";
		case 2:
			return "vnd.android.cursor.item/person";
		default:
			throw new IllegalArgumentException("this is a unknown URI:"+uri);
		}
	}

	/* 
	 * 插入数据
	 */
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		switch (MATCHER.match(uri)) {
		case 1:
			long rowId = db.insert("person", "name", values);
			Uri insertUri = ContentUris.withAppendedId(uri, rowId);
			break;

		default:
			throw new IllegalArgumentException("this is a unknown URI:"+uri);
		}
		return null;
	}

	/* 
	 * 删除数据
	 */
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		int num = 0;
		switch (MATCHER.match(uri)) {
		case 1:
			num = db.delete("person", selection, selectionArgs);
			break;
		case 2:
			long rowId = ContentUris.parseId(uri);
			String where = "personId=" + rowId;
			if(selection!=null && "".equals(selection.trim())){
				where += " and "+selection;
			}
			num = db.delete("person", where, selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("this is a unknown URI:"+uri);
		}
		return num;
	}

	/* 
	 * 修改数据
	 */
	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		int num = 0;
		switch (MATCHER.match(uri)) {
		case 1:
			num = db.update("person", values, selection, selectionArgs);
			break;
		case 2:
			long rowId = ContentUris.parseId(uri);
			String where = "personId=" + rowId;
			if(selection!=null && "".equals(selection.trim())){
				where += " and "+selection;
			}
			num = db.update("person", values, where, selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("this is a unknown URI:"+uri);
		}
		return num;
	}

}


该类中提供了5个方法用来提供对数据操作,我们在测试工程中通过ContentResolver操作的Uri数据,就是调用这里的方法来完成。

仅仅这样,我们并没有完成自定义ContentProvider,我们还需要对内容提供者进行注册,在AndroidManifest.xml来完成注册:

在application中加入一下声明:

<provider android:name="com.wujay.provider.db.PersonProvider" 
            android:authorities="com.wujay.providers.personprovider"/>

这里的authorities必须是唯一的,其他应用就是通过这个唯一标识来找到该内容提供者,类似一个网站的域名,通过域名访问到指定主机。


Android_ContentProvider_Test的目录结构:

ContentProvider内容提供者(一)_第2张图片

既然是测试类,我们就需要引入单元测试环境:

<uses-library android:name="android.test.runner" />

<instrumentation
        android:label="Test for my app"
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.content.test.ui" />

重点来看一下TestProvider.java类:

package com.content.test.ui;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.util.Log;

public class TestProvider extends AndroidTestCase {
	private static final String TAG = "testProvider";
	
	/**
	 * 测试插入数据
	 * @throws Exception
	 */
	public void testInsert() throws Exception{
		Uri uri = Uri.parse("content://com.wujay.providers.personprovider/person");
		ContentResolver resolver = getContext().getContentResolver();
		ContentValues values = new ContentValues();
		values.put("name", "wujay");
		values.put("phone", "1802332877");
		values.put("amount", "180000");
		resolver.insert(uri, values);
	}
	
	/**
	 * 测试删除数据
	 * @throws Exception
	 */
	public void testDelete() throws Exception{
		Uri uri = Uri.parse("content://com.wujay.providers.personprovider/person/2");
		ContentResolver resolver = getContext().getContentResolver();
		resolver.delete(uri, null, null);
	}
	
	
	/**
	 * 测试修改数据
	 * @throws Exception
	 */
	public void testUpdate() throws Exception{
		Uri uri = Uri.parse("content://com.wujay.providers.personprovider/person/2");
		ContentResolver resolver = getContext().getContentResolver();
		ContentValues values = new ContentValues();
		values.put("name", "wujay123456");
		resolver.update(uri, values, null, null);
	}
	
	
	/**
	 * 测试查询数据,并输出到控制台
	 * @throws Exception
	 */
	public void testQuery() throws Exception{
		Uri uri = Uri.parse("content://com.wujay.providers.personprovider/person");
		ContentResolver resolver = getContext().getContentResolver();
		Cursor c = resolver.query(uri, null, null, null, "personId asc");
		while (c.moveToNext()) {
			String name = c.getString(c.getColumnIndex("name"));
			Log.i(TAG, name);
		}
		c.close();
	}
}

我们来看一下update和query得到的结果:

ContentProvider内容提供者(一)_第3张图片




你可能感兴趣的:(android,ContentProvider)