Android基础——ContentProvider使用

注:本文参考 http://blog.sina.com.cn/s/blog_9f233c070101euqx.html

ContentProvider 提供了一个接口用来发布数据,通过ContentResolver 来使用该数据。它们允许将使用数据的应用程序组件和底层数据源分离开来,并提供一种通用的机制来允许一个应用程序共享它们的数据或者使用其它应用程序供的数据。


一、Uri介绍

Uri代表要操作的数据,Uri主要包含了两部分信息:
  • 需要操作的ContentProvider   
  •  对ContentProvider中的什么数据进行操作
URI主要分三个部分:scheme, authority and path。其中authority又分为host和port。 
  • 格式如下:scheme://host:port/path
 ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定。
如下以person 表为例
1、要操作person表中id为10的记录,可以构建这样的路径:/person/10
2、要操作person表中id为10的记录的name字段, person/10/name
3、要操作person表中的所有记录,可以构建这样的路径:/person
4、要操作xxx表中的记录,可以构建这样的路径:/xxx
使用Uri类中的parse()方法把一个字符串转换成Uri,如下:
Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person")
当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方.

二、UriMatcher和ContentUris使用介绍

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。
  • UriMatcher
UriMatcher类用于匹配Uri,它的用法如下
首先第一步把你需要匹配Uri路径全部给注册上,如下:
	//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
	UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
	//如果match()方法匹配content://com.cfox.contentprovid.PersonProvider/person路径,返回匹配码为1
	sMatcher.addURI("com.cfox.contentprovid.PersonProvider", "person", 1);//添加需要匹配uri,如果匹配就会返回匹配码
	//如果match()方法匹配content://com.cfox.contentprovid.PersonProvider/person/230路径,返回匹配码为2
	sMatcher.addURI("com.cfox.contentprovid.PersonProvider", "person/#", 2);//#号为通配符
	switch (sMatcher.match(Uri.parse("content://com.cfox.contentprovid.PersonProvider/person/10"))) { 
  		 case 1
    	 break;
  		 case 2
    	 break;
  		 default://不匹配
   		  break;
	}
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配。如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配content://com.cfox.contentprovid.PersonProvider路径,返回的匹配码为1。

  • ContentUris使用介绍
ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
	Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person")
	Uri resultUri = ContentUris.withAppendedId(uri, 10); 
	//生成后的Uri为:content://com.cfox.contentprovid.PersonProvider/person/10
parseId(uri)方法用于从路径中获取ID部分:
	Uri uri = Uri.parse("content://com.cfox.contentprovid.PersonProvider/person/10")
	long personid = ContentUris.parseId(uri);//获取的结果为:10

三、ContentProvider(内容提供者)共享数据

ContentProvider在android中的作用是对外共享数据,可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行增删改查。关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE或Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。那么,这里为何要使用ContentProvider对外共享数据呢?是这样的,如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数 据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。使用ContentProvider对外共享数据的好处是统一了数据的访问方式。

  • ContentProvider对外共享数
第一步需要继承ContentProvider并重写下面方法:
public class BaseContentProvider extends ContentProvider {

	@Override
	public boolean onCreate() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}
}
ContentProvider类主要方法的作用:
    1. public boolean onCreate():该方法在ContentProvider创建后就会被调用,Android开机后,ContentProvider在其它应用第一次访问它时才会被创建,同时,要讲返回值修改成true。
     2. public Uri insert(Uri uri, ContentValues values):该方法用于供外部应用往ContentProvider添加数据。
     3. public int delete(Uri uri, String selection, String[] selectionArgs):该方法用于供外部应用从ContentProvider删除数据。
     4. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):该方法用于供外部应用更新ContentProvider中的数据。

     5. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):该方法用于供外部应用从ContentProvider中获取数据。
     6. public String getType(Uri uri):该方法用于返回当前Url所代表数据的MIME类型。

 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。
