深入U(xiǎn)nderstanding Android ContentProvider詳解
更新時(shí)間:2013年05月22日 09:31:46 作者:
本篇文章是對(duì)Android ContentProvider進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
1. 什么是ContentProvider
也即內(nèi)容提供者,是對(duì)所有數(shù)據(jù)訪問的一層抽象,為數(shù)據(jù)訪問提供了統(tǒng)一的接口。它有以下優(yōu)點(diǎn):
a. 對(duì)數(shù)據(jù)的抽象,為所有的組件提供統(tǒng)一的訪問數(shù)據(jù)的方式,從而讓組件不必關(guān)心具體數(shù)據(jù)的呈現(xiàn)形式(文件or數(shù)據(jù)庫)。數(shù)據(jù),也可以只關(guān)心自身的管理,而不用去管使用者的訪問問題。這樣就達(dá)到了很好的封裝。
b. 接口更加方便,更加方便的讓組件之間傳送數(shù)據(jù)
ContentProvider的訪問標(biāo)識(shí)為Uri,通過統(tǒng)一的ContentResolver進(jìn)行訪問,而ContentResolver和Uri跟Application的上下文Context以及組件之間的信息傳送工具Intent都是無縫接合,這就讓組件之間進(jìn)行數(shù)據(jù)共享和數(shù)據(jù)傳遞更加的方便和快捷。
所以,ContentProvider的最大好處在于它可以在不同組件之間方便的共享。所以,如果你的應(yīng)用里面用到的數(shù)據(jù)需要在不同的組件之間共享,那么實(shí)現(xiàn)一個(gè)ContentProvider無疑是最佳方案。
2. 實(shí)現(xiàn)方式
ContentProvider的實(shí)現(xiàn)方式非常簡(jiǎn)單,只需要根據(jù)需求實(shí)現(xiàn)一些接口即可,比如:query, insert, delete, update, openFile等。但是具體的數(shù)據(jù)的呈現(xiàn)形式則是根據(jù)不同的目的進(jìn)行自由選擇,比如對(duì)于結(jié)構(gòu)化數(shù)據(jù),選擇SQLiteDatabase可能是比較好的方案,大量的字節(jié)流可能文件是首選等等。
需要注意一點(diǎn)的是,雖然Android中百分之九十的ContentProvider內(nèi)部都是用SQLiteDatabase來存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù),但這并不意味著ContentProvider只能從SQLiteDatabase來管理數(shù)據(jù)。ContentProvider定義了一些接口,你只需要按照需要返回正確的數(shù)據(jù)即可,具體 的實(shí)現(xiàn)方式則由你自由選擇。
比如,Contacts的ContentProvider能提供以vCard的方式輸出,也就是說當(dāng)讀取一個(gè)vCard的uri時(shí),這個(gè)流是一個(gè)vCard形式的文件流,實(shí)現(xiàn)起來的思路就是這樣:
Cursor query(Uri, ....) {
if (uri is for vCard) {
query the Contact's infomation
create a cursor with two columns name and size
put contact's name into cursor
sum all Contact's field and get size
put that size into cursor
return the cursor
}
}
這樣通過Query就能得到這個(gè)vCard的相關(guān)信息文件名字和大小,再通過openInputStream就可以讀取這個(gè)vCard文件流,但是實(shí)際上ContentProvider是沒有vCard形式的數(shù)據(jù),也沒有一個(gè)vCard的文件,它只是在openFile的時(shí)候,識(shí)別出vCard的uri,把Contact數(shù)據(jù)轉(zhuǎn)化成vCard形式寫入輸出流中:
ParcelFileDescriptor openFile(Uri...) {
if (uri is for vcard) {
generate vcard with VCardComposer
write to output stream
}
}
3. 其他替代方案
ContentProvider不是必須的,每個(gè)應(yīng)用必然用到數(shù)據(jù),但是可以選擇用創(chuàng)建一個(gè)ContentProvider來管理,也可以直接使用文件或數(shù)據(jù)庫,如下面的例子:
package com.android.effective;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.Bundle;
import android.util.Log;
public class SQLiteDatabaseDemo extends Activity {
private static final String TAG = "SQLiteDatabaseDemo";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDatabase db = new MyDatabase(this);
int id = db.setName("Michael Jordan");
Log.e(TAG, "id of " + id + " is " + db.getName(id));
}
private class MyDatabase {
private static final String name = "demo.db";
private static final String table = "demo";
private final String[] projection = new String[] {"_id", "name" };
private MyDatabaseHelper helper;
public MyDatabase(Context context) {
helper = new MyDatabaseHelper(context, name, null, 1);
}
public String getName(int id) {
final Cursor c = helper.getReadableDatabase().query("demo", projection, "_id=" + id,
null, null, null, null);
if (c == null || !c.moveToFirst()) {
return null;
}
return c.getString(1);
}
public int setName(String name) {
ContentValues cv = new ContentValues();
cv.put("name", name);
return (int) helper.getWritableDatabase().insert(table, "name", cv);
}
}
private class MyDatabaseHelper extends SQLiteOpenHelper {
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE demo (_id INTEGER PRIMARY KEY, name TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int old, int newver) {
}
}
}
這個(gè)例子中就沒有使用ContentProvider而是讓Activity直接操作SQLiteDatabase來實(shí)現(xiàn)數(shù)據(jù)的管理,或者不用數(shù)據(jù)庫而直接使用文件進(jìn)行管理數(shù)據(jù)。
這種方式實(shí)現(xiàn)起來可能更簡(jiǎn)單,對(duì)于需求不大,數(shù)據(jù)量不大,且只有單一組件使用的情況下,完全可以用這種方式。但是它的缺點(diǎn)也很明顯,就是在組件之間傳遞會(huì)十分麻煩,甚至不能夠在組件之間共享。為了共享,就要把數(shù)據(jù)層進(jìn)行抽象,使其獨(dú)立于任何一個(gè)Activity,以滿足不同的組件對(duì)數(shù)據(jù)進(jìn)行讀寫,但是這樣一來跟實(shí)現(xiàn)一個(gè)ContentProvider就沒有區(qū)別了,還不如實(shí)現(xiàn)一個(gè)ContentProvider來的方便。
所以,規(guī)則就是如果某些數(shù)據(jù)只在一個(gè)Activity中使用,那么沒有必要?jiǎng)?chuàng)建ContentProvider,直接使用文件或直接操作Database就可以達(dá)到目的。但是如果需要跟其他的組件進(jìn)行共享和傳遞數(shù)據(jù),就必須使用ContentProvider。
另外,有了ContentProvider也可以方便跟其他應(yīng)用進(jìn)行交互,把數(shù)據(jù)傳遞給其他應(yīng)用的組件。
在使用SQLiteOpenHelper一定要注意線程同步問題,保證每一個(gè)SQLiteDatabase的方法(如execSQL)的線程安全性,否則可能會(huì)引起十分罕見的異常。曾遇到一個(gè)SQLiteStatement報(bào)出的NPE(NullPointerException),就是由于有多個(gè)線程在操作同一個(gè)SQLiteOpenHelper,而且沒有同步。
也即內(nèi)容提供者,是對(duì)所有數(shù)據(jù)訪問的一層抽象,為數(shù)據(jù)訪問提供了統(tǒng)一的接口。它有以下優(yōu)點(diǎn):
a. 對(duì)數(shù)據(jù)的抽象,為所有的組件提供統(tǒng)一的訪問數(shù)據(jù)的方式,從而讓組件不必關(guān)心具體數(shù)據(jù)的呈現(xiàn)形式(文件or數(shù)據(jù)庫)。數(shù)據(jù),也可以只關(guān)心自身的管理,而不用去管使用者的訪問問題。這樣就達(dá)到了很好的封裝。
b. 接口更加方便,更加方便的讓組件之間傳送數(shù)據(jù)
ContentProvider的訪問標(biāo)識(shí)為Uri,通過統(tǒng)一的ContentResolver進(jìn)行訪問,而ContentResolver和Uri跟Application的上下文Context以及組件之間的信息傳送工具Intent都是無縫接合,這就讓組件之間進(jìn)行數(shù)據(jù)共享和數(shù)據(jù)傳遞更加的方便和快捷。
所以,ContentProvider的最大好處在于它可以在不同組件之間方便的共享。所以,如果你的應(yīng)用里面用到的數(shù)據(jù)需要在不同的組件之間共享,那么實(shí)現(xiàn)一個(gè)ContentProvider無疑是最佳方案。
2. 實(shí)現(xiàn)方式
ContentProvider的實(shí)現(xiàn)方式非常簡(jiǎn)單,只需要根據(jù)需求實(shí)現(xiàn)一些接口即可,比如:query, insert, delete, update, openFile等。但是具體的數(shù)據(jù)的呈現(xiàn)形式則是根據(jù)不同的目的進(jìn)行自由選擇,比如對(duì)于結(jié)構(gòu)化數(shù)據(jù),選擇SQLiteDatabase可能是比較好的方案,大量的字節(jié)流可能文件是首選等等。
需要注意一點(diǎn)的是,雖然Android中百分之九十的ContentProvider內(nèi)部都是用SQLiteDatabase來存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù),但這并不意味著ContentProvider只能從SQLiteDatabase來管理數(shù)據(jù)。ContentProvider定義了一些接口,你只需要按照需要返回正確的數(shù)據(jù)即可,具體 的實(shí)現(xiàn)方式則由你自由選擇。
比如,Contacts的ContentProvider能提供以vCard的方式輸出,也就是說當(dāng)讀取一個(gè)vCard的uri時(shí),這個(gè)流是一個(gè)vCard形式的文件流,實(shí)現(xiàn)起來的思路就是這樣:
復(fù)制代碼 代碼如下:
Cursor query(Uri, ....) {
if (uri is for vCard) {
query the Contact's infomation
create a cursor with two columns name and size
put contact's name into cursor
sum all Contact's field and get size
put that size into cursor
return the cursor
}
}
這樣通過Query就能得到這個(gè)vCard的相關(guān)信息文件名字和大小,再通過openInputStream就可以讀取這個(gè)vCard文件流,但是實(shí)際上ContentProvider是沒有vCard形式的數(shù)據(jù),也沒有一個(gè)vCard的文件,它只是在openFile的時(shí)候,識(shí)別出vCard的uri,把Contact數(shù)據(jù)轉(zhuǎn)化成vCard形式寫入輸出流中:
復(fù)制代碼 代碼如下:
ParcelFileDescriptor openFile(Uri...) {
if (uri is for vcard) {
generate vcard with VCardComposer
write to output stream
}
}
3. 其他替代方案
ContentProvider不是必須的,每個(gè)應(yīng)用必然用到數(shù)據(jù),但是可以選擇用創(chuàng)建一個(gè)ContentProvider來管理,也可以直接使用文件或數(shù)據(jù)庫,如下面的例子:
復(fù)制代碼 代碼如下:
package com.android.effective;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.Bundle;
import android.util.Log;
public class SQLiteDatabaseDemo extends Activity {
private static final String TAG = "SQLiteDatabaseDemo";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDatabase db = new MyDatabase(this);
int id = db.setName("Michael Jordan");
Log.e(TAG, "id of " + id + " is " + db.getName(id));
}
private class MyDatabase {
private static final String name = "demo.db";
private static final String table = "demo";
private final String[] projection = new String[] {"_id", "name" };
private MyDatabaseHelper helper;
public MyDatabase(Context context) {
helper = new MyDatabaseHelper(context, name, null, 1);
}
public String getName(int id) {
final Cursor c = helper.getReadableDatabase().query("demo", projection, "_id=" + id,
null, null, null, null);
if (c == null || !c.moveToFirst()) {
return null;
}
return c.getString(1);
}
public int setName(String name) {
ContentValues cv = new ContentValues();
cv.put("name", name);
return (int) helper.getWritableDatabase().insert(table, "name", cv);
}
}
private class MyDatabaseHelper extends SQLiteOpenHelper {
public MyDatabaseHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE demo (_id INTEGER PRIMARY KEY, name TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int old, int newver) {
}
}
}
這個(gè)例子中就沒有使用ContentProvider而是讓Activity直接操作SQLiteDatabase來實(shí)現(xiàn)數(shù)據(jù)的管理,或者不用數(shù)據(jù)庫而直接使用文件進(jìn)行管理數(shù)據(jù)。
這種方式實(shí)現(xiàn)起來可能更簡(jiǎn)單,對(duì)于需求不大,數(shù)據(jù)量不大,且只有單一組件使用的情況下,完全可以用這種方式。但是它的缺點(diǎn)也很明顯,就是在組件之間傳遞會(huì)十分麻煩,甚至不能夠在組件之間共享。為了共享,就要把數(shù)據(jù)層進(jìn)行抽象,使其獨(dú)立于任何一個(gè)Activity,以滿足不同的組件對(duì)數(shù)據(jù)進(jìn)行讀寫,但是這樣一來跟實(shí)現(xiàn)一個(gè)ContentProvider就沒有區(qū)別了,還不如實(shí)現(xiàn)一個(gè)ContentProvider來的方便。
所以,規(guī)則就是如果某些數(shù)據(jù)只在一個(gè)Activity中使用,那么沒有必要?jiǎng)?chuàng)建ContentProvider,直接使用文件或直接操作Database就可以達(dá)到目的。但是如果需要跟其他的組件進(jìn)行共享和傳遞數(shù)據(jù),就必須使用ContentProvider。
另外,有了ContentProvider也可以方便跟其他應(yīng)用進(jìn)行交互,把數(shù)據(jù)傳遞給其他應(yīng)用的組件。
在使用SQLiteOpenHelper一定要注意線程同步問題,保證每一個(gè)SQLiteDatabase的方法(如execSQL)的線程安全性,否則可能會(huì)引起十分罕見的異常。曾遇到一個(gè)SQLiteStatement報(bào)出的NPE(NullPointerException),就是由于有多個(gè)線程在操作同一個(gè)SQLiteOpenHelper,而且沒有同步。
您可能感興趣的文章:
- Android開發(fā)之ContentProvider的使用詳解
- Android中自定義ContentProvider實(shí)例
- 實(shí)例講解Android中ContentProvider組件的使用方法
- 基于Android ContentProvider的總結(jié)詳解
- Android中自定義ContentProvider實(shí)例
- Android編程使用內(nèi)容提供者方式(ContentProvider)進(jìn)行存儲(chǔ)的方法
- Android內(nèi)容提供者ContentProvider用法實(shí)例分析
- Android編程之創(chuàng)建自己的內(nèi)容提供器實(shí)現(xiàn)方法
相關(guān)文章
Android 判斷是開發(fā)debug模式,還是發(fā)布release模式的方法
下面小編就為大家?guī)硪黄狝ndroid 判斷是開發(fā)debug模式,還是發(fā)布release模式的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12Kotlin遍歷集合導(dǎo)致并發(fā)修改異常的原因和解決方法
這篇文章主要介紹了Kotlin遍歷集合導(dǎo)致并發(fā)修改異常的原因和解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Android開發(fā)之獲取單選與復(fù)選框的值操作示例
這篇文章主要介紹了Android開發(fā)之獲取單選與復(fù)選框的值操作,結(jié)合實(shí)例形式分析了Android針對(duì)單選按鈕、復(fù)選框的事件響應(yīng)、數(shù)值獲取等相關(guān)操作技巧,需要的朋友可以參考下2019-04-04Android實(shí)現(xiàn)截屏方式整理(總結(jié))
本篇文章主要介紹了Android 截屏方式整理(總結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07Android實(shí)現(xiàn)簡(jiǎn)單的下拉阻尼效應(yīng)示例代碼
下面小編就為大家分享一篇Android實(shí)現(xiàn)簡(jiǎn)單的下拉阻尼效應(yīng)示例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01獲取Android手機(jī)中所有短信的實(shí)現(xiàn)代碼
這篇文章主要介紹了獲取Android手機(jī)中所有短信的實(shí)現(xiàn)代碼,需要的朋友可以參考下2014-08-08