1. 程式人生 > >Android四大元件之ContentProvider自定義

Android四大元件之ContentProvider自定義

       ContentProvider 屬於Android應用程式的元件之一,作為應用程式之間唯一的共享資料的途徑,ContentProvider 主要的功能就是儲存並檢索資料以及向其他應用程式提供訪問資料的介面。 

      讓自己的資料和其他應用程式共享有兩種方式:建立自己的ContentProvier(即繼承自ContentProvider的子類)  或者是將自己的資料新增到已有的ContentProvider中去,後者需要保證現有的ContentProvider和自己的資料型別相同且具有該 ContentProvider的寫入許可權。對於ContentProvider,最重要的就是資料模型(data model) 和 URI。

一、資料模型 
       ContentProvider 將其儲存的資料以資料表的形式提供給訪問者,在資料表中每一行為一條記錄,每一列為具有特定型別和意義的資料。每一條資料記錄都包括一個 "_ID" 數值欄位,改欄位唯一標識一條資料。 
二、URI——統一資源識別符號
    URI,每一個Content Provider 都對外提供一個能夠唯一標識自己資料集(data set)的公開URI, 如果一個Content Provider管理多個數據集,其將會為每個資料集分配一個獨立的URI。所有的Content Provider 的URI 都以"content://" 開頭,其中"content:"是用來標識資料是由Content Provider管理的 schema。 


         1                                           2                                                          3      4

1、ContentProvider的scheme已經由Android所規定, scheme為:content://  一般不改變

2、主機名(或叫Authority)用於唯一標識這個ContentProvider,外部呼叫者可以根據這個標識來找到它;一般我們都是用ContentProvider的繼承類完整類名。

3、路徑(path)可以用來表示我們要操作的資料,如果要操作整張表,後面就不需要了。

4、users中的子表

把一個字串轉化成URI

Uri uri = Uri.parse("content://com.example.contentproviderdemo.FirstContentProvider/users/id");

因為Uri代表了要操作的資料,所以我們經常需要解析Uri,並從Uri中獲取資料。Android系統提供了兩個用於操作Uri的工具類,分別為UriMatcher和ContentUris

UriMatcher的使用

public static final UriMatcher uriMatcher;
public static final int INCOMING_USER_COLLECTION = 1;
public static final int INCOMING_USER_SINGLE = 2;
static {
	//UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
	uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
	//匹配content://com.example.contentproviderdemo.FirstContentProvider/users路徑,返回匹配碼為INCOMING_USER_COLLECTION
	uriMatcher.addURI(ProviderMetaData.AUTHORIY, "users",
			INCOMING_USER_COLLECTION);
	//匹配content://com.example.contentproviderdemo.FirstContentProvider/users/#路徑,返回匹配碼為INCOMING_USER_SINGLE
	uriMatcher.addURI(ProviderMetaData.AUTHORIY, "users/#",   //#號為萬用字元
			INCOMING_USER_SINGLE);
}
//根據傳入的URI,返回該URI所表示的資料型別
@Override
public String getType(Uri uri) {
	// TODO Auto-generated method stub
	System.out.println("getType------------>" + uriMatcher.match(uri) + " uri = " + uri.toString());
	switch(uriMatcher.match(uri)){
	case INCOMING_USER_COLLECTION:
		System.out.println("getType-------------->INCOMING_USER_COLLECTION");
		return UserTableMetaData.CONTENT_TYPE;
	case INCOMING_USER_SINGLE:
		System.out.println("getType-------------->INCOMING_USER_SINGLE");
		return UserTableMetaData.CONTENT_TYPE_ITEM;
	default:
		System.out.println("getType-------------->default");
		throw new IllegalArgumentException("Unknown URI" + uri);
	}
}


ContentUris 的使用



ContentUris類用於操作Uri路徑後面的ID部分,它有兩個比較實用的方法:
withAppendedId(uri, id)用於為路徑加上ID部分:
Uri uri = Uri.parse("content://com.example.contentproviderdemo.FirstContentProvider/users")
Uri resultUri = ContentUris.withAppendedId(uri, 10); 
生成後的Uri為:content://com.example.contentproviderdemo.FirstContentProvider/users/10

parseId(uri)方法用於從路徑中獲取ID部分

Uri uri = Uri.parse("content://com.example.contentproviderdemo.FirstContentProvider/users/10")
long personid = ContentUris.parseId(uri);//獲取的結果為:10

下面建立一個類繼承ContentProvider重寫增、刪、改、查方法
package com.example.contentproviderdemo;

import java.util.HashMap;