例如:要得到所有person记录的Uri为content://com.cfox.contentprovid.PersonProvider/person
     那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头
例如:得到id为10的person记录,Uri为content://com.cfox.contentprovid.PersonProvider/person/10
     那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"
	public String getType(Uri uri) {
		UriMatcher uriMatcher = getUriMatcher();
		

		int match = uriMatcher.match(uri);
		switch (match) {
		case ITEMS:
			return "vnd.android.cursor.dir/" + person;
		case ITEMS_ID:
			return "vnd.android.cursor.item/" + person;
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
	}

第二步需要在AndroidManifest.xml使用对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider ,ContentProvider采用了authorities(主机名/域名)对它进行唯一标识,你可以把ContentProvider看作是一个网 站(想想,网站也是提供数据者),authorities 就是他的域名:
	<provider
            android:exported="true"
            android:name="com.example.contentprovideranddb.ContentProvider.PersonProvider"
            android:authorities="com.cfox.contentprovid.PersonProvider" />


四、使用ContentResolver操作ContentProvider中的数据

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:
public Uri insert(Uri uri, ContentValues values):该方法用于往ContentProvider添加数据。
public int delete(Uri uri, String selection, String[] selectionArgs):该方法用于从ContentProvider删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):该方法用于更新ContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):该方法用于从ContentProvider中获取数据。
这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作, 例如 Uri是:Uri.parse("content://com.cfox.contentprovid.PersonProvider/person /10"),那么将会对主机名为com.cfox.contentprovid.PersonProvider的ContentProvider进行操作,操作的数 据为person表中id为10的记录。 使用ContentResolver对ContentProvider中的数据进行添加、删除、修改和查询操作。

五、监听ContentProvider中数据的变化

如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化,可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:
public class BaseContentProvider extends ContentProvider {

	略.........

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		
	//添加数据变化通知
	getContext().getContentResolver().notifyChange(uri, null);
		return 返回一个Uri;
	}
	略........

}


如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法:
public class MainActivity extends Activity {
	private Handler mHandler = new Handler();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		this.getContentResolver().registerContentObserver(PersonProvider.uri, false, new MyObserver(mHandler));
	}
	
	public class MyObserver extends ContentObserver{

		public MyObserver(Handler handler) {
			super(handler);
		}
		
		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
			//数据改变时调用
			System.out.println("changing ------>");
		}
	}
}

附:

在android4.2 在使用到 ContentProvider 的时候遇到的问题,报错: Permission Denial: opening provider  uid=10033) ....  that is not exported from uid 10036 
  • 在AndroidManifest.xml 中 android:exported 属性,这个属性用于指示该服务是否能被其他程序应用组件调用或跟他交互; 取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
        <provider
            android:exported="true"
            android:name="com.example.contentprovideranddb.ContentProvider.PersonProvider"
            android:authorities="com.cfox.contentprovid.PersonProvider" />
  • 重要:它的默认值是依赖于该服务所包含的过滤器
(1)如果没有过滤器则意味着该服务只能通过指定明确的类名来调用,也就是说该服务只能在应用程序内部使用(因为其他外部使用者不会知道该服务的类名),此时它的默认值是false
(2)如果至少包含了一个过滤器,则意味着该服务可以给外部的其他应用提供服务,因此默认值是true。


Dome 代码:

PersonProvider.java
package com.example.contentprovideranddb.ContentProvider;

import android.content.UriMatcher;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.BaseColumns;

public class PersonProvider extends BaseProvider {
	
	public static final String AUTHORITY = "com.cfox.contentprovid.PersonProvider";
	public static final String TAB_PATH = "person";
	public static final Uri uri = Uri.withAppendedPath(Uri.parse("content://" + AUTHORITY), TAB_PATH);
	public static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
	
	static{
		mUriMatcher.addURI(AUTHORITY, TAB_PATH, BaseProvider.ITEMS);
		mUriMatcher.addURI(AUTHORITY, TAB_PATH + "/#", BaseProvider.ITEMS_ID);
	}
	
