android ContentProvider

ContentProvider簡介:

ContentProvider(數據提供者)是在應用程序間共享數據的一種接口機制

ContentProvider提供瞭更為高級的數據共享方法,應用程序可以指定需要共享的數據,而其他應用程序則可以在不知數據來源、路徑的情況下,對共享數據進行查詢、添加、刪除和更新等操作

許多Android系統的內置數據也通過ContentProvider提供給用戶使用,例如通訊錄、音視頻文件和圖像文件等
在創建ContentProvider時,需要首先使用數據庫、文件系統或網絡實現底層存儲功能,然後在繼承ContentProvider的類中實現基本數據操作的接口函數,包括添加、刪除、查找和更新等功能

調用者不能夠直接調用ContentProvider的接口函數,而需要使用ContentResolver對象,通過URI間接調用ContentProvider。

使用ContentProvider可以在不同的應用程序之間共享數據。 它為存儲和獲取數據提供瞭統一的接口。

ContentProvide對數據進行封裝,不用關心數據存儲的細節。

Android系統提供瞭大量的ContentProvider,允許開發者來操作這些contentprovider所暴露的接口。

系統包:https://developer.android.com/intl/zh-cn/reference/android/provider/package-summary.html

聯系人管理的Uri:vc3Ryb25nPjwvcD4KPHA+Q29udGFjdHNDb250cmFjdC5Db250YWN0cy5DT05URU5UX1VSSTq53MDtwarPtcjLPGJyPgpDb250YWN0c0NvbnRyYWN0LkNvbW1vbkRhdGFLaW5kcy5QaG9uZS5DT05URU5UX1VSSTq53MDtwarPtcjLtcS157uwPGJyPgpDb250YWN0c0NvbnRyYWN0LkNvbW1vbkRhdGFLaW5kcy5FbWFpbC5DT05URU5UX1VSSTq53MDtwarPtcjLtcRFLW1haWwuPC9wPgo8cD48c3Ryb25nPrbgw73M5cTayN253MDttcRVcmk6PC9zdHJvbmc+PC9wPgo8cD5NZWRpYVN0b3JlLkF1ZGlvLk1lZGlhLkVYVEVSTkFMX0NPTlRFTlRfVVJJOs3isr+05rSixvfJz7XE0vTGtc7EvP48YnI+Ck1lZGlhU3RvcmUuQXVkaW8uTWVkaWEuSU5URVJOQUxfQ09OVEVOVF9VUkk6xNqyv7TmtKLG98nPtcTS9Ma1zsS8/jxicj4KTWVkaWFTdG9yZS5JbWFnZXMuTWVkaWEuRVhURVJOQUxfQ09OVEVOVF9VUkk6zeKyv7TmtKLG98nPtcTNvMaszsS8/jxicj4KTWVkaWFTdG9yZS5JbWFnZXMuTWVkaWEuSU5URVJOQUxfQ09OVEVOVF9VUkk6xNqyv7TmtKLG98nPtcTNvMaszsS8/jxicj4KTWVkaWFTdG9yZS5WaWRlby5NZWRpYS5FWFRFUk5BTF9DT05URU5UX1VSSTrN4rK/tOa0osb3yc+1xMrTxrXOxLz+PGJyPgpNZWRpYVN0b3JlLlZpZGVvLk1lZGlhLklOVEVSTkFMX0NPTlRFTlRfVVJJOsTasr+05rSixvfJz7XEytPGtc7EvP48YnI+CjwvcD4KPHA+PGJyPgo8L3A+CjxwPjxzdHJvbmc+VVJJtcS88r3po7o8L3N0cm9uZz48YnI+CjwvcD4KPHA+VXJptPqx7cHL0qqy2df3tcTK/b7do6zL/M6qz7XNs7XEw7/Su7j218rUtLj4xuTSu7j2w/vX1qOssci3vcu1zai7sLzHwryho8O/0ru49kNvbnRlbnRQcm92aWRlcra807XT0NK7uPa5q7mytcRVUkmjrNXiuPZVUknTw9Pase3KvtXiuPZDb250ZW50UHJvdmlkZXLL+czhuam1xMr9vt2hozxicj4KPC9wPgo8cD48YnI+CjwvcD4KPHA+PHN0cm9uZz5VUkm1xCYjMjY2ODQ7yr0gOjwvc3Ryb25nPjwvcD4KPHA+PHN0cm9uZz48aW1nIHNyYz0=”/uploadfile/Collfiles/20140404/2014040408455539.jpg” alt=”\”>

