為了保護(hù)系統(tǒng)的整體性和用戶隱私,Android系統(tǒng)中的每個App都是運行在一個帶限制的sandbox中,如果一個app想要從sandbox外獲取信息或資源,app就需要請求permission,而具體請求什么permission則要視你的具體情況而定,請求之后系統(tǒng)會根據(jù)不同的請求來自動授權(quán)或者讓用戶來選擇是否授權(quán).
1. 聲明Permission
如果你要請求某些permission來進(jìn)行相關(guān)操作,你需要在Manifest中聲明這些permission,系統(tǒng)會根據(jù)permission的敏感程度來決定授權(quán)方式,比如:
- 你的app請求permission來打開閃關(guān)燈,系統(tǒng)會自動授權(quán).
- 你的app請求讀取聯(lián)系人的權(quán)限,系統(tǒng)就會詢問用戶是同意授權(quán)該請求.
還有,不同的系統(tǒng)版本系統(tǒng)的授權(quán)方式也會不同:
- Android 5.1及以下是安裝的時候請求授權(quán).
- Android 6.0及以上是app真正操作需要是才請求授權(quán).
1.1 確定你的App需要的Permission
如何確定什么樣的操作需要請求permission?請求哪一種permission?
通常,如果你要獲取一些app外部的信息或資源,或者執(zhí)行一些會影響到設(shè)備或其他app的行為,這就需要permission,比如連接網(wǎng)絡(luò),使用攝像頭,操作wifi開關(guān)等.而要申請哪種permission則可以查看系統(tǒng)提供的permission列表,具體在Normal and Dangerous Permissions中.
要注意的是你只需要申請你直接操作的action所需要的permission即可,如果你調(diào)用其他的app去獲取一些信息或資源再返回給你,你就不需要申請相應(yīng)的permission.比如:
- 你直接去讀取通訊錄,就需要有READ_CONTACTS permission.
- 但是如果你使用Intent來啟動通訊錄app,然后返回給你結(jié)果,你就不需要READ_CONTACTS permission,而需要該permission的則是通訊錄app.
1.2 在Manifest中添加permission
如下示例:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application ...>
...
</application>
</manifest>
2.運行時請求Permission
從Android 6.0(API 23)開始,系統(tǒng)的授權(quán)方式有變,從之前的安裝時授權(quán)變成使用時授權(quán).這會簡化應(yīng)用的安裝流程,也會給用戶在功能上更多的控制權(quán).比如,很多相機(jī)需要打開攝像頭的權(quán)限和獲取位置信息的權(quán)限,現(xiàn)在當(dāng)app請求這兩個權(quán)限時,你可以只授權(quán)獲取相機(jī)但是不授權(quán)獲取位置信息的permission.系統(tǒng)的permission分為兩類:normal和dangeroous:
- Normal Permission: 無獲取用戶隱私信息的風(fēng)險,如果在manifest中聲明了這些權(quán)限,系統(tǒng)會自動授權(quán).
- Dangerous Permission: 需要獲取用戶隱私數(shù)據(jù),在manifest中聲明后,還需要在使用中讓用戶來決定同意是否授權(quán).
對于任何系統(tǒng)版本,都需要在menifest中聲明要使用的permission,但是normal和dangerous的permission還是有不同的影響:
- Android 5.1(API 22)及以下,在安裝某個app的時候,如果你不同意列出的dangerous permissions,系統(tǒng)就不會安裝該app.
- Android 6.0(API 23)及以上,app安裝時不需要授權(quán),在運行時需要授權(quán)時用戶來決定是否同意授權(quán),如果不同意app還是可以繼續(xù)運行只是某些功能可能會不能用.
下面將介紹如何使用Android Support Library來檢查和請求權(quán)限,與Android 6.0相似,但是這個support類庫能夠兼容之前的版本,所以無需判斷系統(tǒng)版本,會更簡單.
2.1 檢查Permission
dangerous permission需要你每次使用時都檢查是否有權(quán)限,可以調(diào)用 ContextCompat.checkSelfPermission()方法來檢查,如下示例:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
返回的結(jié)果為PackageManager中的兩個常量:PERMISSION_GRANTED和PERMISSION_DENIED.
2.2 請求Permission
如果你在manifest中聲明了dangerous permission,就必須要詢問用戶是否同意授權(quán)該permission.Android系統(tǒng)中提供了幾種請求permission的方法,使用這些方法會調(diào)起一個標(biāo)準(zhǔn)的Android dialog,里面有一些選項,這個dialog你是不能定制的.
2.2.1 解釋為什么app需要這些permission
在一些情況下,你可能需要幫助用戶理解為什么你的app需要這些權(quán)限.比如你的應(yīng)用是一個攝影類app,很正常要請求攝像頭相關(guān)的permission,但是用戶可能不理解為什么你的app要獲取地址或者通訊錄信息.在你請求這些可能不好理解的權(quán)限時,你應(yīng)該給用戶提供一些解釋說明,不要太復(fù)雜.
有一個方法可以幫助你確定合適需要提供一個解釋說明,就是ActivityCompat類中的shouldShowRequestPermissionRationale()方法.如果你之前請求過某個請求并被拒絕,該方法會返回true.
- 注意: 當(dāng)用戶拒絕該請求時并勾選了"不再詢問"時,shouldShowRequestPermissionRationale()會返回false,同時如果permission為設(shè)備的政策禁止app要請求的permission時,也會返回false.
2.2.2 只請求需要的permission
如果你的app沒拿到需要的permission,你需要使用requestPermissions()來請求,是異步的,但是你取消不了dialog,除非選擇,代碼如下:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
- 注意: requestPermissions()中的第三個參數(shù)是請求碼,方便回調(diào)處理.并且請求碼只能使用(0-255內(nèi)的數(shù)值)(lower 8 bits).
2.2.3 處理回調(diào)
示例代碼如下:
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
上面的處理結(jié)果是按組permission group來分的,也就是只要你這個組中有個permission被授權(quán),這個組中的其他permission也會被授權(quán).比如你請求READ_CONTACTS和WRITE_CONTACTS兩個permission,如果系統(tǒng)授權(quán)了一個則另一個也會自動會被授權(quán). 但是要注意的是permission的分組有可能在以后改變,所以不能依賴具體分組來做操作.
3. Permission的最佳實踐
Permission的良好請求也是用戶體驗的一部分,用不好分分鐘被卸載,還是要重視的.
3.1 考慮使用Intent
很多情況下你會有兩個選擇:
a. 直接請求permission來執(zhí)行相關(guān)操作.
b. 使用Intent來調(diào)起其他的app來執(zhí)行相關(guān)操作.
比如拍照:
a. 你可以請求你可以請求CAMERA權(quán)限,然后直接操作攝像頭,利用相關(guān)API來拍照,這樣你可以完全控制拍照過程和自定義拍照UI.
b. 你可以使用ACTION_IMAGE_CAPTURE Intent來調(diào)起相機(jī),然后在onActivityResult()中處理返回結(jié)果,具體可以看下這個
相似的,在打電話,訪問通訊錄等你也有上述兩種選擇,現(xiàn)在說說這兩種選擇的優(yōu)點和缺點:
a. 直接請求permission來執(zhí)行相關(guān)操作:
* 你的App擁有完全的控制權(quán),同時又帶來復(fù)雜的工作量.
* 如果用戶沒有給你授權(quán)或者授權(quán)之后撤銷了,你的相關(guān)功能就不能運行.
b. 使用Intent:
* 無需考慮操作的UI,工作量小,執(zhí)行該intent的app會負(fù)責(zé)這些,也就是你對于這些UI沒有控制權(quán).
* 如果有多個處理該Intent的app,則用戶需要選擇一個來操作,如果用戶沒有將其設(shè)為默認(rèn),則每次處理該Intent用戶都要選一次,可能會麻煩.
3.2 測試Permission模型
Android 6.0開始permission的授權(quán)方式改變了,下面有一些tips可以幫助你識別Android 6.0及以后的設(shè)備中permission相關(guān)的問題:
- 識別app當(dāng)前的permission和相關(guān)代碼的路徑.
- Test user flows across permission-protected services and data.
- 測試授權(quán)或撤銷各種組合.
- 使用adb工具類管理permission.
// List permissions and status by group:
$ adb shell pm list permissions -d -g
// Grant or revoke one or more permissions:
$ adb shell pm [grant|revoke] <permission-name> ...
- 分析你的app使用權(quán)限的services