
運(yùn)行時(shí)權(quán)限
從 Android 6.0(API 級(jí)別 23)開始,用戶開始在應(yīng)用運(yùn)行時(shí)向其授予權(quán)限,而不是在應(yīng)用安裝時(shí)授予。
系統(tǒng)權(quán)限分為兩類:
- 正常權(quán)限:只需在你應(yīng)用的 Androidmanifest.xml 中列出,安裝時(shí)授權(quán)。
- 危險(xiǎn)權(quán)限:需要在你應(yīng)用的 Androidmanifest.xml 中列出,并在運(yùn)行時(shí)授權(quán)。
如需了解更多關(guān)于正常權(quán)限與危險(xiǎn)權(quán)限可以參閱這里。
關(guān)于 Android 6.0 運(yùn)行時(shí)權(quán)限的詳細(xì)講解可以看這篇文章和官方文檔。
運(yùn)行時(shí)權(quán)限的加入增加了用戶隱私的安全,但同時(shí)也給開發(fā)者帶來了一些負(fù)擔(dān),因?yàn)槎嗔藱z查權(quán)限,請(qǐng)求權(quán)限,處理權(quán)限請(qǐng)求響應(yīng)的步驟。
于是就出現(xiàn)了一些框架,用于簡(jiǎn)化運(yùn)行時(shí)權(quán)限的處理,如 PermissionsDispatcher,Dexter,RxPermissions,easypermissions 等。
這里只用過 easypermissions 和 PermissionsDispatcher,最終選擇了 PermissionsDispatcher,原因是使用人數(shù)最多,api 設(shè)計(jì)簡(jiǎn)單易用,兼容性較好,easypermissions 使用起來相對(duì)麻煩一些。
PermissionsDispatcher
PermissionsDispatcher 提供簡(jiǎn)單的基于注解的 api 處理 Android Marshmallow 下的運(yùn)行時(shí)權(quán)限(非反射方式)。
項(xiàng)目地址:https://github.com/hotchemi/PermissionsDispatcher
一. 集成說明
注意把 ${latest.version} 改為最新版本。
打開命令行,cd 到工程根目錄,執(zhí)行 gradlew -version 或者 gradlew -v 命令查看當(dāng)前工程 gradle 版本。
根據(jù) gradle 版本進(jìn)行如下配置:
Android Gradle Plugin >= 2.2
在 app 模塊下的 build.gradle 添加下面的配置:
dependencies {
compile 'com.github.hotchemi:permissionsdispatcher:${latest.version}'
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:${latest.version}'
}
Android Gradle Plugin < 2.2
在 project-level 下的 build.gradle 添加下面配置:
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
然后,在 app 模塊下的 build.gradle 添加下面配置:
apply plugin: 'android-apt'
dependencies {
compile 'com.github.hotchemi:permissionsdispatcher:${latest.version}'
apt 'com.github.hotchemi:permissionsdispatcher-processor:${latest.version}'
}
二. 使用說明
1. 添加注解
注意:注解的方法不能是 private 的。
相關(guān)注解說明
| 注解 | 是否必須 | 描述 |
|---|---|---|
@RuntimePermissions |
? | 注冊(cè)一個(gè) Activity 或 Fragment 用于處理權(quán)限 |
@NeedsPermission |
? | 注解一個(gè)方法,說明需要什么權(quán)限(一個(gè)或多個(gè)) |
@OnShowRationale |
注解一個(gè)方法,解釋為什么需要這些權(quán)限 | |
@OnPermissionDenied |
注解一個(gè)方法,當(dāng)用戶拒絕授權(quán)時(shí)將調(diào)用該方法 | |
@OnNeverAskAgain |
注解一個(gè)方法,當(dāng)用戶選擇了 "不再提醒" 將調(diào)用該方法 |
示例代碼
@RuntimePermissions
public class MainActivity extends AppCompatActivity {
// 單個(gè)權(quán)限
// @NeedsPermission(Manifest.permission.CAMERA)
// 多個(gè)權(quán)限
@NeedsPermission({ Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO })
void showCamera() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
.addToBackStack("camera")
.commitAllowingStateLoss();
}
// 向用戶說明為什么需要這些權(quán)限(可選)
@OnShowRationale(Manifest.permission.CAMERA)
void showRationaleForCamera(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage(R.string.permission_camera_rationale)
.setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
.setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
.show();
}
// 用戶拒絕授權(quán)回調(diào)(可選)
@OnPermissionDenied(Manifest.permission.CAMERA)
void showDeniedForCamera() {
Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
}
// 用戶勾選了“不再提醒”時(shí)調(diào)用(可選)
@OnNeverAskAgain(Manifest.permission.CAMERA)
void showNeverAskForCamera() {
Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
}
}
2. 調(diào)用自動(dòng)生成的輔助類
注解寫好后,Build ——> Make Module ***,PermissionsDispatcher 會(huì)生成一個(gè) MainActivityPermissionsDispatcher(Activity Name + PermissionsDispatcher),你可以使用它來安全地訪問這些受權(quán)限保護(hù)的方法。
你唯一需要做的一件事是將工作委托給這個(gè)輔助類:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button_camera).setOnClickListener(v -> {
// 調(diào)用帶權(quán)限檢查的 showCamera 方法
MainActivityPermissionsDispatcher.showCameraWithCheck(this);
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 代理權(quán)限處理到自動(dòng)生成的方法
MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
*PermissionDispatcher.*WithCheck 方法對(duì)你原來的方法進(jìn)行了包裝,包了一層權(quán)限檢查的代碼,你需要做的就是把原來的方法調(diào)用改為 *PermissionDispatcher.*WithCheck。另外,在 onRequestPermissionsResult 中回調(diào) *PermissionsDispatcher.onRequestPermissionsResult 方法。
三. 獲取特殊權(quán)限
兩個(gè)特殊權(quán)限:
-
Manifest.permission.SYSTEM_ALERT_WINDOW:顯示懸浮窗 -
Manifest.permission.WRITE_SETTINGS:讀寫系統(tǒng)設(shè)置
對(duì)于這兩個(gè)權(quán)限,在 Android 下默認(rèn)是需要到 Settings -> Apps 中手動(dòng)授權(quán)的。由于不同 ROM 情況可能還有點(diǎn)不一樣。
PermissionsDispatcher 對(duì)于兩個(gè)特殊權(quán)限也做了兼容處理(Android6.0+),使用方法如上一樣。
如果需要兼容低版本的 Android 系統(tǒng)或者不同 ROM 可以參考這個(gè)開源項(xiàng)目。
四. maxSdkVersion
<uses-permission> 有一個(gè) maxSdkVersion 屬性,PermissionsDispatcher 對(duì)它也做了支持。
比如,在 Androidmanifest.xml 中定義了如下權(quán)限:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
注解中添加 maxSdkVersion:
@RuntimePermissions
public class MainActivity extends AppCompatActivity {
@NeedsPermission(value = Manifest.permission.WRITE_EXTERNAL_STORAGE, maxSdkVersion = 18)
void getStorage() {
// ...
}
}
PermissionsDispatcher Plugin
PermissionsDispatcher Plugin 是 PermissionsDispatcher 的輔助插件,支持自動(dòng)生成權(quán)限檢查相關(guān)代碼。
項(xiàng)目地址:https://github.com/shiraji/permissions-dispatcher-plugin
安裝說明:Android Studio 中打開 Setting,
Plugins ——> Browse repositories...搜索PermissionsDispatcher plugin,點(diǎn) Install 下載安裝,安裝完成后重啟生效。

使用說明:
- 在 Activity/Fragment 中,Generate -> Generate Runtime Permissions...
- 選擇權(quán)限并為每個(gè)注解輸入方法名
- 點(diǎn)擊 Generate ,完成
使用過程中發(fā)現(xiàn)有兩個(gè)小問題需要注意一下:
- 多次生成代碼,
onRequestPermissionsResult方法也會(huì)被自動(dòng)添加多次 - 代碼生成后會(huì)提示你是否 rebuild 工程,建議選擇取消,
Build ——> Make Module ***相對(duì)快些