	public static class Colnums implements BaseColumns{
		public static final String name = "name";
		public static final String age = "age";
		public static final String address = "address";
		
	}
	
	public static void createTable(SQLiteDatabase db){
		db.execSQL("create table " + TAB_PATH + "(" + Colnums._ID + " integer primary key autoincrement, " 
				 + Colnums.name + " text ," 
				 + Colnums.age +  " integer, " 
				 + Colnums.address + " text );");
	}

	@Override
	public UriMatcher getUriMatcher() {
		return mUriMatcher;
	}

	@Override
	public String getTablePath() {
		return TAB_PATH;
	}

	@Override
	public Uri getUri() {
		return uri;
	}

}
DatabaseHelper.java
package com.example.contentprovideranddb.db;

import com.example.contentprovideranddb.ContentProvider.PersonProvider;

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

public class DatabaseHelper extends SQLiteOpenHelper {

	private static final String SQL_NAME = "person_info";
	private static final int SQL_VERION = 1;
	private static DatabaseHelper mDatabaseHelper;

	public DatabaseHelper(Context context) {
		super(context, SQL_NAME, null, SQL_VERION);
	}

	public DatabaseHelper(Context context, String name, int version) {
		super(context, name, null, version);
	}

	public static DatabaseHelper getInstance(Context context) {
		if (mDatabaseHelper == null) {
			mDatabaseHelper = new DatabaseHelper(context);
		}
		return mDatabaseHelper;
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		PersonProvider.createTable(db);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}

}
BaseProvider.java
package com.example.contentprovideranddb.ContentProvider;

import com.example.contentprovideranddb.ContentProvider.PersonProvider.Colnums;
import com.example.contentprovideranddb.db.DatabaseHelper;

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.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.SyncStateContract.Columns;

public abstract class BaseProvider extends ContentProvider {

	public static final int ITEMS = 1;
	public static final int ITEMS_ID = 2;
	private SQLiteOpenHelper mSqlitOpenHelper;

	public abstract UriMatcher getUriMatcher();

	public abstract String getTablePath();

	public abstract Uri getUri();

	@Override
	public boolean onCreate() {
		mSqlitOpenHelper = DatabaseHelper.getInstance(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

		SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();
		UriMatcher uriMatcher = getUriMatcher();
		String tablePath = getTablePath();
		
		uriMatcher.addURI(PersonProvider.AUTHORITY, PersonProvider.TAB_PATH, BaseProvider.ITEMS);
		
		int match = uriMatcher.match(uri);
		switch (match) {
		case ITEMS:
			sqLiteQueryBuilder.setTables(tablePath);
			break;

		case ITEMS_ID:
			sqLiteQueryBuilder.setTables(tablePath);
			sqLiteQueryBuilder.appendWhere(Colnums._ID + "=" + uri.getPathSegments().get(1));
			break;

		default:
			break;
		}

		SQLiteDatabase db = mSqlitOpenHelper.getReadableDatabase();
		Cursor result = sqLiteQueryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);

		if (result == null) {

		} else {
			result.setNotificationUri(getContext().getContentResolver(), uri);
		}
		return result;
	}

