Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 內容提供者ContentProvider和內容解析者ContentResolver

內容提供者ContentProvider和內容解析者ContentResolver

編輯:關於Android編程

簡介

ContentProvider 在android中的作用是對外共享數據,也就是說你可以通過ContentProvider把應用中的數據共享給其他應用訪問,其他應用可以通過ContentProvider 對你應用中的數據進行添刪改查。關於數據共享,以前我們學習過文件操作模式,知道通過指定文件的操作模式為Context.MODE_WORLD_READABLE 或Context.MODE_WORLD_WRITEABLE同樣也可以對外共享數據。那麼,這裡為何要使用ContentProvider 對外共享數據呢?是這樣的,如果采用文件操作模式對外共享數據,數據的訪問方式會因數據存儲的方式而不同,導致數據的訪問方式無法統一,如:采用xml文件對外共享數據,需要進行xml解析才能讀取數據;采用sharedpreferences共享數據,需要使用sharedpreferences API讀取數據。
使用ContentProvider對外共享數據的好處是統一了數據的訪問方式。

ContentProvider的原理是按照一定規則暴露自己的接口給其它應用來訪問自己應用的數據(其實就是自定義增刪改查接口並暴露出去,讓別的應用訪問自己的數據)。

ContentResolver就是按照一定規則訪問內容提供者的數據(其實就是調用內容提供者自定義的接口來操作它的數據)。

 

ContentProvider對外共享數據:

步驟

1. 定義一個類 繼承 ContentProvider
2. 定義匹配規則 指定主機名 + path code urimatcher content://
3. 通過靜態代碼塊添加匹配規則
4. 一定要記得在清單文件配置內容提供者 不要忘記加authorities 說明: 第一步繼承ContentProvider需要重寫下面方法:
public class PersonContentProvider extends ContentProvider{
public boolean onCreate()
public Uri insert(Uri uri, ContentValues values)
public int delete(Uri uri, String selection, String[] selectionArgs)
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
public String getType(Uri uri)}

第四步需要在AndroidManifest.xml使用對該ContentProvider進行配置,為了能讓其他應用找到該ContentProvider , ContentProvider

采用了authorities(主機名/域名)對它進行唯一標識,你可以把 ContentProvider看作是一個網站(想想,網站也是提供數據者),authorities 就是他的域名:
 

ContentProvider類主要方法的作用:

public boolean onCreate()
該方法在ContentProvider創建後就會被調用, Android開機後, 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中獲取數據。
public String getType(Uri uri)
該方法用於返回當前Url所代表數據的MIME類型。如果操作的數據屬於集合類型,那麼MIME類型字符串應該以vnd.android.cursor.dir/開頭,

 

例如:要得到所有person記錄的Uri為content://cn.itcast.provider.personprovider/person,那麼返回的MIME類型字符串應該為:“vnd.android.cursor.dir/person”。如果要操作的數據屬於非集合類型數據,那麼MIME類型字符串應該以vnd.android.cursor.item/開頭,例如:得到id為10的person記錄,Uri為content://cn.itcast.provider.personprovider/person/10,那麼返回的MIME類型字符串應該為:“vnd.android.cursor.item/person”。

 

Uri介紹

Uri代表了要操作的數據,Uri主要包含了兩部分信息:1》需要操作的ContentProvider ,2》對ContentProvider中的什麼數據進行操作,一個Uri由以下幾部分組成:


ContentProvider(內容提供者)的scheme已經由Android所規定, scheme為:content://
主機名(或叫Authority)用於唯一標識這個ContentProvider,外部調用者可以根據這個標識來找到它。
路徑(path)可以用來表示我們要操作的數據,路徑的構建應根據業務而定,如下:
要操作person表中id為10的記錄,可以構建這樣的路徑:/person/10
要操作person表中id為10的記錄的name字段, person/10/name
要操作person表中的所有記錄,可以構建這樣的路徑:/person
要操作xxx表中的記錄,可以構建這樣的路徑:/xxx
當然要操作的數據不一定來自數據庫,也可以是文件、xml或網絡等其他存儲方式,如下:
要操作xml文件中person節點下的name節點,可以構建這樣的路徑:/person/name
如果要把一個字符串轉換成Uri,可以使用Uri類中的parse()方法,如下:
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")

UriMatcher類使用介紹

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

 

掌握它們的使用,會便於我們的開發工作。
UriMatcher類用於匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路徑全部給注冊上,如下:


//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://cn.itcast.provider.personprovider/person路徑,返回匹配碼為1
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路徑,返回匹配碼為2
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#號為通配符
switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) {
case 1
break;
case 2
break;
default://不匹配
break;
}
注冊完需要匹配的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,

