Android中的跨進程通信方法實例及特點分析(二):ContentProvider

1.ContentProvider簡介

在Android中有些數據(如通訊錄、音頻、視頻文件等)是要供很多應用程序使用的,為瞭更好地對外提供數據,Android系統給我們提供瞭Content Provider使用,通過它可以訪問上面所說的數據,例如很多音樂播放器中的掃描功能其實就用到瞭Content Provider功能(當然,也有的播放器是自己去實現更底層的功能)。這樣的好處是統一管理,比如增加瞭某個音頻文件,底層就會將這種變化通知Content Provider,從而當應用程序訪問時就可以獲得當前最新的數據。

當然,Android也允許我們定義自己的Content Provider,隻要繼承它的基類,並且實現下面的方法即可。

public boolean onCreate()
在創建ContentProvider時調用
public Cursor query(Uri, String[],
String, String[], String):用於查詢指定Uri的ContentProvider,返回一個Cursor
public Uri insert(Uri, ContentValues):根據指定的Uri添加數據到ContentProvider中
public int update(Uri, ContentValues,
String, String[]):用於更新指定Uri的ContentProvider中的數據
public int delete(Uri, String,
String[]):根據Uri刪除指定的數據
public String getType(Uri):用於返回指定的Uri中的數據的MIME類型
*如果操作的數據屬於集合類型,那麼MIME類型字符串應該以vnd.android.cursor.dir/開頭。
例如:要得到所有p1記錄的Uri為content://contacts/p1,那麼返回的MIME類型字符串為”vnd.android.cursor.dir/p1″。
*如果要操作的數據屬於非集合類型數據,那麼MIME類型字符串應該以vnd.android.cursor.item/開頭。
例如:要得到id為100的student記錄的Uri為content://contacts/student/100,那麼返回的MIME類型字符串應為”vnd.android.cursor.item/student”。

2.Uri簡介

一個標準的Uri為content://authority/path可分為以下三部分:

(1)content://:這個部分是ContentProvider規定的,就像https://代表Http這個協議一樣,使用ContentProvider的協議是content://

(2)authorities:它在所在的Android系統必須是唯一的,因為系統就是通過它來決定操作或訪問哪個ContentProvider的,這與互聯網上的網址必須唯一是一樣的道理。

(3)path:資源路徑。

顯然,從上面的分析可以看出ContentProvider雖然也可實現跨進程通信,但是它適用的場景主要是與數據庫相關,有時也可能是文本文件或XML等存儲方式。

3.ContentResolver

如果隻是定義一個ContentProvider的話,沒有任何意義,因為ContentProvider隻是內容提供者,它要被別的應用(進程)讀取才有價值。與實現ContentProvder的方法相對應,使用ContentResolver相關的方法如下所示:

getContentResolver():Context類提供的,用於獲取ContentResolver對象;

insert(Uri uri,ContentValues values):向Uri對應的ContentProvider中插入values對應的數據;

update(Uri uri,ContentValues values,String where,String[]selectionArgs):更新Uri對應的ContentProvider中where處的數據,其中selectionArgs是篩選參數;

query(Uri uri,String[]projection,String selection,String[]selectionArgs,String sortOrder):查詢Uri對應的ContentProvider中where處的數據,其中selectionArgs是篩選參數,sortOrder是排序方式;

delete(Uri uri,String where,String[]selectionArgs):刪除Uri對應的ContentProvider中where處的數據,其中selectionArgs是篩選參數;

4.UriMatcher

為瞭確定一個ContentProvider實際能處理的Uri,以及確定每個方法中Uri參數所操作的數據,Android系統提供瞭UriMatcher工具類。它主要有如下兩個方法:

(1)void addURI(String authority,String path,String code):該方法用於向UriMatcher對象註冊Uri。其中authority和path組合成一個Uri,而code則代表該Uri對應的標識碼;

(2)int match(Uri uri):根據前面註冊的Uri來判斷指定Uri對應的標識碼。如果找不到匹配的標識碼,該方法將會返回-1。

下面通過兩個實例來講解ContentProvider的用法,第一個實例是自己定義瞭一個ContentProvider並且在另一個應用中讀取它;第二個實例是讀取當前手機中的聯系人。

首先是第一個例子,項目結構如下圖所示:

下面是各個類的代碼,首先是常量的定義:

package com.android.student.utils;

import android.net.Uri;

/**
 * 這裡定義瞭與ContentProvider相關的字符串以及Student對應的數據表中的字段
 * @author Bettar
 *
 */
public class StudentWords {

	//註意Manifest文件中的authorities屬性要跟這裡保持一致。
	public static final String AUTHORITY="com.android.student.provider";
	
	public static final Uri STUDENT_WITH_ID_URI=Uri.parse("content://"+AUTHORITY+"/student");
	public static final Uri STUDENT_URI=Uri.parse("content://"+AUTHORITY+"/student");
	
	
	public static final String TABLE_NAME="student";
	public static final String ID="id";
	public static final String NAME="name";
	public static final String SCORE="score";
	public static final String ADDR="address";

}

然後是數據庫幫助類:

import com.android.student.utils.StudentWords;

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

public class StudentDbHelper extends SQLiteOpenHelper{

	private Context context;
	public StudentDbHelper(Context context,String name,int version)
	{
		super(context,name,null,version);
		this.context=context;
	}
	
