android ContentProvider讲解

ContentProvider讲解

当应用A需要通过contentProvider共享数据时,应按如下顺序进行操作:

一.  定义一个MyProviderextends android.content.ContentProvider并重写里面的onCreate,getType,insert,delete,query,update六个方法。将定义好的MyProvider在manifest.xml中进行声明注册。

 

1. MyProvider中需要定义一个UriMatcher对象,并在其上注册Provider提供的Uri,返回对应的匹配码,若无匹配,则返回-1;具体如下:

public static UriMatcher uriMatcher;
static{
    uriMatcher= new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI(AUTHORITY,"user", MATCH_DIR);// 添加需要匹配uri,如果匹配就会返回匹配码,没有匹配的,返回-1
    uriMatcher.addURI(AUTHORITY,"user/#", MATCH_ITEM);// 如果match()方法匹配content://com.unj.myprovider/user/230路径,返回匹配码为2
}

 2.对于重写的增删查改四个方法,sql语句对应的相应方法的参数参照如下:

/**
* select id, name from user where age = ? order by name 对应下面的参数为 
* projection = new String{"id","name"}; 
* selection = "age = ?"; 
* selectionArgs = new String{"23"}; 
* sortOrder = "name";
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder)

3. 我们可以根据Uri来添加查询条件:

switch (uriMatcher.match(uri)) {
		case MATCH_DIR:
			break;
		case MATCH_ITEM:
			long id = ContentUris.parseId(uri);
			if (selection != null && !"".equals(selection)) {
				selection += " and ";
			}
			selection += " id = " + id;
			break;
		default:
			throw new IllegalArgumentException("Unkonw URI" + uri);
		}


4. 当增删查改操作使数据发生变化时,可调用如下方法通知调用此ContentProvider的ContentResolver,数据发生了变化。

getContext().getContentResolver().notifyChange(uri, null);//通知,调用此uri的resolver,数据已经发生变化,调用端的ContentObserver会回调onChange方法。

5.如下是我自定义的操纵数据库的一个Provider:MyDBUserProvider.java

package com.androidstudydemo.contentprovider;

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;

/**
 * 当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面六个方法
 * 第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置
 * 详细参见:http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html
 * 
 * @author zkx016
 * 
 */
