Android6.0運行時權限(基於RxPermission開源庫)
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
前言
在6.0以前的系統,都是權限一刀切的處理方式,只要用戶安裝,Manifest申請的權限都會被賦予,並且安裝後權限也撤銷不了。
Android 6.0 采用新的權限模型,只有在需要權限的時候,才告知用戶是否授權;是在runtime時候授權,而不是在原來安裝的時候 ,同時默認情況下每次在運行時打開頁面時候,需要先檢查是否有所需要的權限申請。
判斷是否是需要運行時權限的標記就是targetSDKVersion。
當targetSDKVersion<23的時候,僅在安裝時賦予權限,使用時將不被提醒;
當targetSDKVersion≥23的時候才會使用新的運行時權限規則。
運行時權限未適配可能會導致應用崩潰或者在SD卡中創建目錄和文件不成功。
解決方案:
一、將targetSDKVersion人為地降到小於23,這樣就變成了還是默認使用權限,但是這種並不是Google所推薦使用的。
二、實現APP支持運行時權限
那麽,哪些權限屬於運行時權限呢?
權限的分組
Android中有很多權限,但並非所有的權限都是敏感權限,於是6.0系統就對權限進行了分類,一般為下述幾類
- 正常(Normal Protection)權限
- 危險(Dangerous)權限
- 特殊(Particular)權限
- 其他權限(一般很少用到)
正常權限
正常權限具有如下的幾個特點
- 對用戶隱私沒有較大影響或者不會帶來安全問題。
- 安裝後就賦予這些權限,不需要顯示提醒用戶,用戶也不能取消這些權限。
上述的權限基本設計的是關於網絡,藍牙,時區,快捷方式等方面,只要在AndroidManifest.xml指定了這些權限,就會被授予,並且不能撤銷。
註意:直接在AndroidManifest.xml文件中聲明權限即可。
特殊權限
這裏講特殊權限提前講一下,因為這個相對來說簡單一些。
特殊權限,顧名思義,就是一些特別敏感的權限,在Android系統中,主要由兩個
- SYSTEM_ALERT_WINDOW(設置懸浮窗,進行一些黑科技)
- WRITE_SETTINGS (修改系統設置)
關於上面兩個特殊權限的授權,做法是使用startActivityForResult啟動授權界面來完成。
註意:關於這兩個特殊權限,一般不建議應用申請。
危險權限
危險權限實際上才是運行時權限主要處理的對象,這些權限可能引起隱私問題或者影響其他程序運行。
Android中的危險權限可以歸為以下幾個分組:
權限組 |
權限列表 |
android.permission-group.CALENDAR |
android.permission.READ_CALENDAR (允許程序讀取用戶的日程信息) |
android.permission-group.CAMERA |
android.permission.CAMERA (允許訪問攝像頭進行拍照) |
android.permission-group.CONTACTS |
android.permission.READ_CONTACTS (允許應用訪問聯系人通訊錄信息) android.permission.WRITE_CONTACTS (寫入聯系人,但不可讀取) android.permission.GET_ACCOUNTS (訪問GMail賬戶列表) |
android.permission-group.LOCATION |
android.permission.ACCESS_COARSE_LOCATION (通過WiFi或移動基站的方式獲取用戶錯略的經緯度信息,定位精度大概誤差在30~1500米) android.permission.ACCESS_FINE_LOCATION (通過GPS芯片接收衛星的定位信息,定位精度達10米以內) |
android.permission-group.MICROPHONE |
android.permission.RECORD_AUDIO (錄制聲音通過手機或耳機的麥克) |
android.permission-group.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 (允許程序使用SIP視頻服務) android.permission.PROCESS_OUTGOING_CALLS (允許程序監視,修改或放棄播出電話) |
android.permission-group.SENSORS |
android.permission.BODY_SENSORS (允許從傳感器,用戶使用來衡量什麽是他/她的身體內發生的事情,如心臟速率訪問數據的應用程序) |
android.permission-group.SMS |
android.permission.SEND_SMS (發送短信) android.permission.RECEIVE_SMS (接收短信) android.permission.READ_SMS (讀取短信內容) android.permission.RECEIVE_WAP_PUSH (接收WAP PUSH信息) android.permission.RECEIVE_MMS (接收彩信) android.permission.READ_CELL_BROADCASTS () |
android.permission-group.STORAGE |
android.permission.READ_EXTERNAL_STORAGE (允許程序讀取外部存儲,如SD卡讀文件) android.permission.WRITE_EXTERNAL_STORAGE (允許程序寫入外部存儲,如SD卡上寫文件) |
效果圖
代碼分析
- 基於Android Studio開發環境。
- 基於RxPermission開源庫。
- 項目的最低sdk版本號(minSdkVersion)必須>=11。
- 分為以下四種情況分析:
A) 只有一個運行時權限申請的情況
B) 同時請求多個權限(合並結果)的情況
C) 同時請求多個權限(分別獲取結果)的情況
D) 條件觸發獲取權限(結合RxBinding使用)的情況
使用步驟
一、檢查minSdkVersion值
當前項目的app/ build.gradle文件中的minSdkVersion值必須>=11
二、添加依賴開源庫
在當前項目的app/ build.gradle文件中的dependencies{}裏面添加以下代碼:
普通情況【這個暫時不會用】
//運行時權限
compile ‘com.tbruyelle.rxpermissions:rxpermissions:[email protected]
項目中使用了RxJava2的的情況【建議使用這個--不論項目中是否使用了RxJava2】
//運行時權限
compile ‘com.tbruyelle.rxpermissions2:rxpermissions:[email protected]
compile ‘io.reactivex.rxjava2:rxjava:2.0.2‘
如果想要實現條件觸發獲取權限(結合RxBinding使用)的情況,則還需要依賴RxBinding開源庫
compile ‘com.jakewharton.rxbinding2:rxbinding:2.0.0‘
例如,Demo中的依賴配置如下:
三、在AndroidManifest.xml文件中聲明權限列表
<!-- ======================授權獲取設備ANDROID_ID========================== --> <!-- 訪問電話狀態 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 允許程序讀取外部存儲文件 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 允許程序寫入外部存儲,如SD卡上寫文件 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.CAMERA" />
四、運行時權限申請代碼
註意:需要import的相關類如下:
import com.jakewharton.rxbinding2.view.RxView; import com.tbruyelle.rxpermissions2.Permission; import com.tbruyelle.rxpermissions2.RxPermissions; import io.reactivex.functions.Action; import io.reactivex.functions.Consumer;
4.1 只有一個運行時權限申請的情況
/**只有一個運行時權限申請的情況*/ private void onePermission(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.request(Manifest.permission.READ_PHONE_STATE) //權限名稱,多個權限之間逗號分隔開 .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { Log.e(TAG, "{accept}granted=" + granted);//執行順序——1【多個權限的情況,只有所有的權限均允許的情況下granted==true】 if (granted) { // 在android 6.0之前會默認返回true // 已經獲取權限 Toast.makeText(MainActivity.this, "已經獲取權限", Toast.LENGTH_SHORT).show(); String deviceId = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();//根據不同的手機設備返回IMEI,MEID或者ESN碼 Toast.makeText(MainActivity.this, "{accept}deviceId=" + deviceId, Toast.LENGTH_SHORT).show(); } else { // 未獲取權限 Toast.makeText(MainActivity.this, "您沒有授權該權限,請在設置中打開授權", Toast.LENGTH_SHORT).show(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG,"{accept}");//可能是授權異常的情況下的處理 } }, new Action() { @Override public void run() throws Exception { Log.e(TAG,"{run}");//執行順序——2 } }); }
在Activity的onCreate方法中調用即可:
打印的日誌如下:
4.2 同時請求多個權限(合並結果)的情況
/**同時請求多個權限(合並結果)的情況*/ private void MultPermission(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.request(Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE)//權限名稱,多個權限之間逗號分隔開 .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { Log.e(TAG, "{accept}granted=" + granted);//執行順序——1【多個權限的情況,只有所有的權限均允許的情況下granted==true】 if (granted) { // 在android 6.0之前會默認返回true // 已經獲取權限 Toast.makeText(MainActivity.this, "已經獲取權限", Toast.LENGTH_SHORT).show(); } else { // 未獲取權限 Toast.makeText(MainActivity.this, "您沒有授權該權限,請在設置中打開授權", Toast.LENGTH_SHORT).show(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG,"{accept}");//可能是授權異常的情況下的處理 } }, new Action() { @Override public void run() throws Exception { Log.e(TAG,"{run}");//執行順序——2 } }); }
在Activity的onCreate方法中調用即可:
打印的日誌如下:
4.3 同時請求多個權限(分別獲取結果)的情況
/**同時請求多個權限(分別獲取結果)的情況*/ private void MultPermission2(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.requestEach(Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE)//權限名稱,多個權限之間逗號分隔開 .subscribe(new Consumer<Permission>(){ @Override public void accept(Permission permission) throws Exception { Log.e(TAG, "{accept}permission.name=" + permission.name); Log.e(TAG, "{accept}permission.granted=" + permission.granted); if(permission.name.equals(Manifest.permission.READ_PHONE_STATE) && permission.granted){ // 已經獲取權限 Toast.makeText(MainActivity.this, "已經獲取權限", Toast.LENGTH_SHORT).show(); String deviceId = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();//根據不同的手機設備返回IMEI,MEID或者ESN碼 Toast.makeText(MainActivity.this, "{accept}deviceId=" + deviceId, Toast.LENGTH_SHORT).show(); } } }); }
在Activity的onCreate方法中調用即可:
打印的日誌如下:
4.4 條件觸發獲取權限(結合RxBinding使用)的情況
/**條件觸發獲取權限(結合RxBinding使用)的情況*/ private void clickPermission(View view){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance RxView.clicks(view) .compose(rxPermissions.ensure(Manifest.permission.CAMERA)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) { Log.e(TAG, "{accept}granted=" + granted);//【多個權限的情況,只有所有的權限均允許的情況下granted==true】 if (granted) { // 在android 6.0之前會默認返回true // 已經獲取權限 Toast.makeText(MainActivity.this, "已經獲取CAMERA權限", Toast.LENGTH_SHORT).show(); } else { // 未獲取權限 Toast.makeText(MainActivity.this, "您沒有授權該權限,請在設置中打開授權", Toast.LENGTH_SHORT).show(); } } }); }
在activity的onCreate方法中調用:
打印的日誌如下:
其中,點擊事件對應的控件如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.why.project.runtime.MainActivity"> <Button android:id="@+id/btn_getpermission" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="手動獲取權限" android:layout_centerInParent="true"/> </RelativeLayout>
混淆配置
無
參考資料
聊一聊 Android 6.0 的運行時權限
https://blog.coding.net/blog/understanding-marshmallow-runtime-permission
Android權限管理之Android 6.0運行時權限及解決辦法
http://www.cnblogs.com/whoislcj/p/6064710.html
Android 6.0 運行時權限處理完全解析
http://blog.csdn.net/lmj623565791/article/details/50709663
Manifest.permission
https://developer.android.google.cn/reference/android/Manifest.permission.html
android權限大全
http://www.cnblogs.com/classic/archive/2011/06/20/2085055.html
RxPermissions開源庫github地址:
https://github.com/tbruyelle/RxPermissions
項目demo下載地址
鏈接:http://pan.baidu.com/s/1mi9et1Q 密碼:qru3
Android6.0運行時權限(基於RxPermission開源庫)