1. 程式人生 > >Android6.0運行時權限(基於RxPermission開源庫)

Android6.0運行時權限(基於RxPermission開源庫)

art cti 將不 轉載 誤差 title 特殊 什麽是 roi

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

前言

  在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開源庫)