從Android 6.0開始,權(quán)限不再是在manifest文件中粘貼一下即可,這時(shí)候權(quán)限也正式走進(jìn)大家的視野白筹。項(xiàng)目的6.0適配就是我做的,當(dāng)時(shí)沒有仔細(xì)總結(jié)谅摄,最近在另一個(gè)項(xiàng)目添加權(quán)限的時(shí)候發(fā)現(xiàn)徒河,同一個(gè)功能都沒有添加申請權(quán)限的代碼,一個(gè)會(huì)掛一個(gè)不會(huì)螟凭,花了幾個(gè)小時(shí)在這個(gè)小問題上虚青。因此多花點(diǎn)時(shí)間總結(jié)一下權(quán)限問題。
- Android系統(tǒng)權(quán)限的概念
Android是一個(gè)權(quán)限分隔的操作系統(tǒng)螺男,每個(gè)應(yīng)用都有獨(dú)特的系統(tǒng)標(biāo)識(shí)棒厘。一般情況下纵穿,應(yīng)用沒有權(quán)限執(zhí)行對其它應(yīng)用、系統(tǒng)奢人、用戶可能有不利影響的操作谓媒。每個(gè)應(yīng)用都在應(yīng)用沙盒中運(yùn)行,因此當(dāng)應(yīng)用需要使用沙盒未提供的功能時(shí)何乎,需要申請權(quán)限句惯,比如讀寫sd卡、訪問網(wǎng)絡(luò)支救、訪問其它應(yīng)用的數(shù)據(jù)抢野、讀寫聯(lián)系人、調(diào)用攝像頭等各墨。
權(quán)限在AndroidManifest.xml文件中聲明指孤,Android 6.0以前,有的APP一股腦聲明了各種各樣的權(quán)限贬堵,用戶可能沒有細(xì)看就安裝了恃轩,于是這些APP就可以為所欲為,偷雞摸狗黎做,無法無天叉跛。Android 6.0把權(quán)限分成正常權(quán)限和危險(xiǎn)權(quán)限,AndroidManifest中聲明的正常權(quán)限系統(tǒng)會(huì)自動(dòng)授予蒸殿,而危險(xiǎn)權(quán)限則需要在使用的時(shí)候用戶明確授予筷厘。
換句話說,就是Android 6.0以上的系統(tǒng)在第一次使用危險(xiǎn)權(quán)限的時(shí)候宏所,需要向用戶申請敞掘,征得用戶的同意。如果還是在沒有權(quán)限的情況下執(zhí)行操作楣铁,就會(huì)獲得Crash大禮包,錯(cuò)誤日志為java.lang.SecurityException: Permission Denial
更扁。因此盖腕,應(yīng)用對危險(xiǎn)權(quán)限的申請,需要相應(yīng)的處理浓镜。
- 危險(xiǎn)權(quán)限和對應(yīng)的權(quán)限組
危險(xiǎn)權(quán)限都屬于權(quán)限組溃列,應(yīng)用在向用戶申請危險(xiǎn)權(quán)限時(shí),系統(tǒng)會(huì)彈對話框膛薛,描述應(yīng)用要訪問的權(quán)限組听隐,這時(shí)候用戶如果同意授權(quán),則權(quán)限組包含的所有權(quán)限都會(huì)被系統(tǒng)授予哄啄。比如雅任,應(yīng)用被授予READ_EXTERNAL_STORAGE權(quán)限之后风范,如果再申請WRITE_EXTERNAL_STORAGE權(quán)限,系統(tǒng)會(huì)立即授予該權(quán)限沪么。
危險(xiǎn)權(quán)限表格如下
權(quán)限組 | 權(quán)限 |
---|---|
CALENDAR | READ_CALENDAR WRITE_CALENDAR |
CAMERA | CAMERA |
CONTACTS | READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS |
LOCATION | ACCESS_FINE_LOCATION ACCESS_COARSE_LOCATION |
MICROPHONE | RECORD_AUDIO |
PHONE | READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS |
SENSORS | BODY_SENSORS |
SMS | SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS |
STORAGE | READ_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE |
- 申請權(quán)限的正確姿勢
- 需要申請的所有權(quán)限在AndroidManifest文件中聲明
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- 使用時(shí)檢查權(quán)限硼婿,沒有權(quán)限則申請
//使用兼容庫就無需判斷系統(tǒng)版本
int hasWriteStoragePermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteStoragePermission == PackageManager.PERMISSION_GRANTED) {
//擁有權(quán)限,執(zhí)行操作
initScan();
}else{
//沒有權(quán)限禽车,向用戶請求權(quán)限
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MyApplication.CODE_FOR_WRITE_PERMISSION);
}
- 覆寫onRequestPermissionsResult方法
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
//通過requestCode來識(shí)別是否同一個(gè)請求
if (requestCode == CODE_FOR_WRITE_PERMISSION){
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//用戶同意寇漫,執(zhí)行操作
initScan();
}else{
//用戶不同意,向用戶展示該權(quán)限作用
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
new AlertDialog.Builder(thisActivity)
.setMessage(R.string.storage_permissions_remind)
.setPositiveButton("OK", (dialog1, which) ->
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
EventConstConfig.CODE_FOR_CAMERA_PERMISSION))
.setNegativeButton("Cancel", null)
.create()
.show();
}
}
}
}
shouldShowRequestPermissionRationale方法返回值分幾種情況殉摔,怎么使用看應(yīng)用的具體交互需求州胳。
- 第一次請求該權(quán)限,返回false逸月。
- 請求過該權(quán)限并被用戶拒絕栓撞,返回true。
- 請求過該權(quán)限彻采,但用戶拒絕的時(shí)候勾選不再提醒腐缤,返回false。
- 申請權(quán)限的幾個(gè)小細(xì)節(jié)
使用兼容庫
checkSelfPermission肛响、requestPermissions等幾個(gè)權(quán)限相關(guān)的方法用v4包里的可以兼容6.0以下版本岭粤,否則需要包一層版本判斷。targetSDKVersion的問題
我遇到的問題就是這個(gè)特笋,有個(gè)細(xì)節(jié)沒注意到剃浇。Android系統(tǒng)觸發(fā)動(dòng)態(tài)申請權(quán)限的條件其實(shí)有兩個(gè),設(shè)備系統(tǒng)版本在Android 6.0以上并且targetSDKVersion>=23猎物。因此其實(shí)在targetSDKVersion版本小于23的情況下虎囚,即使在6.0以上的設(shè)備運(yùn)行也不會(huì)掛,但會(huì)在安裝時(shí)列出所有權(quán)限蔫磨,同6.0以下的設(shè)備淘讥。官方建議保持targetSDKVersion在最新的版本。
- 使用第三方庫easypermissions
申請權(quán)限的第三方庫有很多堤如,但沒遇到特別簡潔的蒲列,也就沒有特地去研究和使用,避免導(dǎo)入太多第三方庫搀罢,easypermissions這個(gè)庫是其中一個(gè)項(xiàng)目用到的蝗岖,這里簡單的提一下。
- 導(dǎo)入easypermissions
compile 'pub.devrel:easypermissions:0.1.9'
- 檢查權(quán)限榔至,沒有就進(jìn)行申請
if (!EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA)) {
EasyPermissions.requestPermissions(this, getString(R.string.camera_peemission_tip), CAMERA_REQUEST_CODE, Manifest.permission.CAMERA);
}
- 覆寫方法
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
// light();
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
Dialog dialog = new AlertDialog.Builder(this, R.style.MyAlertDialogStyle)
.setTitle(R.string.tips)
.setMessage(R.string.camera_peemission_tip)
.setPositiveButton(R.string.to_open, (dialog2, which) -> {
startActivity(new Intent(Settings.ACTION_APPLICATION_SETTINGS));
})
.setNegativeButton(R.string.cancel, (dialog3, which) -> {
dialog3.dismiss();
})
.create();
dialog.show();
}