public class MyDBUserProvider extends ContentProvider {
	public static final String AUTHORITY = "com.unj.myprovider";
	public static final String TABLE_NAME = "user";
	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
	+ "/user");

	public static final int MATCH_DIR = 1;
	public static final int MATCH_ITEM = 2;
	MyDBHelper myDBHelper;
	/**
	 * 该ContentProvider所返回的数据类型定义
	 */
	public static final String CONTENT_TYPE_DIR = "vnd.android.cursor.dir/user";// 返回集合类型的数据
	public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/user";// 返回非集合类型的数据

	/**
	 * UriMatcher类用于匹配Uri,首先第一步把你需要匹配Uri路径全部给注册上,
	 */
	public static UriMatcher uriMatcher;
	static {
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcher.addURI(AUTHORITY, "user", MATCH_DIR);// 添加需要匹配uri,如果匹配就会返回匹配码
		uriMatcher.addURI(AUTHORITY, "user/#", MATCH_ITEM);// 如果match()方法匹配content://com.unj.myprovider/person/230路径,返回匹配码为2
	}

	/**
	 * 该方法在ContentProvider创建后就会被调用,Android开机后,ContentProvider在其它应用第一次访问它时才会被创建。
	 */
	@Override
	public boolean onCreate() {
		myDBHelper = new MyDBHelper(getContext());// 这里的实现,常见前篇关于Sqlite的文章。
		return true;
	}

	/**
	 * 该方法用于返回当前Url所代表数据的MIME类型。
	 * 如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
	 * 例如:要得到所有person记录的Uri为content://com.ljq.provider.personprovider/person,
	 * 如果操作的数据不属于集合类型那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。
	 * 如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
	 */
	@Override
	public String getType(Uri uri) {
		switch (uriMatcher.match(uri)) {// 进行匹配
		case MATCH_DIR:
			return CONTENT_TYPE_DIR;
		case MATCH_ITEM:
			return CONTENT_TYPE_ITEM;
		default:
			throw new IllegalArgumentException("Unknown URI" + uri);
		}

	}

	/**
	 * 该方法用于供外部应用往ContentProvider添加数据。
	 */
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		int type = uriMatcher.match(uri);
		if (type != MATCH_DIR) {
			throw new IllegalArgumentException("Unknown URI:" + uri);
		}

		SQLiteDatabase db = myDBHelper.getWritableDatabase();
		long rowId = db.insert(TABLE_NAME, null, values);// 返回的是受影响的那一行的行号,出错时返回-1;
		if (rowId > 0) {
			/**
			 * withAppendedId(uri, id)用于为路径加上ID部分,
			 * 生成后的Uri为:content://com.unj.myprovider/person/10
			 */
			Uri noteUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
			/**
			 * 如果ContentProvider的访问者需要知道ContentProvider中的数据发生变化,
			 * 可以在ContentProvider发生数据变化时调用getContentResolver().notifyChange(uri,
			 * null)来通知注册在此URI上的访问者,
			 */
			getContext().getContentResolver().notifyChange(uri, null);
			db.close();
			return noteUri;
		} else {
			db.close();
			throw new IllegalArgumentException("Failed to insert row into"
					+ uri);
		}
	}

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

		SQLiteDatabase db = myDBHelper.getWritableDatabase();

		switch (uriMatcher.match(uri)) {
		case MATCH_DIR:
			break;
		case MATCH_ITEM:
			long id = ContentUris.parseId(uri);
			if (selection != null && !"".equals(selection)) {
				selection += " and ";
			}
			selection += " id = " + id;
			break;
		default:
			throw new IllegalArgumentException("Unkonw URI" + uri);
		}

		int rows = db.delete(TABLE_NAME, selection, selectionArgs);
		if (rows > 0) {
			getContext().getContentResolver().notifyChange(uri, null);
			db.close();
			return rows;
		} else {
			db.close();
			throw new IllegalArgumentException("Failed to delete row in" + uri);
		}

	}

	/**
	 * select id, name from user where age = ? order by name 对应下面的参数为 projection
	 * = new String{"id","name"}; selection = "age = ?"; selectionArgs = new
	 * String{"23"}; sortOrder = "name";
	 */
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		switch (uriMatcher.match(uri)) {
		case MATCH_DIR:
			// tableName = uri.getLastPathSegment();
			break;
		case MATCH_ITEM:
			long id = ContentUris.parseId(uri);
			if (selection != null && !"".equals(selection)) {
				selection += " and ";
			}
			selection += "id = " + id;
			break;

		default:
			throw new IllegalArgumentException("Unkonw URI" + uri);
		}

		SQLiteDatabase db = myDBHelper.getReadableDatabase();
		Cursor cursor = db.query(TABLE_NAME, projection, selection,
				selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), uri);// @1
		return cursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		switch (uriMatcher.match(uri)) {
		case MATCH_DIR:
			break;
		case MATCH_ITEM:
			long id = ContentUris.parseId(uri);
			if (selection != null && !"".equals(selection)) {
				selection += " and ";
			}
			selection += "id = " + id;
			break;

		default:
			throw new IllegalArgumentException("Unkonw URI" + uri);
		}
		SQLiteDatabase db = myDBHelper.getWritableDatabase();
		int rowsAffected = db.update(TABLE_NAME, values, selection,
				selectionArgs);//返回受影响的行数
		db.close();
		if (rowsAffected > 0) {
			getContext().getContentResolver().notifyChange(CONTENT_URI, null);// @2
			return rowsAffected;
		}
		

		return 0;
	}

}


6.MyProvider定义完成之后,需要在manifest.xml中进行Provider的注册,具体在<application></application>内部:

<!-- 自定义contentProvider-->
       <provider
           android:name="com.androidstudydemo.contentprovider.MyDBUserProvider"
           android:authorities="com.unj.myprovider" >
       </provider>


 

二. 在其他应用(B)中可以对应的通过Context获得ContentResolver,ContentResolver里面有和ContentProvider相对应的增删查改四个方法,在B应用中直接调用contentResolver.insert()等方法就会获得A应用中提供的数据,B在调用时必须保证A应用同时在运行。


1. 新建一个android project,声明自己需要数据的Uri和authority:

public static final String AUTHORITY = "com.unj.myprovider";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
			+ "/user");

2.定义一个ContentObserver,重写里面的onChange(boolean selfChange, Uri uri)方法,它的作用是当Provider里面对应的Uri数据发生变化,此方法将会被回调。