A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的;”content://”

B:URI 的標識,它定義瞭是哪個Content Provider提供這些數據。對於第三方應用程序,為瞭保證URI標識的唯一性,它必須是一個完整的、小寫的類名。這個標識在元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱; “content://hx.android.text.myprovider”,這個部分就是ContentProvider的authority.

C:路徑,通俗的講就是你要操作的數據庫中表的名字;”content://hx.android.text.myprovider/tablename”

D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部; “content://hx.android.text.myprovider/tablename/#” #表示數據id,動態改變

註意:

路徑(path):可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下: ? 要操作contact表中id為10的記錄,可以構建這樣的路徑:/contact/10 ? 要操作contact表中id為10的記錄的name字段, contact/10/name ? 要操作contact表中的所有記錄,可以構建這樣的路徑:/contact

要操作的數據不一定來自數據庫,也可以是文件等他存儲方式,如下: 要操作xml文件中contact節點下的name節點,可以構建這樣的路徑:/contact/name

如果要把一個字符串轉換成Uri,可以使用Uri類中的parse()方法,如下: Uri uri = Uri.parse(“content://com.changcheng.provider.contactprovider/contact”)


工具類:UriMatcher :

為瞭確定該ContentProvider實際能處理的Uri,以及確定每個方法中Uri參數所操作的數據,Android提供瞭UriMacher工具類,用法如下:

1.首先把你需要匹配Uri路徑全部給註冊上(便於ContentProvider判斷具體的操作,一般是確定對單個記錄操作還是多個記錄操作),如:

uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);

void addUri(String authority,String path,int code):該方法用於向UriMacher對象註冊Uri.其中authority和path組合成一個Uri,而code則代表對應的標識碼。標識碼匹配Uri的類型,需要註冊多少個Uri則根據業務需要。

2.註冊完需要匹配的Uri後,就可以使用uriMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼.

//如果match()匹配路徑,返回匹配碼為1
content://com.changcheng.sqlite.provider.contactprovider/contact
//如果match()匹配路徑,返回匹配碼為2
content://com.changcheng.sqlite.provider.contactprovider/contact/23

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

用來解析來自ContentResolver傳遞過來的Uri則能知道這個Uri具體操作的類型,由uriMatcher.addURI指定,則根據返回的標識碼就知道瞭要操作的類型(一般是確定對單個記錄操作還是多個記錄操作)

工具類:ContentUris :

ContentUris類用於操作Uri字符串的工具類,它有兩個比較實用的方法:

withAppendedId(uri, id)用於為路徑加上ID部分:

