本篇文章主要介紹以下幾個(gè)知識(shí)點(diǎn):
- Android 運(yùn)行時(shí)權(quán)限;
- 內(nèi)容提供器

7.1 運(yùn)行時(shí)權(quán)限
在介紹內(nèi)容提供器之前先來(lái)了解了解 Android 運(yùn)行時(shí)權(quán)限。
7.1.1 Android 權(quán)限機(jī)制
Android 把所有的權(quán)限歸成兩類:普通權(quán)限和危險(xiǎn)權(quán)限。
普通權(quán)限
? 不會(huì)直接威脅到用戶的安全和隱私的權(quán)限,對(duì)于這部分的權(quán)限申請(qǐng),系統(tǒng)會(huì)自動(dòng)幫我們進(jìn)行授權(quán)。危險(xiǎn)權(quán)限
? 可能觸及用戶隱私,或?qū)υO(shè)備安全性造成影響的權(quán)限,如獲取聯(lián)系人信息、地理位置等,對(duì)于這部分的權(quán)限申請(qǐng),必須由用戶手動(dòng)點(diǎn)擊授權(quán)才可以,否則程序無(wú)法使用相應(yīng)的功能。
下面列出了 Android 中所有的危險(xiǎn)權(quán)限:
上表中每個(gè)危險(xiǎn)權(quán)限都屬于一個(gè)權(quán)限組,若用戶同意授權(quán)某個(gè)權(quán)限名,那么該權(quán)限所對(duì)應(yīng)的權(quán)限組中的其他權(quán)限也會(huì)同時(shí)跟著被授權(quán)。
注:訪問(wèn) http://developer.android.com/reference/android/Manifest.permission.html 可以查看完整的權(quán)限列表。
7.1.2 在運(yùn)行程序時(shí)申請(qǐng)權(quán)限
接下來(lái)通過(guò)個(gè)例子來(lái)介紹如何在運(yùn)行程序時(shí)申請(qǐng)權(quán)限。
此次的例子是通過(guò)點(diǎn)擊界面上的一個(gè)按鈕來(lái)直接撥打電話。撥打電話時(shí)需要聲明上節(jié)表中的 CALL_PHONE 這個(gè)危險(xiǎn)權(quán)限。
首先,在 AndroidManifest.xml 文件中聲明權(quán)限:
<uses-permission android:name="android.permission.CALL_PHONE" />
然后在布局 activity_call_phone.xml 中添加個(gè)按鈕:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/btn_call_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="點(diǎn)擊撥打電話"/>
</RelativeLayout>
接著修改 activity 中代碼如下:
public class CallPhoneActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_phone);
Button btn_call_phone = (Button) findViewById(R.id.btn_call_phone);
btn_call_phone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
// Intent.ACTION_CALL 是系統(tǒng)內(nèi)置的打電話動(dòng)作(而Intent.ACTION_DIAL指打開(kāi)撥號(hào)界面,不需要聲明權(quán)限)
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (SecurityException e){
e.printStackTrace();
}
}
});
}
}
在 Android 6.0 前,撥打電話功能的實(shí)現(xiàn)很簡(jiǎn)單,上面代碼就把撥打電話功能實(shí)現(xiàn)了,在低于 Android 6.0 系統(tǒng)的手機(jī)上可以正常運(yùn)行,但在 Android 6.0 或更高版本系統(tǒng)的手機(jī)上運(yùn)行則無(wú)效,會(huì)拋出“Permission Denial”的異常。
接下來(lái),修改 activity 中的代碼,修復(fù)上面的問(wèn)題,如下:
public class CallPhoneActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_phone);
Button btn_call_phone = (Button) findViewById(R.id.btn_call_phone);
btn_call_phone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 1. 判斷用戶是否授權(quán),借助 ContextCompat.checkSelfPermission() 方法
// ContextCompat.checkSelfPermission() 方法接收兩參數(shù):context 和權(quán)限名
if (ActivityCompat.checkSelfPermission(CallPhoneActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 3. 若沒(méi)授權(quán),則調(diào)用 ActivityCompat.requestPermissions()方法來(lái)向用戶授權(quán)
// 其三個(gè)參數(shù):Activity實(shí)例、String數(shù)組(放權(quán)限名)、請(qǐng)求碼(只要唯一就行了,這里傳1)
ActivityCompat.requestPermissions(CallPhoneActivity.this,
new String[]{Manifest.permission.CALL_PHONE}, 1);
// 4. 調(diào)用完 requestPermissions()方法后,會(huì)彈出一個(gè)權(quán)限申請(qǐng)對(duì)話框,供用戶選擇,
// 最后回調(diào) onRequestPermissionsResult()方法
}else {
// 2. 若已授權(quán),則直接執(zhí)行撥打電話的邏輯
call();
}
}
});
}
/**
* 撥打電話方法
*/
private void call() {
try {
// Intent.ACTION_CALL 是系統(tǒng)內(nèi)置的打電話動(dòng)作
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}catch (SecurityException e){
e.printStackTrace();
}
}
/**
* 無(wú)論用戶是否同意權(quán)限申請(qǐng),都會(huì)回調(diào)此方法
* @param requestCode
* @param permissions
* @param grantResults 授權(quán)的結(jié)果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
call();
}else {
ToastUtils.showShort("你拒絕了權(quán)限請(qǐng)求");
}
break;
default:
break;
}
}
}
這樣就完成了權(quán)限申請(qǐng)的代碼編寫(xiě)了,具體過(guò)程看代碼注釋,現(xiàn)在運(yùn)行一下程序,效果如下:

好了,關(guān)于運(yùn)行時(shí)權(quán)限的內(nèi)容先介紹到這,下面進(jìn)入主題——內(nèi)容提供器。
7.2 內(nèi)容提供器
內(nèi)容提供器(Content Provider)主要用于在不同的應(yīng)用程序之間實(shí)現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機(jī)制,允許一個(gè)程序訪問(wèn)到另一個(gè)程序中的數(shù)據(jù),同時(shí)還能保證被訪問(wèn)數(shù)據(jù)的安全性。目前,使用內(nèi)容提供器是 Android 實(shí)現(xiàn)跨程序共享數(shù)據(jù)的標(biāo)準(zhǔn)方式。
不同于文件存儲(chǔ)和 SharedPreferences 存儲(chǔ)中的兩種全局可讀寫(xiě)操作模式,內(nèi)容提供器可以選擇只對(duì)哪一部分?jǐn)?shù)據(jù)進(jìn)行共享,從而保證程序中的隱私數(shù)據(jù)不會(huì)有泄露的風(fēng)險(xiǎn)。
7.2.1 訪問(wèn)其他程序中的數(shù)據(jù)
內(nèi)容提供器的用法有兩種:
- 使用現(xiàn)有的內(nèi)容提供器來(lái)讀取和操作相應(yīng)程序中的數(shù)據(jù)
- 創(chuàng)建自己的內(nèi)容提供器給我們程序的數(shù)據(jù)提供外部訪問(wèn)接口
7.2.1.1 ContentResolver 的基本用法
每一個(gè)應(yīng)用程序,借助 ContenResolver 類才能訪問(wèn)內(nèi)容提供器中共享的數(shù)據(jù),該類可通過(guò) Context 中的 getContentResolver() 方法獲取實(shí)例,該類提供的一系列對(duì)數(shù)據(jù)的增刪查改方法不同于 SQLiteDataBase,是不接收表名參數(shù)的,而是用參數(shù)Uri(稱為內(nèi)容 URI) 代替。
內(nèi)容 URI 給內(nèi)容提供器中的數(shù)據(jù)建立了唯一標(biāo)識(shí)符,它由 authority(區(qū)分不同的應(yīng)用程序)和 path(區(qū)分不同的表)組成。如某個(gè)程序包名為 com.example.app 且存在兩張表 table1 和 table2 ,其標(biāo)準(zhǔn)格式寫(xiě)法如下:
在得到了 內(nèi)容 URI 字符串后,需要調(diào)用 Uri.parse() 方法把它解析成 Uri 對(duì)象才可作為參數(shù)傳入,代碼如下:
Uri uri = Uri.parse("content://com.example.app.provider/table1")
- 查詢操作
現(xiàn)在就可以用這個(gè) Uri 對(duì)象來(lái)查詢 table1 表中的數(shù)據(jù)了,代碼如下:
Curson curson = getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
下表對(duì)上面的參數(shù)進(jìn)行了詳細(xì)的解釋:
查詢完后返回一個(gè) Curson 對(duì)象,讀取代碼如下:
// 通過(guò)移動(dòng)游標(biāo)的位置來(lái)遍歷Cursor的所有行
if (cursor != null){
while (cursor.moveToNext()){
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
}
cursor.close();
}
- 添加操作
向 table1 表中添加一條數(shù)據(jù):
// 將待添加的數(shù)據(jù)組裝到 ContentValues 中
ContentValues values = new ContentValues();
values.put("column1","text");
values.put("column2",1);
// 調(diào)用 insert() 方法添加數(shù)據(jù)
getContentResolver().insert(uri, values);
- 更新操作
更新數(shù)據(jù),把 column1 的值清空:
ContentValues values = new ContentValues();
// 清空
values.put("column1","");
// 調(diào)用 update() 方法更新數(shù)據(jù)
getContentResolver().update(uri, values, "column1 = ? and column2 = ?",new String[]{"text","1"});
- 刪除操作
調(diào)用 ContentResolver 的 delete() 刪除數(shù)據(jù):
getContentResolver().delete(uri, "column2 = ?", new String[]{"1"});
以上就是 ContentResolver 中的增刪查改方法。
7.2.1.2 讀取系統(tǒng)聯(lián)系人
接下來(lái)舉個(gè)例子來(lái)加深學(xué)習(xí):利用內(nèi)容提供器來(lái)讀取系統(tǒng)聯(lián)系人。
由于模擬器上木有聯(lián)系人,先向模擬器中創(chuàng)建幾個(gè)聯(lián)系人。聯(lián)系人準(zhǔn)備好后,首先在項(xiàng)目的 AndroidManifest.xml 中聲明讀取聯(lián)系人的權(quán)限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
簡(jiǎn)單起見(jiàn),在布局 activity_read_contact.xml 中放一個(gè) ListView 來(lái)顯示讀取的聯(lián)系人信息:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/contacts_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</RelativeLayout>
接著修改 activity 中的代碼如下:
public class ReadContactActivity extends AppCompatActivity {
ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_read_contact);
// 獲取 listView 的實(shí)例,設(shè)置適配器
ListView contacts_view = (ListView) findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,contactsList);
contacts_view.setAdapter(adapter);
// 判斷是否授權(quán)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS},1);
}else {
readContacts();
}
}
/**
* 讀取聯(lián)系人方法
*/
private void readContacts() {
Cursor cursor = null;
try{
// 查詢聯(lián)系人數(shù)據(jù)
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null);
if (cursor != null){
while (cursor.moveToNext()){
// 獲取聯(lián)系人姓名
String name = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
// 獲取聯(lián)系人號(hào)碼
String number = cursor.getString(cursor.getColumnIndex
(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(name + "\n" + number);
}
adapter.notifyDataSetChanged();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (cursor != null){
cursor.close();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
readContacts();
}else {
ToastUtils.showShort("你拒絕了權(quán)限請(qǐng)求");
}
break;
default:
break;
}
}
}
運(yùn)行程序,效果如下:

7.2.2 創(chuàng)建自己的內(nèi)容提供器
上一小節(jié),介紹了如何訪問(wèn)其他應(yīng)用程序得數(shù)據(jù),其思路是獲取應(yīng)用程序的內(nèi)容 URI 后借助 ContentResolver 進(jìn)行 CRUD 操作就行了。接下來(lái)介紹創(chuàng)建自己的內(nèi)容提供器。
7.2.2.1 創(chuàng)建步驟
首先,新建一個(gè)類去繼承 ContentProvider,重寫(xiě)它的6個(gè)抽象方法,如下:
/**
* 自己的內(nèi)容提供器
* Created by KXwon on 2016/12/18.
*/
public class MyProvider extends ContentProvider{
// 初始化內(nèi)容提供器的時(shí)候調(diào)用,返回true表示成功,false失敗
@Override
public boolean onCreate() {
return false;
}
// 從內(nèi)容提供器中查詢數(shù)據(jù)
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
return null;
}
// 向內(nèi)容提供器中添加一條數(shù)據(jù)
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
return null;
}
// 從內(nèi)容提供器中刪除數(shù)據(jù)
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}
// 更新內(nèi)容提供器中已有的數(shù)據(jù)
@Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
// 根據(jù)傳入的內(nèi)容 URI 來(lái)返回 MIME 類型
@Override
public String getType(Uri uri) {
return null;
}
}
之前提到一個(gè)標(biāo)準(zhǔn)的內(nèi)容 URI 寫(xiě)法是這樣的:
// 表示調(diào)用方期望訪問(wèn)的是 com.example.app 這個(gè)應(yīng)用的 table1 表中的數(shù)據(jù)
content://com.example.app.provider/table1
除此之外,還可以在內(nèi)容 URI 后面加一個(gè)id,如下:
// 表示調(diào)用方期望訪問(wèn)的是 com.example.app 這個(gè)應(yīng)用的 table1 表中 id 為 1 的數(shù)據(jù)
content://com.example.app.provider/table1/1
內(nèi)容 URI 的格式主要就只有以上兩種,可以通過(guò)用通配符的方式來(lái)匹配這兩種內(nèi)容 URI,規(guī)則如下:
?(1) *:表示匹配任意長(zhǎng)度的任意字符。
?(2)# :表示匹配任意長(zhǎng)度的數(shù)字
所以,一個(gè)能夠匹配任意表的內(nèi)容 URI 格式可寫(xiě)成:
content://com.example.app.provider/*
一個(gè)能夠匹配 table1 表中任意一行數(shù)據(jù)的內(nèi)容 URI 格式可寫(xiě)成:
content://com.example.app.provider/table1/#
然后,再借助 UriMatcher 這個(gè)類實(shí)現(xiàn)內(nèi)容 URI 功能,修改 MyProvider 類如下:
public class MyProvider extends ContentProvider{
public static final int TABLE1_DIR = 0; //訪問(wèn) table1 表中的所有數(shù)據(jù)
public static final int TABLE1_ITEM = 1;//訪問(wèn) table1 表中的單條數(shù)據(jù)
public static final int TABLE2_DIR = 3; //訪問(wèn) table2 表中的所有數(shù)據(jù)
public static final int TABLE2_ITEM = 4;//訪問(wèn) table2 表中的單條數(shù)據(jù)
private static UriMatcher uriMatcher;
static {
// 創(chuàng)建 UriMatcher 實(shí)例
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 調(diào)用 addURI() 方法,此方法接收3個(gè)參數(shù):authority、path、自定義代碼
uriMatcher.addURI("com.example.app.provider","table1",TABLE1_DIR);
uriMatcher.addURI("com.example.app.provider","table1/#",TABLE1_ITEM);
uriMatcher.addURI("com.example.app.provider","table2",TABLE2_DIR);
uriMatcher.addURI("com.example.app.provider","table2/#",TABLE2_ITEM);
}
// 從內(nèi)容提供器中查詢數(shù)據(jù)
@Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
switch (uriMatcher.match(uri)){
case TABLE1_DIR:
// 查詢 table1 表中的所有數(shù)據(jù)
break;
case TABLE1_ITEM:
// 查詢 table1 表中的單條數(shù)據(jù)
break;
case TABLE2_DIR:
// 查詢 table2 表中的所有數(shù)據(jù)
break;
case TABLE2_ITEM:
// 查詢 table2 表中的單條數(shù)據(jù)
break;
default:
break;
}
. . .
}
. . .
}
上述代碼只是以 query() 方法做了個(gè)示范,其他3個(gè)增刪改的方法也差不多。
而 getType() 方法,是所有的內(nèi)容提供器都必須提供的一個(gè)方法,用于獲取 Uri 對(duì)象所對(duì)應(yīng)的 MIME 類型。MIME 字符串主要由 3 部分組成,并有如下格式規(guī)定:
?(1) 必須由 vnd 開(kāi)頭
?(2)若內(nèi)容 URI 以路徑結(jié)尾,則后接 android.cursor.dir/,若內(nèi)容 URI 以 id 結(jié)尾,則后接 android.cursor.item/
?(3)最后接上 vnd.<authority>.<path>
最后,實(shí)現(xiàn) getType() 方法中的邏輯,完善 MyProvide r類如下:
public class MyProvider extends ContentProvider{
. . .
// 根據(jù)傳入的內(nèi)容 URI 來(lái)返回 MIME 類型
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
default:
break;
}
return null;
}
}
到這里,一個(gè)完整的內(nèi)容提供器就創(chuàng)建完成了。
7.2.2.1 實(shí)現(xiàn)跨程序數(shù)據(jù)共享
好了,例子來(lái)了。上一章項(xiàng)目中建立了 BookStore.db 數(shù)據(jù)庫(kù),里面有 book 表和 category 表這兩張表,為簡(jiǎn)單起見(jiàn),在上一章的基礎(chǔ)上繼續(xù)開(kāi)發(fā)。
首先,為項(xiàng)目創(chuàng)建個(gè)內(nèi)容提供器,在 Android Studio 中右擊 com.wonderful.myfirstcode.chapter7.provider包(你項(xiàng)目所在的包名)→New→Other→Content Provider,會(huì)彈出如下窗口:
可以看到,這里把內(nèi)容提供器命名為 DatabaseProvider,authority 指定為項(xiàng)目包名,Exported 表示是否允許外部程序訪問(wèn)我們的內(nèi)容提供器,Enabled 表示是否啟用這個(gè)內(nèi)容提供器。兩個(gè)勾選點(diǎn)擊 Finish 完成創(chuàng)建。
接著修改 DatebaseProvider 中的代碼如下:
public class DataBaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0; //訪問(wèn) book 表中的所有數(shù)據(jù)
public static final int BOOK_ITEM = 1;//訪問(wèn) book 表中的單條數(shù)據(jù)
public static final int CATEGORY_DIR = 3;
public static final int CATEGORY_ITEM = 4;
public static final String AUTHORITY = "com.wonderful.myfirstcode.chapter7.provider";
private static UriMatcher uriMatcher;
private MyDatabaseHelper dbHelper;
static {
// 創(chuàng)建 UriMatcher 實(shí)例
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 調(diào)用 addURI() 方法,此方法接收3個(gè)參數(shù):authority、path、自定義代碼
uriMatcher.addURI(AUTHORITY,"book",BOOK_DIR);
uriMatcher.addURI(AUTHORITY,"book/#",BOOK_ITEM);
uriMatcher.addURI(AUTHORITY,"category",CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY,"category/#",CATEGORY_ITEM);
}
/**
* 初始化內(nèi)容提供器
*/
@Override
public boolean onCreate() {
// 創(chuàng)建 MyDatabaseHelper 實(shí)例
dbHelper = new MyDatabaseHelper(getContext(),"BookStore.db",null,2);
// 返回true表示完成了創(chuàng)建或升級(jí)數(shù)據(jù)庫(kù)
return true;
}
/**
* 查詢數(shù)據(jù)
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
// 查詢 book 表中的所有數(shù)據(jù)
cursor = db.query("book",projection,selection,selectionArgs,
null,null,sortOrder);
break;
case BOOK_ITEM:
// 查詢 book 表中的單條數(shù)據(jù)
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book",projection,"id = ?",new String[]{bookId},
null,null,sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category",projection,selection,selectionArgs,
null,null,sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category",projection,"id = ?",new String[]
{categoryId}, null,null,sortOrder);
break;
default:
break;
}
return cursor;
}
/**
* 添加數(shù)據(jù)
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book",null,values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category",null,values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category" +
newCategoryId);
break;
default:
break;
}
return uriReturn;
}
/**
* 更新數(shù)據(jù)
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
int updatedRows = 0;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
updatedRows = db.update("book",values,selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updatedRows = db.update("book",values,"id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
updatedRows = db.update("category",values,selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updatedRows = db.update("category",values,"id = ?",new String[]
{categoryId});
break;
default:
break;
}
return updatedRows;
}
/**
* 刪除數(shù)據(jù)
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
int deletedRows = 0;
switch (uriMatcher.match(uri)){
case BOOK_DIR:
deletedRows = db.delete("book",selection,selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deletedRows = db.delete("book","id = ?",new String[]{bookId});
break;
case CATEGORY_DIR:
deletedRows = db.delete("category",selection,selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deletedRows = db.delete("category","id = ?",new String[]
{categoryId});
break;
default:
break;
}
return deletedRows;
}
/**
* 獲取 Uri 對(duì)象所對(duì)應(yīng)的 MIME 類型
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.wonderful.myfirstcode." +
"chapter7.provider.category";
}
return null;
}
}
另外,內(nèi)容提供器一定要在 AndroidManifest.xml 中注冊(cè)才可使用。不過(guò)剛用 AS 創(chuàng)建的內(nèi)容提供器已經(jīng)幫我們自動(dòng)注冊(cè)完成了,如下:
<provider
android:name=".chapter7.provider.DataBaseProvider"
android:authorities="com.wonderful.myfirstcode.chapter7.provider"
android:enabled="true"
android:exported="true">
</provider>
現(xiàn)在,內(nèi)容提供器已經(jīng)創(chuàng)建好了,接下來(lái)新建一個(gè)項(xiàng)目 ProviderTest 來(lái)訪問(wèn)上面程序中的數(shù)據(jù),記得清空上一章項(xiàng)目里的數(shù)據(jù),以防造成干涉。
先來(lái)編寫(xiě)下布局文件 activity_main.xml,添加4個(gè)按鈕來(lái)增刪查改,如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<Button
android:id="@+id/btn_add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加數(shù)據(jù)"/>
<Button
android:id="@+id/btn_query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查詢數(shù)據(jù)"/>
<Button
android:id="@+id/btn_update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="更新數(shù)據(jù)"/>
<Button
android:id="@+id/btn_delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="刪除數(shù)據(jù)"/>
</LinearLayout>
然后修改 MainActivity 中的代碼如下:
public class MainActivity extends AppCompatActivity {
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 添加數(shù)據(jù)
Button btn_add_data = (Button) findViewById(R.id.btn_add_data);
btn_add_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book");
ContentValues values = new ContentValues();
values.put("name", "第一行代碼");
values.put("author", "郭霖");
values.put("pages", 1000);
values.put("price", 66.66);
Uri newUri = getContentResolver().insert(uri,values);
newId = newUri.getPathSegments().get(1);
}
});
// 查詢數(shù)據(jù)
Button btn_query_data = (Button) findViewById(R.id.btn_query_data);
btn_query_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book");
Cursor cursor = getContentResolver().query(uri,null,null,null,null);
if (cursor != null){
while (cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
String auhtor = cursor.getString(cursor.getColumnIndex("auhtor"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("MainActivity", "書(shū)名: " + name);
Log.d("MainActivity", "作者: " + auhtor);
Log.d("MainActivity", "頁(yè)數(shù): " + pages);
Log.d("MainActivity", "價(jià)格: " + price);
}
}
cursor.close();
}
});
// 更新數(shù)據(jù)
Button btn_update_data = (Button) findViewById(R.id.btn_update_data);
btn_update_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book/"
+ newId);
ContentValues values = new ContentValues();
values.put("name", "第二行代碼");
values.put("pages", 2000);
values.put("price", 88.88);
getContentResolver().update(uri,values,null,null);
}
});
// 刪除數(shù)據(jù)
Button btn_delete_data = (Button) findViewById(R.id.btn_delete_data);
btn_delete_data.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("content://com.wonderful.myfirstcode.chapter7.provider/book/"
+ newId);
getContentResolver().delete(uri,null,null);
}
});
}
}
代碼很簡(jiǎn)單,不做解釋了,現(xiàn)在運(yùn)行一下項(xiàng)目,如下界面:

點(diǎn)擊 添加數(shù)據(jù) 按鈕,此時(shí)數(shù)據(jù)就添加到上一章項(xiàng)目的數(shù)據(jù)庫(kù)中了,點(diǎn)擊 查詢數(shù)據(jù) 按鈕,打印日志如下:
然后點(diǎn)擊 更新數(shù)據(jù) 按鈕,再次點(diǎn)擊 查詢數(shù)據(jù) 按鈕查看打印日志如下:
最后點(diǎn)擊 刪除數(shù)據(jù) 按鈕,此時(shí)數(shù)據(jù)就沒(méi)了,點(diǎn)擊 查詢數(shù)據(jù) 按鈕也就查詢不到數(shù)據(jù)了。
以上,跨程序共享數(shù)據(jù)功能成功實(shí)現(xiàn)了。
與內(nèi)容提供器相關(guān)內(nèi)容就介紹到這。下篇文章將進(jìn)入手機(jī)多媒體的學(xué)習(xí)。