private ContentObserver contentObserver = new ContentObserver(handler) {
		public void onChange(boolean selfChange, Uri uri) {
			Toast.makeText(MainActivity.this, "数据发生改变, uri is:" + uri,
					Toast.LENGTH_SHORT).show();
			queryClick();
		};
	};

3,在应用中通过getContext().getContentResolver() 获得ContentResolver,并注册ContentObserver

<span style="white-space:pre">		</span>resolver = getContentResolver();
		/**
		 * 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听
		 * ,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法
		 */
		resolver.registerContentObserver(CONTENT_URI, false, contentObserver);
4. 如下是我自定义的Bproject,用来测试上方的Provider:

package com.example.providertest;

import android.app.Activity;
import android.content.ContentResolver;
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;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
	public static final String AUTHORITY = "com.unj.myprovider";
	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
			+ "/user");

	private ContentResolver resolver;
	private TextView textView;
	private Button query;
	private int id = 0;
	Handler handler = new Handler();
	private ContentObserver contentObserver = new ContentObserver(handler) {
		public void onChange(boolean selfChange, Uri uri) {
			Toast.makeText(MainActivity.this, "数据发生改变, uri is:" + uri,
					Toast.LENGTH_SHORT).show();
			queryClick();
		};
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		query = (Button) this.findViewById(R.id.button2);
		resolver = getContentResolver();
		/**
		 * 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听
		 * ,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法
		 */
		resolver.registerContentObserver(CONTENT_URI, false, contentObserver);
		textView = (TextView) findViewById(R.id.textView1);
	}

	public Uri insertClick() {
		ContentValues values = new ContentValues();
		values.put("id", id);
		values.put("name", "喻文杰" + id);
		Uri uri = resolver.insert(CONTENT_URI, values);
		id++;
		return uri;
	}

	public void deleteClick() {
		Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null);
		int count = cursor.getColumnCount();
		if (count > 0 && cursor.moveToLast()) {
			int id = resolver.delete(CONTENT_URI, "id = ?",
					new String[] { cursor.getInt(cursor.getColumnIndex("id"))
							+ "" });
		} else {
			Toast.makeText(MainActivity.this, "删除失败",
					Toast.LENGTH_SHORT).show();
		}

	}

	public void updateClick() {
		Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null);
		if (cursor.moveToFirst()) {
			ContentValues values = new ContentValues();
			int id = cursor.getInt(cursor.getColumnIndex("id"));
			values.put("name", "胡锦涛" + id);
			Uri uri = ContentUris.withAppendedId(CONTENT_URI, id);
			int result = resolver.update(uri, values, "", null);
		}
		if (!cursor.isClosed()) {
			cursor.close();
		}
	}

	public void queryClick() {
		textView.setText("");
		Cursor cursor = resolver.query(CONTENT_URI, null, null, null, null);
		while (cursor.moveToNext()) {
			textView.append("id : "
					+ cursor.getInt(cursor.getColumnIndex("id")) + "     ");
			textView.append("name : "
					+ cursor.getString(cursor.getColumnIndex("name")) + "\n");
		}
		if (!cursor.isClosed()) {
			cursor.close();
		}
		textView.append("getAuthority:" + CONTENT_URI.getAuthority()
				+ " \n********\n getEncodedAuthority:"
				+ CONTENT_URI.getEncodedAuthority()
				+ " \n********\n getEncodedFragment:"
				+ CONTENT_URI.getEncodedFragment()
				+ " \n********\ngetEncodedPath: "
				+ CONTENT_URI.getEncodedPath() + " \n********\ngetHost: "
				+ CONTENT_URI.getHost() + " \n********\ngetLastPathSegment: "
				+ CONTENT_URI.getLastPathSegment() + " \n********\ngetPath: "
				+ CONTENT_URI.getPath() + " \n********\ngetPort: "
				+ CONTENT_URI.getPort() + " \n********\ngetQuery: "
				+ CONTENT_URI.getQuery() + " \n********\ngetScheme: "
				+ CONTENT_URI.getScheme() + " \n********\ngetUserInfo: "
				+ CONTENT_URI.getUserInfo() + "\n********\n");
	}

	@Override
	public void onClick(View arg0) {
		switch (arg0.getId()) {
		case R.id.button1:// 增
			insertClick();
			break;
		case R.id.button3:// 删
			deleteClick();
			break;
		case R.id.button2:// 查
			queryClick();
			break;
		case R.id.button4:// 改
			updateClick();
			break;

		default:
			break;
		}

	}
}




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