小談Android四大元件之ContentProvider
內容提供者的作用
1.應用程式建立的資料庫預設都是私有的,別的應用程式不可以訪問裡面的資料.
2.如果需要把自己應用程式私有的資料庫暴露給別的使用者增刪改查,就需要使用內容提供者.
3.作用: 一個應用程式訪問另外一個應用程式在硬碟上儲存的資料
注:ContentProvider可以理解為:應用程式資料庫的後門.
編寫ContentProvider的流程
建立一個自定義內容提供者的Android專案:
1.寫一個類繼承ContentProvider, 重寫的所有方法(主要是增刪改查)預設都是空實現.
2.在清單檔案下宣告provider. 必須指定主機名!!!
<provider
android:name="內容提供者的包名"
android:authorities="自定義主機名"><provider/>
注:主機名也就是對外提供的uri,可以任意自定義,一般的規則是應用程式包名.
3.在內容提供者程式碼內部定義UriMatcher -用於判斷uri是否匹配
//使用靜態變數和程式碼塊,便於自動載入. static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { mUriMatcher.addURI("在清單檔案裡面定義的authorities", "自定義匹配字串", 成功返回的標識); } 注:1.自定義匹配字串規則: 一般使用被操作的資料庫中的表名. 2.成功返回標識:SUCCESS, ERROR
4.在內容提供者執行增刪改查方法時,判斷uri是否合法
int code=matcher.match(uri);
if(code==SUCCESS){
//資料庫增刪改查的邏輯
}else{
throw new IllegalArgumentException("口令不正確,")
}
5.例項化資料
MyDBHelper helper = new MyDBHelper(this);
helper.getWritableDatabase();
再建立另一個Android專案訪問內容提供者:
在MainActivity裡寫對資料庫增刪改查的邏輯方法
1.建立內容提供者解析器 ContentResolver resolver=上下文.getContentResolver(); 2.定義要訪問的Uri路徑 Uri uri = Uri.parse("content://自定義主機名/自定義匹配字串") //注意:content://是標準寫法 3.通過內容解析器執行操作:增,刪,改,查 如新增:resolver.insert(uri, values);
小結:
1.寫一個類繼承ContentProvider,實現增刪改查的方法
2.在清單檔案中配置內容提供者,指定android:authorities="com.yashiro.database"
3.在內容提供者程式碼的內部,宣告uriMatcher
4.通過uriMatcher檢查uri的路徑是否正確
5.在另外一個應用程式裡面,通過contentResolver 增刪改查
案例
<案例一: 用內容提供者作業系統簡訊>
思路: 直接訪問系統簡訊應用的內容提供者,實現對簡訊應用資料庫的增加和刪除操作.
步驟:
1.通過查詢系統原始碼,可以確定簡訊息內容提供者的Uri 應該為:content://sms
2.檢視Android 模擬器下的/data/data/com.android.providers.telephony/databases/目錄,檢視其mmssms.db檔案,確定sms表儲存的資料格式:
*其中,address 儲存的是聯絡人號碼,date 是傳送日期,type 對應簡訊的型別(傳送是1/接收是2),body是簡訊的主體內容。
3.建立一個Android專案,在MainActivity裡寫刪除和新增簡訊的業務邏輯
1).配置清單檔案,新增簡訊的讀寫許可權
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
2).實現新增和刪除業務邏輯
/**
* 利用內容提供者,新增簡訊
*
* @param view
*/
public void add(View view) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
values.put("address", "110");
values.put("type", 1);
values.put("date", System.currentTimeMillis());
values.put("body", "小子誒....你小心了,我們盯上你le~~");
resolver.insert(uri, values);
Toast.makeText(this, "新增成功", 0).show();
}
/**
* 利用內容提供者,刪除簡訊
*
* @param view
*/
public void delete(View view) {
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
resolver.delete(uri, "address=?", new String[] { "110" });
Toast.makeText(this, "刪除成功", 0).show();
}
<案例二: 用內容提供者作業系統聯絡人>
開發現狀:
現在很多App 都可以對系統聯絡人進行操作,比如微信就可以直接將號碼新增到系統聯絡人中,比如QQ 可以關聯/備份/恢復系統聯絡人。
完成這些操作,需要能夠訪問並修改系統聯絡人的資料庫.
思路: 直接訪問系統聯絡人應用的內容提供者,實現對聯絡人資料的增加和刪除操作.
步驟:
1.開啟Android 原始碼,檢視packages\providers\路徑下的檔案,其中ContactsProvider 就是聯絡人的內容提供者.找到清單檔案,發現內容提供者的類名為ContactsProvider2.java.
2.根據原碼確定Uri資訊如下:
操作raw_contacts 表的Uri:
content://com.android.contacts/raw_contacts
操作data 表的Uri:
content://com.android.contacts/data
3.注意事項:
*由於contacts2.db 資料庫使用了檢視,所以操作資料庫表時,看到的表字段名稱和真實操作的有所不同。
*比如:data 表在查詢的時候沒有mimetype_id 欄位,取代的是mimetype 欄位。
*使用指南:
1.查詢raw_contact表 獲取聯絡人的contact_id.
2.根據contact_id查詢data表,獲取聯絡人的資料
3.根據mimetype確定資料的型別
4.查詢手機聯絡人
步驟:
1).建立一個專案,配置清單檔案,新增讀取聯絡人的許可權
android:readPermission="android.permission.READ_CONTACTS"
2).建立一個聯絡人bean,有資訊name,phone,Email,QQ.
3).建立一個聯絡人工具類,獲取所有的聯絡人資訊,放到一個List集合中返回.
public class ContactInfoUtils {
/**
* 獲取所有的聯絡人資訊
*
* @param context
* 上下文
* @return
*/
public static List<ContactInfo> getAllContactInfos(Context context) {
List<ContactInfo> infos = new ArrayList<ContactInfo>();
ContentResolver resolver = context.getContentResolver();
// 查詢raw_contact表
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
null, null, null);
//根據遊標判斷是否有資料
while (cursor.moveToNext()) {
String id = cursor.getString(0);
System.out.println("Id:" + id);
if (id != null) {
ContactInfo info = new ContactInfo();
// 查詢data表
Cursor datacursor = resolver.query(datauri, new String[] {
"data1", "mimetype" }, "raw_contact_id=?",
new String[] { id }, null);
//根據遊標判斷是否有資料
while (datacursor.moveToNext()) {
String data1 = datacursor.getString(0);
String mimetype = datacursor.getString(1);
if ("vnd.android.cursor.item/name".equals(mimetype)) {
info.setName(data1);
} else if ("vnd.android.cursor.item/im".equals(mimetype)) {
info.setQq(data1);
} else if ("vnd.android.cursor.item/email_v2"
.equals(mimetype)) {
info.setEmail(data1);
} else if ("vnd.android.cursor.item/phone_v2"
.equals(mimetype)) {
info.setPhone(data1);
}
}
datacursor.close();
infos.add(info);
}
}
cursor.close();
return infos;
}
}
4).在MainActiviry裡獲取聯絡人資訊的集合,並顯示到介面上
infos = ContactInfoUtils.getAllContactInfos(this);
ListView lv = (ListView) findViewById(R.id.lv);
//設定介面卡
lv.setAdapter(new BaseAdapter() {
@Override
public int getCount() {
return infos.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = new TextView(MainActivity.this);
tv.setText(infos.get(position).toString());
return tv;
}
...
5.新增手機聯絡人
步驟:
1).新建一個Android專案,配置清單檔案
<!-- 讀取聯絡人 寫入聯絡人 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
2).設定佈局檔案,獲取使用者錄入的姓名,電話,郵箱資訊
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="姓名" />
<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="電話" />
<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="郵箱" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="save"
android:text="儲存資料" />'
3).在MainActivity裡寫實現新增的業務邏輯
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);
//關心控制元件
et_name = (EditText) findViewById(R.id.et_name);
et_phone = (EditText) findViewById(R.id.et_phone);
et_email = (EditText) findViewById(R.id.et_email);
}
public void save(View view) {
//獲取輸入資訊
String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();
String email = et_email.getText().toString().trim();
//定義要訪問的Uri路徑
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
//獲取遊標
Cursor cursor = getContentResolver().query(uri, new String[] { "_id" },
null, null, "_id desc");
//遊標初始化到開始位置
cursor.moveToFirst();
int _id = cursor.getInt(0);
int newId = _id + 1;
//文字資料
ContentValues values = new ContentValues();
values.put("contact_id", newId);
//根據uri新增資料
getContentResolver().insert(uri, values);
// 往data表裡面新增資料.
// 1.新增name
ContentValues nameValue = new ContentValues();
nameValue.put("raw_contact_id", newId);
nameValue.put("data1", name);
nameValue.put("mimetype", "vnd.android.cursor.item/name");
getContentResolver().insert(datauri, nameValue);
// 2.新增email
ContentValues emailValue = new ContentValues();
emailValue.put("raw_contact_id", newId);
emailValue.put("data1", email);
emailValue.put("mimetype", "vnd.android.cursor.item/email_v2");
getContentResolver().insert(datauri, emailValue);
// 3.新增phone
ContentValues phoneValue = new ContentValues();
phoneValue.put("raw_contact_id", newId);
phoneValue.put("data1", phone);
phoneValue.put("mimetype", "vnd.android.cursor.item/phone_v2");
getContentResolver().insert(datauri, phoneValue);
Toast.makeText(this, "新增資料成功", 0).show();
}
}
內容觀察者 ContentObserver
•觀察資料庫內容是否發生改變,如果改變,通知觀察者
使用步驟:
1.在內容提供者ContentProvider的增刪改查方法中,增加通知的方法:
getContext().getContentResolver().notifyChange(uri, null);
2.在觀察者類註冊觀察者,監控內容提供者的方法
//定義觀察的uri
Uri uri = Uri.parse("content://被觀察的主機名/資料庫表名");
//註冊觀察者
getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
System.out.println("我是觀察者,我發現銀行的資料庫變化了.");
super.onChange(selfChange);
}
});
小結:
內容觀察者一般配合內容提供者一起使用.讓內容提供者即向另外的一個程式暴露了私有的資料庫,又將資料庫的變化情況告訴給了觀察者.