import com.example.contentproviderdemo.ProviderMetaData.UserTableMetaData;
import com.sqlite3.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.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class FirstContentProvider extends ContentProvider {

	public static final UriMatcher uriMatcher;
	public static final int INCOMING_USER_COLLECTION = 1;
	public static final int INCOMING_USER_SINGLE = 2;
	private DatabaseHelper dh;
	static {
		//UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		//匹配content://com.example.contentproviderdemo.FirstContentProvider/users路徑,返回匹配碼為INCOMING_USER_COLLECTION
		uriMatcher.addURI(ProviderMetaData.AUTHORIY, "users",
				INCOMING_USER_COLLECTION);
		//匹配content://com.example.contentproviderdemo.FirstContentProvider/users/#路徑,返回匹配碼為INCOMING_USER_SINGLE
		uriMatcher.addURI(ProviderMetaData.AUTHORIY, "users/#",   //#號為萬用字元
				INCOMING_USER_SINGLE);
	}
	
	public static HashMap<String,String> userProjectionMap;
	static
	{
		userProjectionMap = new HashMap<String,String>();
		userProjectionMap.put(UserTableMetaData._ID, UserTableMetaData._ID);
		userProjectionMap.put(UserTableMetaData.USER_NAME, UserTableMetaData.USER_NAME);
	}
	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		// TODO Auto-generated method stub
		System.out.println("delete");
		return 0;
	}

	//根據傳入的URI,返回該URI所表示的資料型別
	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		System.out.println("getType------------>" + uriMatcher.match(uri) + " uri = " + uri.toString());
		switch(uriMatcher.match(uri)){
		case INCOMING_USER_COLLECTION:
			System.out.println("getType-------------->INCOMING_USER_COLLECTION");
			return UserTableMetaData.CONTENT_TYPE;
		case INCOMING_USER_SINGLE:
			System.out.println("getType-------------->INCOMING_USER_SINGLE");
			return UserTableMetaData.CONTENT_TYPE_ITEM;
		default:
			System.out.println("getType-------------->default");
			throw new IllegalArgumentException("Unknown URI" + uri);
		}
	}

	/**
	 * 該函式的返回值是一個Uri,這個Uri表示的是剛剛使用這個函式所插入的資料
	 * content://mars.cp.FirstContentProvider/users/1
	 */
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		System.out.println("insert");
		SQLiteDatabase db = dh.getWritableDatabase();
		long rowId = db.insert(UserTableMetaData.TABLE_NAME, null, values);
		if(rowId > 0){
			Uri insertedUserUri = ContentUris.withAppendedId(UserTableMetaData.CONTENT_URI, rowId);
			//通知監聽器,資料已經改變
			getContext().getContentResolver().notifyChange(insertedUserUri, null);
			return insertedUserUri;
		}
		throw new SQLException("Failed to insert row into" + uri);
	}

	//是一個回撥方法,所以說在ContentProvider建立的時候執行 
	@Override
	public boolean onCreate() {
		//開啟資料庫  
		dh = new DatabaseHelper(getContext(),ProviderMetaData.DATABASE_NAME);
		System.out.println("onCreate");
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
			String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		switch(uriMatcher.match(uri)){
		case INCOMING_USER_COLLECTION:
			qb.setTables(UserTableMetaData.TABLE_NAME);
			qb.setProjectionMap(userProjectionMap);
			System.out.println("----------------------INCOMING_USER_COLLECTION");
			break;
		case INCOMING_USER_SINGLE:
			qb.setTables(UserTableMetaData.TABLE_NAME);
			qb.setProjectionMap(userProjectionMap);
			qb.appendWhere(UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
			System.out.println("----------------------INCOMING_USER_SINGLE");
			break;
		default:
			break;
		}
		System.out.println("----------------------query");
		String orderBy;
		if(TextUtils.isEmpty(sortOrder)){
			orderBy = UserTableMetaData.DEFAULT_SORT_ORDER;
		}
		else{
			orderBy = sortOrder;
		}
		//SQLiteDatabase db = dh.getWritableDatabase();
		SQLiteDatabase db = dh.getReadableDatabase();
		System.out.println("query------------------2");
		Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
		System.out.println("query------------------3");
		c.setNotificationUri(getContext().getContentResolver(), uri);
		System.out.println("query");
		return c;
	}

	@Override
	public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
		// TODO Auto-generated method stub
		System.out.println("update");
		return 0;
	}

}
定義一個儲存常量的類 ProviderMetaData.java
package com.example.contentproviderdemo;

import android.net.Uri;
import android.provider.BaseColumns;

/**
 * 定義程式中用到的常量
 * @author Administrator
 *
 */
public class ProviderMetaData {
	public static final String AUTHORIY = "com.example.contentproviderdemo.FirstContentProvider";
	//資料庫名稱
	public static final String DATABASE_NAME = "FirstProvider1.db";
	//資料庫的版本
	public static final int DATABASE_VERSION = 2;
	//表名 
	public static final String USERS_TABLE_NAME = "users";
	
	public static final class UserTableMetaData implements BaseColumns{
		//表名
		public static final String TABLE_NAME = "users";
		//訪問該ContentProvider的URI
		public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORIY + "/users");
		//該ContentProvider所返回的資料型別的定義
		public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.firstprovider.user";
		public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.firstprovider.user";
		//列名  
		public static final String USER_NAME = "name";
		//預設的排序方法
		public static final String DEFAULT_SORT_ORDER = "_id desc";
	}
}
在Activity中定義兩個按鈕,進行插入和查詢操作
package com.example.contentproviderdemo;

import com.example.contentproviderdemo.ProviderMetaData.UserTableMetaData;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class CPActivity extends Activity {

	private Button insertButton = null;
	private Button queryButton = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_cp);
		queryButton = (Button) findViewById(R.id.query);
		insertButton = (Button) findViewById(R.id.insert);
		
		insertButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				ContentValues values = new ContentValues();
				values.put(ProviderMetaData.UserTableMetaData.USER_NAME,
						"zhangsan");
				Uri uri = getContentResolver().insert(ProviderMetaData.UserTableMetaData.CONTENT_URI,  values);
				System.out.println("uri--->" + uri.toString());
			}
		});

		queryButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				
				Cursor c = getContentResolver().query(
						ProviderMetaData.UserTableMetaData.CONTENT_URI, null,
						null, null, null);
				while(c.moveToNext()){
					System.out.println(c.getString(c.getColumnIndex(UserTableMetaData.USER_NAME)));
				}
				String str = getContentResolver().getType(ProviderMetaData.UserTableMetaData.CONTENT_URI);
				System.out.println("queryButton--->" + str);
			}
		});
	}

}

得到的結果是


樣例程式碼