假設匹配content://cn.itcast.provider.personprovider/person路徑,返回的匹配碼為1

代碼示例

package com.itheima.transaction;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

public class AccountProvider extends ContentProvider {

	private static final int QUEYSUCESS = 0;  // ctrl + shift + X(變大寫)   變小寫  + y  
	private static final int INSERTSUCESS = 1;
	
	private static final int UPDATESUCESS  = 2;
	
	private static final int DELSUCESS  = 3;
	
	//1 想使用內容提供者 必須定義 匹配規則   code:定義的匹配規則 如果 匹配不上  有一個返回碼  -1
	 static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

	private MyOpenHelper helper;
	
	//2 我要添加匹配規則
	 
	 static{
		 //開始添加匹配規則 
		 /**
		  * authority   主機名  通過主機名來訪問我暴露的數據 
		  * path   你也可以隨意 寫 com.itheima.contentprovider/query
		  * code 匹配碼 
		  */
		 matcher.addURI("com.itheima.contentprovider", "query", QUEYSUCESS);
		 //添加插入匹配規則
		 matcher.addURI("com.itheima.contentprovider", "insert", INSERTSUCESS);
		 //添加更新匹配規則
		 matcher.addURI("com.itheima.contentprovider", "update", UPDATESUCESS);
		 //添加刪除匹配規則
		 matcher.addURI("com.itheima.contentprovider", "delete", DELSUCESS);
		 
		 
	 }
	 
	
	@Override
	public boolean onCreate() {
		helper = new MyOpenHelper(getContext());
		
		
		
		return false;
	}

	//Uri 范圍比較大  不但可以指定  tel:   可以定義很多語法 
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		//傳遞過來的uri 是否和我們定義的匹配規則 匹配
		int match = matcher.match(uri);
		if (match == QUEYSUCESS ) {
			//說明匹配成功 
			SQLiteDatabase db = helper.getReadableDatabase();  //獲取數據庫對象 
			Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
			
			//注意  這個地方 不要關閉 cursor 和  db 
			
			//大吼一聲 數據庫發生了改變 
			getContext().getContentResolver().notifyChange(uri, null);
			
			return cursor;
			
		}else{
			//匹配失敗 
			throw new IllegalArgumentException("路徑匹配失敗");
			
		}
		
		
	}

	@Override
	public String getType(Uri uri) {
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		
		int match = matcher.match(uri);
		if (match == INSERTSUCESS) {
			//說明匹配成功
			SQLiteDatabase db = helper.getReadableDatabase();
			long insert = db.insert("info", null, values);
			
			//執行上面這句話 說明我的數據庫內容 發生了變化   首先 要發送一條通知 說明 我發生改變 
			
			if (insert>0) {

				//數據庫發生變化  發送一個通知 
				getContext().getContentResolver().notifyChange(uri, null);
			}
			
			
			
			
			Uri uri2 = Uri.parse("com.itheima.contentprovider/"+insert);
			return uri2;
		}else{
			//匹配失敗 
			throw new IllegalArgumentException("路徑匹配失敗");
			
		}
				
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int match = matcher.match(uri);
		if (match == DELSUCESS) {
			//匹配成功
			SQLiteDatabase db = helper.getReadableDatabase();
			int delete = db.delete("info", selection, selectionArgs);
			
			if (delete>0) {
				//大吼一聲 數據庫發生了改變 
				getContext().getContentResolver().notifyChange(uri, null);
				
			}
			
			
			return delete;
			
		}else {
			
			//匹配失敗 
			throw new IllegalArgumentException("路徑匹配失敗");
			
		}
		
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		
		int match = matcher.match(uri);
		if (match == UPDATESUCESS) {
			//匹配成功 
			SQLiteDatabase db = helper.getReadableDatabase();
			int update = db.update("info", values, selection, selectionArgs);
			
			if (update>0) {
				//大吼一聲 數據庫發生了改變 
				getContext().getContentResolver().notifyChange(uri, null);
			}
			
			return update;
		}else {
			//匹配失敗 
			throw new IllegalArgumentException("路徑匹配失敗");
		}
		
	}

}

內容解析者ContentResolver

使用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.parse(“content://cn.itcast.providers.personprovider/person/10”),那麼將會對主機名為cn.itcast.providers.personprovider的ContentProvider進行操作,操作的數據為person表中id為10的記錄。

使用ContentResolver對ContentProvider中的數據進行添加、刪除、修改和查詢操作:
	ContentResolver resolver =  getContentResolver();
	Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");
	//添加一條記錄
	ContentValues values = new ContentValues();
	values.put("name", "itcast");
	values.put("age", 25);
	resolver.insert(uri, values);		
	//獲取person表中所有記錄
	Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
	while(cursor.moveToNext()){
		Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));
	}
	//把id為1的記錄的name字段值更改新為liming
	ContentValues updateValues = new ContentValues();
	updateValues.put("name", "liming");
	resolver.update(updateIdUri, updateValues, null, null);

 