Uri uri =Uri.parse(“content://cn.itcast.provider.personprovider/person”)
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成後的Uri為:content://cn.itcast.provider.personprovider/person/10

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

Uri uri = Uri.parse(“content://cn.itcast.provider.personprovider/person/10”)
long personid = ContentUris.parseId(uri);//獲取的結果為:10

ContentProvider的編程方法 :

程序開發人員通過繼承ContentProvider類可以創建一個新的數據提供者,過程可以分為三步 :

繼承ContentProvider,並重載六個函數

delete():刪除數據集,返回被刪除的記錄條數
insert():添加數據集,返回新插入的記錄的Uri
qurey():查詢數據集,返回查詢得到的Cursor
update():更新數據集,返回被更新的記錄條數
onCreate():初始化底層數據集和建立數據連接等工作
getType():返回指定URI的MIME數據類型,
如果URI是單條數據,則返回的MIME數據類型應以vnd.android.cursor.item開頭
如果URI是多條數據,則返回的MIME數據類型應以vnd.android.cursor.dir/開頭

聲明CONTENT_URI,實現UriMatcher

註冊ContentProvider

	

屬性:

 name:ContentProvider的實現類的類名

authorities:指定該ContentProvider對應的Uri(域名)

android:exported:指定該ContentProvider是否允許其他應用調用


ContentResolver的編程方法:

使用ContentProvider是通過Android組件都具有的ContentResolver對象,通過URI進行數據操作

程序開發人員隻需要知道URI和數據集的數據格式,則可以進行數據操作,解決不同應用程序之間的數據共享問題

每個Android組件都具有一個ContentResolver對象,獲取ContentResolver對象的方法是調用getContentResolver()函數

ContentResolver resolver = getContentResolver();

調用ContentResolver的如下方法:

insert(Uri url,ContentValues values):

delete(Uri url,String where String[] selectionArgs):

update(Uri uri,ContentValues values,String where,String[] selectionArgs):

query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder):

實例代碼如下:

ContentProviderDemo:

工具類:(用來暴露給使用者,用作說明文檔)

package edu.hrbeu.ContentProviderDemo;

import android.net.Uri;

public class People{
	
	public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
	public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
	public static final String MINE_ITEM = "vnd.hrbeu.people";
	
	public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM;
	public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM;
	
	public static final String AUTHORITY = "edu.hrbeu.peopleprovider";
	public static final String PATH_SINGLE = "people/#";
	public static final String PATH_MULTIPLE = "people";
	public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
	public static final Uri  CONTENT_URI = Uri.parse(CONTENT_URI_STRING); 
	
	public static final String KEY_ID = "_id";
	public static final String KEY_NAME = "name";
	public static final String KEY_AGE = "age";
	public static final String KEY_HEIGHT = "height";
}

ContentProvider:

package edu.hrbeu.ContentProviderDemo;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.net.Uri;

public class PeopleProvider extends ContentProvider{
	
	private static final String DB_NAME = "people.db";
	private static final String DB_TABLE = "peopleinfo";
	private static final int DB_VERSION = 1;
	
	private SQLiteDatabase db;
	private DBOpenHelper dbOpenHelper;
	
	private static final int MULTIPLE_PEOPLE = 1;
	private static final int SINGLE_PEOPLE = 2;
	private static final UriMatcher uriMatcher;
	
	static {
		 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		 uriMatcher.addURI(People.AUTHORITY, People.PATH_MULTIPLE, MULTIPLE_PEOPLE);
		 uriMatcher.addURI(People.AUTHORITY, People.PATH_SINGLE, SINGLE_PEOPLE);
	}
	
	@Override
	public String getType(Uri uri) {
		switch(uriMatcher.match(uri)){
			case MULTIPLE_PEOPLE:
				return People.MINE_TYPE_MULTIPLE;
			case SINGLE_PEOPLE:
				return People.MINE_TYPE_SINGLE;
			default:
				throw new IllegalArgumentException("Unkown uri:"+uri);
		}
	}
	
	@Override
	public boolean onCreate() {
		Context context = getContext();
		dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
		db = dbOpenHelper.getWritableDatabase();

		if (db == null)
			return false;
		else 
			return true;	
	}

	
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		long id = db.insert(DB_TABLE, null, values);
		if ( id > 0 ){
			Uri newUri = ContentUris.withAppendedId(People.CONTENT_URI, id);
			getContext().getContentResolver().notifyChange(newUri, null);
			return newUri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int count = 0;
		switch(uriMatcher.match(uri)){
			case MULTIPLE_PEOPLE:
				count = db.delete(DB_TABLE, selection, selectionArgs);
				break;
			case SINGLE_PEOPLE:
				String segment = uri.getPathSegments().get(1);
				count = db.delete(DB_TABLE, People.KEY_ID + "=" + segment, selectionArgs);
				break;
			default:
				throw new IllegalArgumentException("Unsupported URI:" + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
	
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		qb.setTables(DB_TABLE);
		switch(uriMatcher.match(uri)){
			case SINGLE_PEOPLE:
				qb.appendWhere(People.KEY_ID + "=" + uri.getPathSegments().get(1));
				break;
			default:
				break;
		}
		Cursor cursor = qb.query(db, 
				projection, 
				selection, 
				selectionArgs, 
				null, 
				null, 
				sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int count;
		switch(uriMatcher.match(uri)){
			case MULTIPLE_PEOPLE:
				count = db.update(DB_TABLE, values, selection, selectionArgs);
				break;
			case SINGLE_PEOPLE:
				String segment = uri.getPathSegments().get(1);
				count = db.update(DB_TABLE, values, People.KEY_ID+"="+segment, selectionArgs);
				break;
			default:
				throw new IllegalArgumentException("Unknow URI:" + uri);	
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}
	
	
	  private static class DBOpenHelper extends SQLiteOpenHelper {

		  public DBOpenHelper(Context context, String name, CursorFactory factory, int version) {
		    super(context, name, factory, version);
		  }

		  private static final String DB_CREATE = "create table " + 
		    DB_TABLE + " (" + People.KEY_ID + " integer primary key autoincrement, " +
		    People.KEY_NAME+ " text not null, " + People.KEY_AGE+ " integer," + People.KEY_HEIGHT + " float);";

		  @Override
		  public void onCreate(SQLiteDatabase _db) {
		    _db.execSQL(DB_CREATE);
		  }

		  @Override
		  public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) {		    
		    _db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
		    onCreate(_db);
		  }
		}
	  
	 
}

AndroidManifest.xml:

 
        
    

ContentResolverDemo:

工具類:

package edu.hrbeu.ContentResolverDemo;

import android.net.Uri;

public class People{
	
	public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
	public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
	public static final String MINE_ITEM = "vnd.hrbeu.people";
	
	public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM;
	public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM;
	
	public static final String AUTHORITY = "edu.hrbeu.peopleprovider";
	public static final String PATH_SINGLE = "people/#";
	public static final String PATH_MULTIPLE = "people";
	public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
	public static final Uri  CONTENT_URI = Uri.parse(CONTENT_URI_STRING); 
	
	public static final String KEY_ID = "_id";
	public static final String KEY_NAME = "name";
	public static final String KEY_AGE = "age";
	public static final String KEY_HEIGHT = "height";
}

ContentResolver:

package edu.hrbeu.ContentResolverDemo;


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

public class ContentResolverDemo extends Activity {
	
	private EditText nameText;
	private EditText ageText;
	private EditText heightText;
	private EditText idEntry;
	
	private TextView labelView;
	private TextView displayView;
	
	private ContentResolver resolver;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        nameText = (EditText)findViewById(R.id.name);
        ageText = (EditText)findViewById(R.id.age);
        heightText = (EditText)findViewById(R.id.height);
        idEntry = (EditText)findViewById(R.id.id_entry);
        
        labelView = (TextView)findViewById(R.id.label);
        displayView = (TextView)findViewById(R.id.display);
        
        
        
        Button addButton = (Button)findViewById(R.id.add);
        Button queryAllButton = (Button)findViewById(R.id.query_all);      
        Button clearButton = (Button)findViewById(R.id.clear);
        Button deleteAllButton = (Button)findViewById(R.id.delete_all);
        
        Button queryButton = (Button)findViewById(R.id.query);
        Button deleteButton = (Button)findViewById(R.id.delete);
        Button updateButton = (Button)findViewById(R.id.update);
        
        
        addButton.setOnClickListener(addButtonListener); 
        queryAllButton.setOnClickListener(queryAllButtonListener);     
        clearButton.setOnClickListener(clearButtonListener);
        deleteAllButton.setOnClickListener(deleteAllButtonListener);      
        
        queryButton.setOnClickListener(queryButtonListener);
        deleteButton.setOnClickListener(deleteButtonListener);
        updateButton.setOnClickListener(updateButtonListener);
        
        resolver = this.getContentResolver();
           
    }
    
    
    OnClickListener addButtonListener = new OnClickListener() {
		@Override
		public void onClick(View v) {

			ContentValues values = new ContentValues();
			  
			values.put(People.KEY_NAME, nameText.getText().toString());
			values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString()));
			values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString()));
		    			
			Uri newUri = resolver.insert(People.CONTENT_URI, values);

			labelView.setText("添加成功,URI:" + newUri);

		}	
    };
    
    OnClickListener queryAllButtonListener = new OnClickListener() {
		@Override
		public void onClick(View v) {
			Cursor cursor = resolver.query(People.CONTENT_URI,
					new String[] { People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT},
					null, null, null);
			if (cursor == null){
				labelView.setText("數據庫中沒有數據");
				return;
			}
			labelView.setText("數據庫:" + String.valueOf(cursor.getCount()) + "條記錄");
			
			String msg = "";
			if (cursor.moveToFirst()){
				do{
					msg += "ID:" + cursor.getInt(cursor.getColumnIndex(People.KEY_ID)) + ",";
					msg += "姓名:" + cursor.getString(cursor.getColumnIndex(People.KEY_NAME))+ ",";
					msg += "年齡:" + cursor.getInt(cursor.getColumnIndex(People.KEY_AGE)) + ", ";
					msg += "身高:" + cursor.getFloat(cursor.getColumnIndex(People.KEY_HEIGHT)) + "\n";
				}while(cursor.moveToNext());
			  }

			displayView.setText(msg);
		}
    };
    
    
    
    OnClickListener clearButtonListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			displayView.setText("");
		}	
    };
    
    OnClickListener deleteAllButtonListener = new OnClickListener() {
		@Override
		public void onClick(View v) {
			resolver.delete(People.CONTENT_URI, null, null);
			String msg = "數據全部刪除" ;
			labelView.setText(msg);
		}	
    };
    
    OnClickListener queryButtonListener = new OnClickListener() {
		@Override 
		public void onClick(View v) {
			Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString());
			Cursor cursor = resolver.query(uri,
					new String[] { People.KEY_ID, People.KEY_NAME, People.KEY_AGE, People.KEY_HEIGHT},
					null, null, null);
			if (cursor == null){
				labelView.setText("數據庫中沒有數據");
				return;
			}
			
			String msg = "";
			if (cursor.moveToFirst()){
				msg += "ID:" + cursor.getInt(cursor.getColumnIndex(People.KEY_ID)) + ",";
				msg += "姓名:" + cursor.getString(cursor.getColumnIndex(People.KEY_NAME))+ ",";
				msg += "年齡:" + cursor.getInt(cursor.getColumnIndex(People.KEY_AGE)) + ", ";
				msg += "身高:" + cursor.getFloat(cursor.getColumnIndex(People.KEY_HEIGHT)) + "\n";
			  }

			labelView.setText("數據庫:");
			displayView.setText(msg);
		}
    };
    
    OnClickListener deleteButtonListener = new OnClickListener() {
		@Override
		public void onClick(View v) {

			Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString());
			int result = resolver.delete(uri, null, null);
			String msg = "刪除ID為"+idEntry.getText().toString()+"的數據" + (result>0?"成功":"失敗");
			labelView.setText(msg);
		}	
    };
    
    OnClickListener updateButtonListener = new OnClickListener() {
		@Override
		public void onClick(View v) {
			ContentValues values = new ContentValues();
			  
			values.put(People.KEY_NAME, nameText.getText().toString());
			values.put(People.KEY_AGE, Integer.parseInt(ageText.getText().toString()));
			values.put(People.KEY_HEIGHT, Float.parseFloat(heightText.getText().toString()));
		    			
			Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/" + idEntry.getText().toString());
			int result = resolver.update(uri, values, null, null);
			
			String msg = "更新ID為"+idEntry.getText().toString()+"的數據" + (result>0?"成功":"失敗");
			labelView.setText(msg);
		}
    };
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *