編輯:關於Android編程
Android四大組件之Content Provider
一、概念
Content Provider 作為Android應用程序四大組件之一,為存儲和查詢數據提供統一的接口,實現程序間數據的共享。Android系統內一些常見的數據如音樂、視頻、圖像等都內置了一系列的Content Provider。
應用程序間共享數據有兩種方式:
一是創建子類繼承於Content Provider,重寫該類用於數據存儲和查詢的方法。
二是直接使用已經存在的Content Provider,如聯系人等。
在Content Provider中數據的是以表的形式存儲,在數據表中每一行為一條記錄,每一列為類型和數據。每一條記錄都包括一個唯一的名叫_ID的數值字段,這個字段唯一標識一條數據記錄,在創建表的時候用 INTEGER PRIMARY KEY AUTOINCREMENT來標識此字段。
二、相關類介紹
類URI:
每一個Content Provider為其管理的多個數據集,分配一個URI,這個URI對外提供了一個能夠唯一標識自己數據集的字符串。這樣別的應用程序就可以通過這個URI來訪問這個數據集。Android中所有的Content Provider的URI都有固定格式:content://****開頭,一般可分為4個部分:
標准前綴:用來標識一個Content Provider,固定Content://
URI標識:定義了是哪個Content Provider提供這些數據。一般是定義該ContentProvider的包類的名字。和AndroidManifest.xml中定義的authorities屬性相同。
路徑:標識URI下的某一個Item。
記錄的ID:如果URI中包含表示某個記錄的ID,則返貨該id對應的數據。否則表示返回全部。
注意:"content://com.android.people.provider/contacts/#" 這裡#表示匹配任意數字"content://com.android.people.provider/contacts/*"表示匹配任意文本
UriMatcher:
Uri標識了要操作的數據,而UriMatcher即使Android提供給我們用於操作Uri這個數據的工具類。
常用方法:
public void addURI(String authority, String path, int code) 往UriMatcher對象裡面添加Uri。
public int match(Uri uri) 與UriMatcher對象的Uri進行匹配,如果成功返回上面傳入的code值,否則返回-1.
類ContentUris:
類似於UriMatcher,也是一個操作Uri數據的工具類,用於在Uri後面追加一個ID或解析出傳入的Uri對象的ID值。
常用方法:
public static Uri withAppendId(Uri contentUri, long id) 為前面contentUri加上ID部分
public static long parseId(Uri contentUri) 從contentUri中獲取ID部分
類ContentProvider:
常用方法:
[java]
public abstract boolean onCreate();
public abstract Uri insert(Uri uri, ContentValues values)
public abstract int delete(Uri uri, String selection, String[] selectionArsg);
public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
public abstract Cursor query(Uri uri, String[] peojection, String selection, String[] selectionArgs, String sortOrder)
public abstract String getType(Uri uri)
public abstract boolean onCreate();
public abstract Uri insert(Uri uri, ContentValues values)
public abstract int delete(Uri uri, String selection, String[] selectionArsg);
public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
public abstract Cursor query(Uri uri, String[] peojection, String selection, String[] selectionArgs, String sortOrder)
public abstract String getType(Uri uri)
這些都是抽象方法需要子類去實現。類 ContentValues:
android.content.ContentValues 這個用於存儲ContentResolver能處理的數據的集合。
構造函數:
ContentValues() // 創建一個空的ContentValues對象,初始化默認大小
ContentValues(int size)
ContentValues(ContentValues from)
常用方法:
[java]
void clear()
boolean containKey(String key)
Object get(String key)
void put(String key, Type value) // Type: Byte Integer Float Short byte[] String Double Long Boolean
int size()
Type getAsTypeArray(String key) // Type: Object Boolean Byte byte[] Double Float Integer Long Short String<SPAN style="FONT-SIZE: 14px">
</SPAN>
void clear()
boolean containKey(String key)
Object get(String key)
void put(String key, Type value) // Type: Byte Integer Float Short byte[] String Double Long Boolean
int size()
Type getAsTypeArray(String key) // Type: Object Boolean Byte byte[] Double Float Integer Long Short String
類android.content.ContentResolver:
一個程序可以通過實現一個ContentProvider的抽象接口將自己的數據以類似數據庫中表的方式完全暴露出去,那麼外界其他應用程序怎麼從數據庫中獲取數據呢,這就需要ContentResolver了,通過URI表示外界訪問需要的數據庫。 這個類為我們定義了一系列的方法包括:插入、刪除、修改、查詢等,與ContentProvider基本類似,主要根據傳入的參數Uri找到對應的Content Provider,調用其相應的方法。
構造函數:
public ContentResolver(Context context)
一般在代碼中我們直接通過: MainActivity.this.getContentResolver()獲得當前應用程序的一個ContentResolver實例。
這裡我們需要考慮一個問題,就是如果多個程序同時通過ContentResolver共享訪問一個ContentProvider,會不會不同步,尤其是數據寫入的時候這就需要在AndroidManifest.xml中定義ContentProvider的時候加上:<provider>元素的multiprocess屬性。同時Android在ContentResolver中為我們提供了
notifyChange()接口,在數據發生改變時通知其他的ContentObserver。
[java]
final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
final void unregisterContentObserver(ContentObserver observer)
void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)
void notifyChange(Uri uri, ContentObserver observer)
final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
final void unregisterContentObserver(ContentObserver observer)
void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)
void notifyChange(Uri uri, ContentObserver observer)
三、使用已經存在的ContentProvider
使用已經存在的ContentProvider包括兩種情況:一是從已經存在的ContentProvider裡面讀取數據,如獲得
手機裡面聯系人的信息。二是將自己的數據加入到ContentProvider裡面,讓其他程序能共享次數據,這就需要獲得對這個
ContentProvider的寫權限了。
Android中電話簿就是通過ContentProvider實現數據共享的,系統中有很多已經存在的共享URI,我們可以使用ContentResolver通過Uri來操作不同的標的數據。如Contacts.People.CONTENT_URI
在Android中為我們提供了兩種方法來查詢Content Provider:一是使用ContentResolver的query()方法,二是使用Activity對象的manageQuery()方法,他們的參數都相同,而且都返回Cursor對象。但是使用manageQuery()方法返回的Cursor對象的生命周期自動被Activity來管理,被管理的Cursor對象在Activity進入暫停狀態的時候調用自己的deactivate()方法卸載,在Activity回到運行狀態的時候調用自己的requery()方法重新查詢生成的Cursor。而使用ContentResolver的query()方法返回的Cursor對象需要手動加入Activity來管理,這是通過Activity的startManagingCursor()方法來實現的。
四、創建自己的ContentProvider
(1) 創建一個繼承於ContentProvider的類MyContentProvider
(2) 定義一個public static final Uri 類型變量CONTENT_URI
如:public static final Uri CONTENT_URI = Uri.parse("content://com.android.MyContentProvider")
(3) 定義需要返回給客戶端的數據列名,如果使用到數據庫SQLite,必須定義一個_id,表示記錄的唯一性。
(4) 創建數據存儲系統,如文件系統或數據庫SQLite系統。
(5) 如果存儲字節型數據,如位圖文件等,數據列其實是一個表示實際保存文件的URI字符串,用來讀取對應的實際文件數據。 處理這種數據類型的Content Provider需要實現一個名為_data的字段,該字段列出了該文件在Android系統的實際路 徑,客戶端可以通過調用方法ContentResolver.
openOutputStream()來處理該URI指向的文件資源。
(6) 查詢返回一個Cursor類型對象,所有執行寫操作的方法如insert()、update()及delete()都將被監聽,可以通過Content
Resolver().notifyChange()來通過監聽器關於數據更新的消息。
(7) 在AndroidManifest.xml中使用<provider>標簽來設置Content Provider信息,如:android:authorities、android:name
android:permission等。
Demo:
數據庫類DatabaseHelper用來存儲個人信息,名字(name)對應年齡(age)
[java]
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "personInfo.db";
private static final String TB_NAME = "person";
private static final int VERSION = 1;
private static final String creat_cmd = "create table IF NOT EXISTS " + TB_NAME + " (_id integer PRIMARY KEY autoincrement, name text, age integer)";
private static final String upgrade_cmd = "alert table " + TB_NAME + " add sex varchar(8)";
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(creat_cmd);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL(upgrade_cmd);
}
}
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "personInfo.db";
private static final String TB_NAME = "person";
private static final int VERSION = 1;
private static final String creat_cmd = "create table IF NOT EXISTS " + TB_NAME + " (_id integer PRIMARY KEY autoincrement, name text, age integer)";
private static final String upgrade_cmd = "alert table " + TB_NAME + " add sex varchar(8)";
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
// TODO Auto-generated constructor stub
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(creat_cmd);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL(upgrade_cmd);
}
}
創建自己的ContentProvider
[java]
// 創建一個MyProvider繼承於ContentProvider
public class MyProvider extends ContentProvider {
private DatabaseHelper dbHelper; // 數據庫類
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int ALL_PERSON = 1;
private static final int PERSON = 2;
private static final String TAG = "MyProvider";
private static final String TABLE_NAME = "person";
// 定義自己的URI
private static final String AUTHORITY = "com.myAndroid.myProvider";
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
static {
URI_MATCHER.addURI(AUTHORITY, "person", ALL_PERSON);
URI_MATCHER.addURI(AUTHORITY, "person/#", PERSON);
}
// ContentProvider的入口,初始化
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
Log.i(TAG, "----onCreate----");
dbHelper = new DatabaseHelper(this.getContext());
return false;
}
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
Log.i(TAG, "----delete----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
count = db.delete(TABLE_NAME, arg1, arg2);
case PERSON:
long id = ContentUris.parseId(arg0);
String where = "_id=" + id;
if(arg1 != null && !"".equals(arg1)) {
where += " and " + arg1;
}
count = db.delete(TABLE_NAME, where, arg2);
default:
throw new IllegalArgumentException("Unknown Uri:" + arg0.toString());
}
getContext().getContentResolver().notifyChange(arg0, null); // 通知注冊客戶端數據發生改變
return count;
}
@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
Log.i(TAG, "----getType----");
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
return "com.android.cursor.dir/person";
case PERSON:
return "com.android.cursor.item/person";
default:
throw new IllegalArgumentException("Unknow Uri" + arg0.toString());
}
}
@Override
public Uri insert(Uri uri, ContentValues arg1) {
// TODO Auto-generated method stub
Log.i(TAG, "----insert----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri insertUri = null;
switch (URI_MATCHER.match(uri)) {
case ALL_PERSON:
long rowId = db.insert(TABLE_NAME, "name", arg1);
Log.d(TAG, "insert:"+arg1.toString()+" Id:"+rowId);
insertUri = ContentUris.withAppendedId(uri, rowId);
// this.getContext().getContentResolver().notifyChange(insertUri, null);
break;
default:
throw new IllegalArgumentException("Unknown Uri:" + uri.toString());
}
getContext().getContentResolver().notifyChange(insertUri, null);
return insertUri;
}
// 處理查詢,返回Cursor
@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
String arg4) {
// TODO Auto-generated method stub
Log.i(TAG, "----query----");
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursir cursor = null;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON: // query all the person info
cursor = db.query(TABLE_NAME, arg1, arg2, arg3, null, null, arg4);
case PERSON: // query only one person info from a given ID
long id = ContentUris.parseId(arg0);
String where = " _id=" + id;
if( (!"".equals(arg2)) && (arg2 != null)) {
where += " and " + arg2 ;
}
cursor = db.query(TABLE_NAME, arg1, where, arg3, null, null, arg4);
default:
throw new IllegalArgumentException("unknow uri" + arg0.toString());
}
if(cursor != null) {
// 注冊該Uri對應的數據發生改變時,向客戶端發送通知
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
return cursor;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
Log.i(TAG, "----update----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
count = db.update(TABLE_NAME, arg1, arg2, arg3);
break;
case PERSON:
long id = ContentUris.parseId(arg0);
String where = "_id=" + id;
if( (arg2 != null) && (!"".equals(arg2))) {
where += " and " + arg2;
}
count = db.update(TABLE_NAME, arg1, where, arg3);
break;
default:
throw new IllegalArgumentException("Unknow Uri:"+arg0.toString());
}
getContext().getContentResolver().notifyChange(arg0, null);
return count;
}
}
// 創建一個MyProvider繼承於ContentProvider
public class MyProvider extends ContentProvider {
private DatabaseHelper dbHelper; // 數據庫類
private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private static final int ALL_PERSON = 1;
private static final int PERSON = 2;
private static final String TAG = "MyProvider";
private static final String TABLE_NAME = "person";
// 定義自己的URI
private static final String AUTHORITY = "com.myAndroid.myProvider";
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
static {
URI_MATCHER.addURI(AUTHORITY, "person", ALL_PERSON);
URI_MATCHER.addURI(AUTHORITY, "person/#", PERSON);
}
// ContentProvider的入口,初始化
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
Log.i(TAG, "----onCreate----");
dbHelper = new DatabaseHelper(this.getContext());
return false;
}
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
Log.i(TAG, "----delete----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
count = db.delete(TABLE_NAME, arg1, arg2);
case PERSON:
long id = ContentUris.parseId(arg0);
String where = "_id=" + id;
if(arg1 != null && !"".equals(arg1)) {
where += " and " + arg1;
}
count = db.delete(TABLE_NAME, where, arg2);
default:
throw new IllegalArgumentException("Unknown Uri:" + arg0.toString());
}
getContext().getContentResolver().notifyChange(arg0, null); // 通知注冊客戶端數據發生改變
return count;
}
@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
Log.i(TAG, "----getType----");
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
return "com.android.cursor.dir/person";
case PERSON:
return "com.android.cursor.item/person";
default:
throw new IllegalArgumentException("Unknow Uri" + arg0.toString());
}
}
@Override
public Uri insert(Uri uri, ContentValues arg1) {
// TODO Auto-generated method stub
Log.i(TAG, "----insert----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri insertUri = null;
switch (URI_MATCHER.match(uri)) {
case ALL_PERSON:
long rowId = db.insert(TABLE_NAME, "name", arg1);
Log.d(TAG, "insert:"+arg1.toString()+" Id:"+rowId);
insertUri = ContentUris.withAppendedId(uri, rowId);
// this.getContext().getContentResolver().notifyChange(insertUri, null);
break;
default:
throw new IllegalArgumentException("Unknown Uri:" + uri.toString());
}
getContext().getContentResolver().notifyChange(insertUri, null);
return insertUri;
}
// 處理查詢,返回Cursor
@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
String arg4) {
// TODO Auto-generated method stub
Log.i(TAG, "----query----");
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursir cursor = null;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON: // query all the person info
cursor = db.query(TABLE_NAME, arg1, arg2, arg3, null, null, arg4);
case PERSON: // query only one person info from a given ID
long id = ContentUris.parseId(arg0);
String where = " _id=" + id;
if( (!"".equals(arg2)) && (arg2 != null)) {
where += " and " + arg2 ;
}
cursor = db.query(TABLE_NAME, arg1, where, arg3, null, null, arg4);
default:
throw new IllegalArgumentException("unknow uri" + arg0.toString());
}
if(cursor != null) {
// 注冊該Uri對應的數據發生改變時,向客戶端發送通知
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
return cursor;
}
@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
Log.i(TAG, "----update----");
SQLiteDatabase db = dbHelper.getWritableDatabase();
int count = 0;
switch (URI_MATCHER.match(arg0)) {
case ALL_PERSON:
count = db.update(TABLE_NAME, arg1, arg2, arg3);
break;
case PERSON:
long id = ContentUris.parseId(arg0);
String where = "_id=" + id;
if( (arg2 != null) && (!"".equals(arg2))) {
where += " and " + arg2;
}
count = db.update(TABLE_NAME, arg1, where, arg3);
break;
default:
throw new IllegalArgumentException("Unknow Uri:"+arg0.toString());
}
getContext().getContentResolver().notifyChange(arg0, null);
return count;
}
}
客戶端使用Content Provider:
[java]
// 定義自己的URI
private static final String TABLE_NAME = "person";
private static final String AUTHORITY = "com.myAndroid.myProvider";
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
// 插入
ContentResolver contenResolver = MainActivity.this.getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "hello");
values.put("age", 25);
Uri resultUri = contentResolver.insert(CONTENT_URI, values);
if(ContentUris.parseId(resultUri) > 0) { Log.i(TAG, "OK"); }
// 查詢
String columns[] = new String[] ("_id", "name", "age");
Cursor cursor = contentResolver.query(CONTENT_URI, columns, null, null, "_id");
if(cursor.moveToFirst()) {
do {
Log.i(TAG, "_id:"+cursor.getInt(cursor.getColumnIndex("_id")));
Log.i(TAG, "name:"+cursor.getString(cursor.getColumnIndex("name")));
Log.i(TAG, "age:"+cursor.getInt(cursor.getColumnIndex("age")));
} while(cursor.moveToNext());
cursor.close();
}
// 刪除 ID為1的記錄
contentResolver.update(Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME+"/1"), null, null);
// 修改ID為 1 的記錄
ContentValues values = new ContentValues();
values.put("name", "world");
values.put("age", 32);
contentResolver.update(Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME+"/1"), values, null, null);
// 定義自己的URI
private static final String TABLE_NAME = "person";
private static final String AUTHORITY = "com.myAndroid.myProvider";
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
// 插入
ContentResolver contenResolver = MainActivity.this.getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "hello");
values.put("age", 25);
Uri resultUri = contentResolver.insert(CONTENT_URI, values);
if(ContentUris.parseId(resultUri) > 0) { Log.i(TAG, "OK"); }
// 查詢
String columns[] = new String[] ("_id", "name", "age");
Cursor cursor = contentResolver.query(CONTENT_URI, columns, null, null, "_id");
if(cursor.moveToFirst()) {
do {
Log.i(TAG, "_id:"+cursor.getInt(cursor.getColumnIndex("_id")));
Log.i(TAG, "name:"+cursor.getString(cursor.getColumnIndex("name")));
Log.i(TAG, "age:"+cursor.getInt(cursor.getColumnIndex("age")));
} while(cursor.moveToNext());
cursor.close();
}
// 刪除 ID為1的記錄
contentResolver.update(Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME+"/1"), null, null);
// 修改ID為 1 的記錄
ContentValues values = new ContentValues();
values.put("name", "world");
values.put("age", 32);
contentResolver.update(Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME+"/1"), values, null, null);
最後我們還需要在AndroidManifest.xml中定義我們的provider屬性:
[html]
<provider android:name=".MyProvider" android:authorities="com.myAndroid.myProvider" />
<provider android:name=".MyProvider" android:authorities="com.myAndroid.myProvider" />
我們畫常規圖形時一般用word上面自帶的插件或visio畫圖。但是我在畫linux內核文件系統結構圖、內存分配以及學習數據結構樹、圖時,發現使用Graphviz非常的方便
前面已經學了activity的一些使用,那麼下面我們進行android中基本的控件的學習和使用。 1.android中的TextView控件 新建一個項目,項目名為UIT
RT..我之前感覺Android網絡通信很神奇,Magic...最近學習了網絡知識,現在來總結一下怎麼做 好了,就拿例子來說明原理. 說下這個Demo的用處: 1.用戶可
在Android上開發一些小應用既可以積累知識又可以增加樂趣,與任務式開發不同,所以想到在And