監聽ContentProvider中數據的變化

如果ContentProvider的訪問者需要知道ContentProvider中的數據發生變化,可以在ContentProvider 發生數據變化時調用

 

getContentResolver().notifyChange(uri, null)來通知注冊在此URI上的訪問者,例子如下:

public class PersonContentProvider extends ContentProvider {
	public Uri insert(Uri uri, ContentValues values) {
		db.insert("person", "personid", values);
		getContext().getContentResolver().notifyChange(uri, null);
	}
	}
如果ContentProvider的訪問者需要得到數據變化通知,必須使用ContentObserver對數據(數據采用uri描述)進行監聽,當監聽到數據變化通知時,

 

系統就會調用ContentObserver的onChange()方法:

	getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),
        		true,//true表示只要發出通知的Uri以方法第一個參數開頭都能被監聽到,否側監聽uri必須與發出通知的uri完全匹配才能被監聽到
                                           new PersonObserver(new Handler()));
	public class PersonObserver extends ContentObserver{
	public PersonObserver(Handler handler) {
		super(handler);
	}
	public void onChange(boolean selfChange) {
	    //此處可以進行相應的業務處理
	}
	}

 

實例代碼

利用內容解析者備份短信

package com.itheima.backupsms;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.xmlpull.v1.XmlSerializer;

import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.app.Activity;
import android.database.Cursor;
import android.util.Xml;
import android.view.Menu;
import android.view.View;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	/**
	 * 點擊按鈕備份短信
	 * 
	 * @param v
	 */
	public void backup(View v) {

		try {
			// 1 初始化xml 序列化器
			XmlSerializer serializer = Xml.newSerializer();
			// 2初始化xml序列化器的 參數
			File file = new File(Environment.getExternalStorageDirectory()
					.getPath(), "smsbackup.xml");
			FileOutputStream fos = new FileOutputStream(file);
			serializer.setOutput(fos, "utf-8");

			// 3 開始寫 xml 頭
			serializer.startDocument("utf-8", true);

			// 開始寫 xml 根節點 smss
			serializer.startTag(null, "smss");
			// 1 把我們關系的短信數據庫裡的內容 給 獲取出來 我們要做的操作 就是通過內容解析者 把數據給查詢出來
			Uri uri = Uri.parse("content://sms");

			Cursor cursor = getContentResolver().query(uri,
					new String[] { "address", "date", "body" }, null, null,
					null);
			while (cursor.moveToNext()) {
				// 寫sms 節點
				serializer.startTag(null, "sms");
				String address = cursor.getString(0);
				String date = cursor.getString(1);
				String body = cursor.getString(2);

				//開始寫 address 節點 
				serializer.startTag(null, "address");
				serializer.text(address);
				serializer.endTag(null, "address");
				
				//開始寫 date 節點 
				serializer.startTag(null, "date");
				serializer.text(date);
				serializer.endTag(null, "date");
				
				
				//開始寫 body 節點 
				serializer.startTag(null, "body");
				serializer.text(body);
				serializer.endTag(null, "body");
				
				serializer.endTag(null, "sms");
			}

			// 開始寫 address date body

			serializer.endTag(null, "smss");

			// 文檔結束
			serializer.endDocument();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

利用內容解析者插入短信

//1 先拿到 內容解析器 
		Uri uri = Uri.parse("content://sms");
		ContentValues values = new ContentValues();
		values.put("address", "110");
		values.put("date", System.currentTimeMillis());
		values.put("body", "您的事犯了 請您馬上來一趟");
		getContentResolver().insert(uri, values);

利用內容解析者獲取聯系人數據

1. 首先 我要查詢raw_contacts表 獲取到 contact_id
2. 去data表 根據 contact_id 去獲取 mimetype data1的數據
3. 然後根據 mimetype_id 來區分數據類型
package com.itheima.getcontactinfo.utils;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;

import com.itheima.getcontactinfo.domain.ContactInfo;

public class ContactUtils {

	public static List getContactInfos(Context context) {

		List contactLists = new ArrayList();
		// 1 首先 我要查詢raw_contacts表 獲取到 contact_id列
		// (1)我如何查詢 ?用 內容解析者 path
		Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
		Uri datauri = Uri.parse("content://com.android.contacts/data");

		ContentResolver resolver = context.getContentResolver();
		Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
				null, null, null);
		while (cursor.moveToNext()) {
			// 獲取到contact_id 的值
			String contact_id = cursor.getString(0);
			System.out.println("contact_id--" + contact_id);

			// 有個小細節注意一下 我要判斷 contact_id 是否為空
			if (contact_id != null) {

				ContactInfo info = new ContactInfo();
				info.setId(contact_id);

				// 2去data表 根據 contact_id 去獲取 mimetyple_id列 data1 列

				Cursor dataCursor = resolver.query(datauri, new String[] {
						"mimetype", "data1" }, "raw_contact_id=?",
						new String[] { contact_id }, null);
				while (dataCursor.moveToNext()) {
					String mimetype = dataCursor.getString(0); // 獲取到mimeytype
					String data1 = dataCursor.getString(1);

					// 3 然後根據 mimetype類型 來區分數據類型
					if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
						info.setEmail(data1);

					} else if ("vnd.android.cursor.item/name".equals(mimetype)) {

						info.setName(data1);

					} else if ("vnd.android.cursor.item/phone_v2"
							.equals(mimetype)) {
						System.out.println("data---電話號碼-" + data1);
						info.setPhone(data1);
					}

				}
				dataCursor.close();
				contactLists.add(info); // 把info信息存到集合中

			}

		}
		cursor.close();

		return contactLists;

	}

}
package com.itheima.getcontactinfo.domain;