	@Override
	public String getType(Uri uri) {
		UriMatcher uriMatcher = getUriMatcher();
		String tablePath = getTablePath();

		int match = uriMatcher.match(uri);
		switch (match) {
		case ITEMS:
			return "vnd.android.cursor.dir/" + tablePath;
		case ITEMS_ID:
			return "vnd.android.cursor.item/" + tablePath;
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {

		System.out.println("insert uri--->" + uri);
		SQLiteDatabase db = mSqlitOpenHelper.getWritableDatabase();
		String tablePath = getTablePath();
		ContentValues contentValues = null;
		Uri contentUri = getUri();
		if (values != null) {
			contentValues = new ContentValues(values);
		} else {
			contentValues = new ContentValues();
		}

		long rowId = db.insert(tablePath, null, contentValues);
		Uri resultUri = ContentUris.withAppendedId(contentUri, rowId);

		//添加数据变化通知
		getContext().getContentResolver().notifyChange(uri, null);
		return resultUri;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {

		SQLiteDatabase db = mSqlitOpenHelper.getWritableDatabase();

		UriMatcher uriMatcher = getUriMatcher();
		String tablePath = getTablePath();
		int matcher = uriMatcher.match(uri);
		switch (matcher) {
		case ITEMS:

			return db.delete(tablePath, selection, selectionArgs);
		case ITEMS_ID:
			String rowId = uri.getPathSegments().get(1);
			String where = "";
			if (selection == null) {
				where = Columns._ID + "=" + rowId;
			} else {
				where = Columns._ID + "=" + rowId + "AND (" + selection + ")";
			}
			return db.delete(tablePath, where, selectionArgs);

		default:
			break;
		}

		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
		SQLiteDatabase db = mSqlitOpenHelper.getWritableDatabase();

		UriMatcher uriMatcher = getUriMatcher();
		String tablePath = getTablePath();

		ContentValues contentValues = null;
		if (values != null) {
			contentValues = new ContentValues(values);
		} else {
			contentValues = new ContentValues();
		}

		int matcher = uriMatcher.match(uri);
		switch (matcher) {
		case ITEMS:

			return db.update(tablePath, contentValues, selection, selectionArgs);
		case ITEMS_ID:
			String where = "";

			if (selection == null) {
				String rowId = uri.getPathSegments().get(1);
				where = Colnums._ID + "=" + rowId;
			} else {
				String rowId = uri.getPathSegments().get(1);
				where = Colnums._ID + "=" + rowId + "AND (" + selection + ")";
			}
			return db.update(tablePath, contentValues, where, selectionArgs);
		default:
			break;
		}
		return -1;
	}
}

MianActivtiy.java
package com.example.contentprovideranddb;

import com.example.contentprovideranddb.ContentProvider.PersonProvider;

import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;

public class MainActivity extends Activity {
	private int id = 0;

	private Handler mHandler = new Handler();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		this.getContentResolver().registerContentObserver(PersonProvider.uri, false, new MyObserver(mHandler));
	}
	
	public class MyObserver extends ContentObserver{

		public MyObserver(Handler handler) {
			super(handler);
			
		}
		
		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
			System.out.println("base changing ------>");
			
		}
		
	}

	public void insert(View v) {

		ContentValues values = new ContentValues();
		values.put("name", "zhangesan");
		values.put("age", 12);
		values.put("address", "北京市");

		Uri uri = this.getContentResolver().insert(PersonProvider.uri, values);
		System.out.println("uri----->" + uri);

	}

	public void delete(View v) {
		int rowId = this.getContentResolver().delete(ContentUris.withAppendedId(PersonProvider.uri, id), null, null);
		System.out.println("delete----->" + rowId);
	}

	public void update(View v) {
		ContentValues values = new ContentValues();
		values.put("name", "lisiguang");
		values.put("age", 12);
		values.put("address", "长春市");

		int rowId = this.getContentResolver().update(ContentUris.withAppendedId(PersonProvider.uri, id), values, null, null);
		System.out.println("update----->" + rowId);
	}

	public void query(View v) {

		Cursor cursor = this.getContentResolver().query(PersonProvider.uri, null, null, null, null);

		while (cursor.moveToNext()) {
			id = cursor.getInt(cursor.getColumnIndex("_id"));
			String name = cursor.getString(cursor.getColumnIndex("name"));
			int age = cursor.getInt(cursor.getColumnIndex("age"));
			String addr = cursor.getString(cursor.getColumnIndex("address"));

			System.out.println("id---->" + id + ";name ----->" + name + "; age ---->" + age + "; addr ---->" + addr);
		}
	}
}



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