小序
原本以爲這次項目完成得不錯,結果測試妹子在禪道上又提了些BUG,大多涉及到6.0權限的問題。這就比較尷尬了,畢竟是一開始並沒有顧慮的。而關於權限申請,其實際上已經有很多前輩提供過相應的解決辦法,在此謝過。
正文
1. 需動態申請的權限
權限的申請依據谷歌的分類來講,一類屬於普通的權限申請,與往常一樣可直接在 AndroidManifest.xml 註冊就可以了;另一類則需要在應用程序使用的地方執行動態申請,該類權限屬危險權限(組),當然前提也得先在 AndroidManifest.xml 下注冊聲明。這裏僅羅列需要動態申請的權限內容(摘自這裏)。
權限組 | 權限 |
---|---|
CALENDAR 日曆 | android.permission.READ_CALENDAR android.permission.WRITE_CALENDAR |
CAMERA 相機 | android.permission.CAMERA |
CONTACTS 聯繫人 | android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS |
LOCATION 位置 | android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION |
MICROPHONE 麥克風 | android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
PHONE 電話 | android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
SENSORS 傳感器 | android.permission.BODY_SENSORS |
SMS 短信 | android.permission.SEND_SMS android.permission.RECEIVE_SMS android.permission.READ_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECEIVE_MMS android.permission.READ_CELL_BROADCASTS |
STORAGE 存儲 | android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE |
2. 具體實現
瞭解完需要申請的權限之後,需要根據實際項目當中需要用到的相關權限,在相應的位置開始我們的權限申請。申請過程主要包括以下兩個操作,另外也有人封裝了些簡單易用的依賴庫,有興趣的親也可瞭解下。
/**
* 以定動態申請位權限爲例
*/
public void handPermission() {
// 定位權限組
String[] mPermissionGroup = new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION};
// 過濾已持有的權限
List<String> mRequestList = new ArrayList<>();
for (String permission : mPermissionGroup) {
if ((ContextCompat.checkSelfPermission(getActivity(), permission)
!= PackageManager.PERMISSION_GRANTED)) {
mRequestList.add(permission);
}
}
// 申請未持有的權限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !mRequestList.isEmpty()) {
ActivityCompat.requestPermissions(mActivity, mRequestList.toArray(
new String[mRequestList.size()]), 100);
} else {
// 權限都有了,就可以繼續後面的操作
}
}
/**
* 回調權限申請結果
*
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 100: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 同意權限申請,就可以繼續後面的操作
} else {
// 拒絕權限申請,提示申請權限的重要性
}
}
break;
}
}
3. 巧遇問題
a. 反覆申請
“重複申請”是不合理的,這樣會嚴重影響到用戶的體驗。最好是在用戶拒絕權限的申請用給予用戶用好提示,或本次應用開啓權限僅申請一次,不通過提示爲授權之類的。
部分機子系統需要特別注意,因爲它們整個應用的申請僅提示一次,如最爲熟悉的小米/紅米。無論之後再怎麼requestPermissions也無濟於事,到頭來還得提示用戶到應用設置手動啓動權限。這算是比較噁心的一個事情,且其他機子上如果用戶勾選了不再提示也是一樣的。
建議權限申請不要再onResume()的生命週期中執行,因爲權限申請操作後會再次調用onResume(),個人在小米/紅米機子上測試出現過該問題。
b. 多權限申請帶來的問題
String[] mPermissionGroup = new String[]{
Manifest.permission.CAMERA
Manifest.permission.ACCESS_COARSE_LOCATION};
// 注意:這樣的多權限申請是存在問題的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(mActivity, mPermissionGroup, 100);
}
如上面所見,我定義了一個需要權限請求的數組。裏面包括了兩個權限組,一個是照相機的權限,另一個則是定位的權限(權限組當中的任意成員被授予權限,相當於權限組內的各個權限都將被授予權限)。那麼在實際執行當中會是什麼效果呢?
經上面圖示效果可以看到,多權限申請的時候每一個權限組的申請都被單獨開來了。那麼問題來了,因爲權限的申請是我們主動發出的,並且一個應用當中往往不止一個地方需要權限申請。簡單的說如果你已經執行百度定位授予了定位權限了。那麼你下次就應該先判斷定位權限是否已被授權再加入到多權限申請單中。
那麼有人就要問了,即使一開始不判斷是否授權。在申請授權的回調過程中不也能自己判斷麼?嗯,確實是這樣沒錯。但那只是針對mPermissionGroup這個整體,即如果當中有部分權限已經被授予,而剩下的未授予,將全部被重新申請。
E/SQLiteDatabase: Failed to open database ‘/storage/emulated/0/emlibs/libs/monitor.db’
E/SQLiteDatabase: Failed to open database ‘/storage/emulated/0/baidu/tempdata/ls.db’
以上列舉兩種異常,主要在小米/紅米機子上測試出現,其他機子未測試確認。出現該問題的罪魁禍首就是,在多權限申請當中將原已授權的權限拒絕了,且該權限剛好正被使用。解決以上原因只需要一步“過濾”,遍歷mPermissionGroup集合當中每個成員的授權狀態再執行多權限申請(如第2點當中寫到的)。