public class ContactInfo {

	private String id;
	private String name;
	private String phone;
	private String email;
	
	
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	@Override
	public String toString() {
		return "ContactInfo [id=" + id + ", name=" + name + ", phone=" + phone
				+ ", email=" + email + "]";
	}

	
	
	
	
}

利用內容解析者插入聯系人

1. 先往raw_contacts 插入一條數據
2. 往data表插入數據 根據mimetype contact_id 往裡插入數據
package com.itheima.insert.contact;

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

public class MainActivity extends Activity {

	private EditText et_name;
	private EditText et_phone;
	private EditText et_email;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//1找到我們關心的控件
		
		et_name = (EditText) findViewById(R.id.et_name);
		et_phone = (EditText) findViewById(R.id.et_phone);
		et_email = (EditText) findViewById(R.id.et_email);
		
		
		
		
	}
	
	/**
	 * 保存聯系人的信息
	 * @param v
	 */
	public void click(View v){
		
		//1 要先拿到  edittext的值 
		String name = et_name.getText().toString().trim();
		String phone = et_phone.getText().toString().trim();
		String email = et_email.getText().toString().trim();
		
		//2 往 raw_contacts裡插入一條數據   拿到內容解析者 
		Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
		Uri datauri = Uri.parse("content://com.android.contacts/data");
		
		ContentValues values = new ContentValues();
		// 插入數據之前  我要先查詢 一下  raw_contacts 表一共有多少行的數據 
		Cursor cursor = getContentResolver().query(uri, null, null, null, null);
		int count = cursor.getCount();  //獲取到一共有多少行 
	    int contact_id =  count + 1;  
		
		values.put("contact_id", contact_id);
		getContentResolver().insert(uri, values);
		
		// 3 往data 表插入數據 
		
		ContentValues nameValues = new ContentValues();
		nameValues.put("data1", name);
		nameValues.put("mimetype", "vnd.android.cursor.item/name");
		nameValues.put("raw_contact_id", contact_id);
		getContentResolver().insert(datauri, nameValues);

		ContentValues phoneValues = new ContentValues();
		phoneValues.put("data1", phone);
		phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
		phoneValues.put("raw_contact_id", contact_id);
		getContentResolver().insert(datauri, phoneValues);
		
		ContentValues emailValues = new ContentValues();
		emailValues.put("data1", email);
		emailValues.put("mimetype", "vnd.android.cursor.item/email_v2");
		emailValues.put("raw_contact_id", contact_id);
		getContentResolver().insert(datauri, emailValues);
		
		
		
		
		
	}

	
}

利用監聽和內容解析者監聽短信

package com.itheima.smslistener;

import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.view.Menu;

public class MainActivity extends Activity {

	private Uri	uri;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		
		//1注冊一個內容觀察者 
	   	uri = Uri.parse("content://sms");
		getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler()));
		
	}

	
	
	private  class MyObserver  extends ContentObserver{

		public MyObserver(Handler handler) {
			super(handler);
		}
		@Override
		public void onChange(boolean selfChange) {
			
			//當短信的數據庫發生了變化    我就去取出所有短信的內容 
			
			Cursor cursor = getContentResolver().query(uri, new String[]{"address","body","date"}, null, null, null);
			while(cursor.moveToNext()){
				
				String address = cursor.getString(0);
				String body = cursor.getString(1);
				String date = cursor.getString(2);
				
				System.out.println("address---"+address+"--body:"+body);
				
			}
			
			super.onChange(selfChange);
		}
		
		
	}
	
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved