文章目錄
- 前言
- 五、ContentProvider內容提供器
- 1.簡介
- 2.ContentProvider使用方式
- 3.ContentResolver使用方式
- 4.ContentObserver使用方式
- 5.讀取短信里的流量資訊的DEMO
- 總結
前言
明年豈無年,心事恐蹉跎,-----蘇軾《守歲》接上文,總結ContentProvider內容提供器的用法,
五、ContentProvider內容提供器
1.簡介
查閱API檔案:
public abstract class ContentProvider
extends Object implements ComponentCallbacks2
封裝資料并通過單個ContentResolver介面將其提供給應用程式, 只有在需要在多個應用程式之間共享資料時才需要內容提供者, 例如,聯系人資料由多個應用程式使用,并且必須存盤在內容提供者中, 如果您不需要在多個應用程式之間共享資料,則可以通過SQLiteDatabase直接使用資料庫,
Android四大組件之一ContentProvider,是跟資料存取有關的組件,ContenProvider 作為中間介面,本身并不直接保存資料,而是通過SQLiteOpenHelper與SQLiteDatabase間接操作底層的資料庫,完整的內容組件由三大部分組成:內容提供器ContentProvider,內容決議器ContentResolver、內容觀察器ContentObserver, 如果某個應用租序通過ContentProvider暴露了自己的資料操作介面,那么不管該應用程式是否啟動,其他應用程式都可通過該介面來操作來增刪查改該應用程式的內部資料,
ContentProvider只是一個服務端的資料存取介面,我們需要在其基礎上實作一個具體類,并重寫幾個關于資料庫管理的方法,這幾個方法就是暴露給客戶端ContentResolver來呼叫的,
onCreate: 創建資料庫并獲得資料庫連接,
query: 查詢資料,
inser: 插入資料.
update: 更新資料,
delete:洗掉資料,
geType:獲取資料型別,
2.ContentProvider使用方式
創建一個ContentProvider子類很簡單,在指定目錄,滑鼠右鍵–new–other–Content Provider,出現如下配置界面:(再次說明:以滑鼠右鍵創建的組件AS會自動在組態檔中配置)

其中唯一識別符號URI Authority代表的是該應用程式所管理的資料地址,其他應用程式可以根據這個URI識別符號來訪問該介面中的方法,其余項見名知意,
定義一個常量類,保存常用的字串,方便ContentResolver和ContentProvider使用:
public class UserInfoContent implements BaseColumns {
// 這里的名稱必須與AndroidManifest.xml里的android:authorities保持一致
public static final String AUTHORITIES = "UserInfoProvider";
// 表名
public static final String TABLE_NAME = UserDBHelper.TABLE_NAME;
// 訪問該內容提供器的URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");
// 下面是該表的各個欄位名稱
public static final String USER_NAME = "name";
public static final String USER_PHONE = "phone";
// 默認的排序方法
public static final String DEFAULT_SORT_ORDER = "_id desc";
}
然后重寫ContentProvider的幾個方法:
public class UserInfoProvider extends ContentProvider {
private final static String TAG = "UserInfoProvider";
private UserDBHelper userDB; // 宣告一個用戶資料庫的幫助器物件
public static final int USER_INFO = 1; // Uri匹配時的代號
public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static { // 往Uri匹配器中添加指定的資料路徑
uriMatcher.addURI(UserInfoContent.AUTHORITIES, "/user", USER_INFO);
}
// 根據指定條件洗掉資料
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
if (uriMatcher.match(uri) == USER_INFO) {
// 獲取SQLite資料庫的寫連接
SQLiteDatabase db = userDB.getWritableDatabase();
// 執行SQLite的洗掉操作,回傳洗掉記錄的數目
count = db.delete(UserInfoContent.TABLE_NAME, selection, selectionArgs);
db.close(); // 關閉SQLite資料庫連接
}
return count;
}
// 插入資料
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri newUri = uri;
if (uriMatcher.match(uri) == USER_INFO) {
// 獲取SQLite資料庫的寫連接
SQLiteDatabase db = userDB.getWritableDatabase();
// 向指定的表插入資料,回傳記錄的行號
long rowId = db.insert(UserInfoContent.TABLE_NAME, null, values);
if (rowId > 0) { // 判斷插入是否執行成功
// 如果添加成功,利用新記錄的行號生成新的地址
newUri = ContentUris.withAppendedId(UserInfoContent.CONTENT_URI, rowId);
// 通知監聽器,資料已經改變
getContext().getContentResolver().notifyChange(newUri, null);
}
db.close(); // 關閉SQLite資料庫連接
}
return uri;
}
// 創建ContentProvider時呼叫,可在此獲取具體的資料庫幫助器實體
@Override
public boolean onCreate() {
userDB = UserDBHelper.getInstance(getContext(), 1);
return false;
}
// 根據指定條件查詢資料庫
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
if (uriMatcher.match(uri) == USER_INFO) {
// 獲取SQLite資料庫的讀連接
SQLiteDatabase db = userDB.getReadableDatabase();
// 執行SQLite的查詢操作
cursor = db.query(UserInfoContent.TABLE_NAME,
projection, selection, selectionArgs, null, null, sortOrder);
// 設定內容決議器的監聽
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
return cursor;
}
// 獲取Uri資料的訪問型別,暫未實作
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not yet implemented");
}
// 更新資料,暫未實作
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
其中UriMatcher 是一個操作Uri的工具類,用來確認每個ContentProvider能處理的Uri,以及確定每個方法中Uri所操作的資料,
有兩個方法:
void addURI(String authority, String path, int code)
添加要匹配的URI,以及匹配此URI時回傳的標識碼,
int match(Uri uri)
嘗試匹配URL中的路徑,以此獲得指定Uri對應的標識碼,
這樣就完成了ContentProvider服務端的資料庫操作的封裝,但是,我們自己的程式幾乎不會用到ContentProvider來暴露自己的資料,反而是一些系統應用如短信,電話,照片等才需要暴露自己的資料,好家伙,合著就是一個工具人,,

3.ContentResolver使用方式
前面提到了利用ContentProvider實作服務端的資料操作封裝,如果其他應用程式想使用此程式的內部資料操作方法,就要通過內容決議器 ContentResolver來訪問,要獲取ContentResolver物件,用Context物件的getContentResolver方法即可,
查閱API檔案:
public abstract class ContentResolver
extends Object
不出意外的發現其提供了類似ContentProvider中操作資料的方法:
query: 查詢資料,
inser: 插入資料.
update: 更新資料,
delete:洗掉資料,
geType:獲取資料型別,
毫無疑問,使用ContentResolver這些一一對應的方法便能間接的呼叫ContentProvider中的對應的方法,其中ContentResolver中的方法引數Uri便是在創建ContentProvider時指定的唯一識別符號,
4.ContentObserver使用方式
ContentObserver內容觀察器采用主動查詢方式,有查詢就有資料,沒有查詢就沒資料,如果要實時獲取最新的資料,就要給ContentResolver注冊一個觀察器,ContentResolver中的資料一旦發生變化,觀察器就會回呼相應的方法,
查閱API檔案:
public abstract class ContentObserver
extends Object
使用方法如下:
1.創建一個繼承自ContentObserver的子類,然后重寫其onChange方法實作回呼,
2.為ContentResolver注冊一個上面的觀察器,即呼叫其
void registerContentObserver (Uri uri,boolean notifyForDescendants, ContentObserver observer)方法,
3.監聽完畢,準備注銷ContentObserver,可以呼叫
void unregisterContentObserver(ContentObserver observer),
5.讀取短信里的流量資訊的DEMO
實作一個獲取短信里的流量資訊的DEMO
其中發送短信方法如下:
// 短信發送事件
private String SENT_SMS_ACTION = "SENT_SMS_ACTION";
// 短信接收事件
private String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION";
public void sendSmsAuto(String phoneNumber, String message) {
// 以下指定短信發送事件的詳細資訊
Intent sentIntent = new Intent(SENT_SMS_ACTION);
sentIntent.putExtra("phone", phoneNumber);
sentIntent.putExtra("message", message);
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// 以下指定短信接收事件的詳細資訊
Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION);
deliverIntent.putExtra("phone", phoneNumber);
deliverIntent.putExtra("message", message);
PendingIntent deliverPI = PendingIntent.getBroadcast(this, 1, deliverIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// 獲取默認的短信管理器
SmsManager smsManager = SmsManager.getDefault();
// 開始發送短信內容,要確保打開發送短信的完全權限,不是那種還需提示的不完整權限
smsManager.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI);
}
定義一個內容觀察器子類,重寫onChange方法:
// 定義一個短信獲取的觀察器
private static class SmsGetObserver extends ContentObserver {
private Context mContext; // 宣告一個背景關系物件
public SmsGetObserver(Context context, Handler handler) {
super(handler);
mContext = context;
}
// 觀察到短信的內容提供器發生變化時觸發
public void onChange(boolean selfChange) {
String sender = "", content = "";
// 構建一個查詢短信的條件陳述句,這里使用移動號碼測驗,故而查找10086發來的短信
String selection = String.format("address='10086' and date>%d", System.currentTimeMillis() - 1000 * 60 * 60);
// 通過內容決議器獲取符合條件的結果集游標
Cursor cursor = mContext.getContentResolver().query(
mSmsUri, mSmsColumn, selection, null, " date desc");
// 回圈取出游標所指向的所有短信記錄
while (cursor.moveToNext()) {
sender = cursor.getString(0);
content = cursor.getString(1);
break;
}
cursor.close(); // 關閉資料庫游標
mCheckResult = String.format("發送號碼:%s\n短信內容:%s", sender, content);
// 依次決議流量校準短信里面的各項流量數值,并拼接流量校準的結果字串
String flow = String.format("流量校準結果如下:\n\t總流量為:%s\n\t已使用:%s" +
"\n\t剩余:%s", findFlow(content, "總流量為", ","),
findFlow(content, "已使用", "MB"), findFlow(content, "剩余", "MB"));
if (tv_check_flow != null) { // 離開該頁面時就不再顯示流量資訊
// 把流量校準結果顯示到文本視圖tv_check_flow上面
tv_check_flow.setText(flow);
}
super.onChange(selfChange);
}
}
其中的findFlow方法:
// 決議流量校準短信里面的流量數值
private static String findFlow(String sms, String begin, String end) {
int begin_pos = sms.indexOf(begin);
if (begin_pos < 0) {
return "未獲取";
}
String sub_sms = sms.substring(begin_pos);
int end_pos = sub_sms.indexOf(end);
if (end_pos < 0) {
return "未獲取";
}
if (end.equals(",")) {
return sub_sms.substring(begin.length(), end_pos);
} else {
return sub_sms.substring(begin.length(), end_pos + end.length());
}
}
初始化內容觀察器:
private static TextView tv_check_flow;
private static String mCheckResult;
private Handler mHandler = new Handler(); // 宣告一個處理器物件
private SmsGetObserver mObserver; // 宣告一個短信獲取的觀察器物件
private static Uri mSmsUri; // 宣告一個系統短信提供器的Uri物件
private static String[] mSmsColumn; // 宣告一個短信記錄的欄位陣列
// 初始化短信觀察器
private void initSmsObserver() {
//mSmsUri = Uri.parse("content://sms/inbox");
//Android5.0之后似乎無法單獨觀察某個信箱,只能監控整個短信
mSmsUri = Uri.parse("content://sms");
mSmsColumn = new String[]{"address", "body", "date"};
// 創建一個短信觀察器物件
mObserver = new SmsGetObserver(this, mHandler);
// 給指定Uri注冊內容觀察器,一旦Uri內部發生資料變化,就觸發觀察器的onChange方法
getContentResolver().registerContentObserver(mSmsUri, true, mObserver);
}
其他的頁面控制元件系結很簡單,就不羅列了,
最后別忘了在組態檔里宣告短信的權限:
<!-- 短信 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
總結
復習了Android第二大組件ContentProvider,還有廣播和服務兩個組件,不知道哪一天才復習得到了,
又是一年結束時,時間是真的快,想回首卻發現沒有什么可以回首的事情,身邊的同齡人大多數已經得到了牛逼的作業,我是真的羨慕,

希望新的一年,自己能努力向上,更加豐富知識與技能,也祝大家在新的一年闔家團圓,展翅高飛,一飛沖天,平步青云,漲薪加工資,
最后問大家一個問題:不論什么情況,有什么東西能永遠伴隨我們?
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/258977.html
標籤:其他
上一篇:app自動化
下一篇:#Android Day1