	@Override
	public void onCreate(SQLiteDatabase db)
	{
		String createSQL="create table "+StudentWords.TABLE_NAME+"("+StudentWords.ID
				+" integer primary key autoincrement,"
				+StudentWords.NAME+" varchar,"
				+StudentWords.SCORE+" integer,"
				+StudentWords.ADDR+" varchar)";
		db.execSQL(createSQL);
	}
	@Override
	public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
	{
		//升級版本時,可能要執行表結構的修改之類,此處暫時不考慮升級問題,因而隻是用Toast提示
		Toast.makeText(context, 
				"newVersion:"+newVersion+" will replace oldVersion:"+oldVersion,
				Toast.LENGTH_LONG).show();	
	}
	
}

最後是ContentProvider類的定義:

package com.android.student.provider;

import com.android.student.database.StudentDbHelper;

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;
import android.provider.UserDictionary.Words;

import com.android.student.utils.StudentWords;

public class StudentProvider extends ContentProvider{

	private static final String TAG="StudentProvider";
	private static UriMatcher matcher=new UriMatcher(UriMatcher.NO_MATCH);
	private static final int STUDENT_WITH_ID=1;
    private static final int STUDENT=2;
    
    private StudentDbHelper dbHelper;
    
    static
    {
    	matcher.addURI(StudentWords.AUTHORITY,"student/#",STUDENT_WITH_ID);
    	//matcher.addURI(StudentWords.AUTHORITY, "student", SINGLE_STUDENT);
    	//註意其中的#為通配符
    	matcher.addURI(StudentWords.AUTHORITY, "student", STUDENT);
    }
    
    @Override
    public boolean onCreate()
    {
    	dbHelper=new StudentDbHelper(this.getContext(),"student.db3",1);
    	return true;
    }
    
    @Override
    public String getType(Uri uri)
    {
    	switch(matcher.match(uri))
    	{
    	case STUDENT_WITH_ID:
    		return "vnd.android.cursor.item/com.android.student";	
    	case STUDENT:
    		return "vnd.android.cursor.dir/com.android.student";
    		default:
    			throw new IllegalArgumentException("Unknown Uri:"+uri);
    	}
    }
    
    /**
     * 由單一的selection這一個篩選條件組合成包含id的復雜篩選條件
     * @param uri
     * @param selection
     * @return
     */
    private String getComplexSelection(Uri uri,String selection)
    {
    	long id=ContentUris.parseId(uri);
    	String complexSelection=StudentWords.ID+"="+id;
    	if(selection!=null&&!"".equals(selection))
    	{
    		complexSelection+=" and "+selection;
    	}
    	return complexSelection;
    }
    
	@Override
	public int delete(Uri uri,String selection,String[]selectionArgs) {
		
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		int num=0;
		switch(matcher.match(uri))
		{
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			num=db.delete(StudentWords.TABLE_NAME, complexSelection, selectionArgs);
			break;
		case STUDENT:
			num=db.delete(StudentWords.TABLE_NAME,selection,selectionArgs);
			break;
		default:
			throw new IllegalArgumentException("Unknown Uri:"+uri);
		}
		//通知數據已經改變
		getContext().getContentResolver().notifyChange(uri, null);
		return num;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		switch(matcher.match(uri))
		{
		     
			case STUDENT_WITH_ID:
			case STUDENT:
				long rowId=db.insert(StudentWords.TABLE_NAME, StudentWords.ID,values);
				if(rowId>0)
				{
					Uri studentUri=ContentUris.withAppendedId(uri, rowId);
					//如果設置瞭觀察者的話,要通知所有觀察者
					getContext().getContentResolver().notifyChange(studentUri, null);
					return studentUri;
				}
				break;
			default:
				throw new IllegalArgumentException("Unknow Uri:"+uri);			
		}
	    return null;
	}

	/**
	 * 註意其中的projection其實是columns,即列名數組
	 */
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		switch(matcher.match(uri))
		{
		//這其實是包含id信息的情況
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			return db.query(StudentWords.TABLE_NAME,projection,complexSelection,selectionArgs,null,null,sortOrder);
		//這是不帶數字的情況,但是也未必就是好多個,一個也可以。
		case STUDENT:	
			return db.query(StudentWords.TABLE_NAME
					,projection,selection,selectionArgs,null,null,sortOrder);
		default:
				throw new IllegalArgumentException("Unknow Uri"+uri);
		}
	
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) 
	{
		SQLiteDatabase db=dbHelper.getWritableDatabase();
		int num=0;
		switch(matcher.match(uri))
		{
		case STUDENT_WITH_ID:
			String complexSelection=getComplexSelection(uri,selection);
			num=db.update(StudentWords.TABLE_NAME, values, complexSelection, selectionArgs);	
			break;
		case STUDENT:
			num=db.update(StudentWords.TABLE_NAME, values, selection,selectionArgs);
			break;
			default:
				throw new IllegalArgumentException("Unknow Uri:"+uri);
		}
		
		getContext().getContentResolver().notifyChange(uri,null);
		return num;	
	}
	
	
}

當然,要記得在Manifest文件中加入ContentProvider的聲明:

 

下面是對ContentResolver的例子,首先項目中的文件結構如下:

然後是layout文件 :

vcD4KPHByZSBjbGFzcz0=”brush:java;”>

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。