動態(tài)權(quán)限設(shè)置—RxPermissions

首先我們先科普一下它的技術(shù)背景:

  • 從 Android 6.0(API 級別 23)開始,出于對用戶安全性能的考慮,將權(quán)限這部分分成 了兩類:一類是Install權(quán)限,稱之為安裝時權(quán)限,另一類是Runtime權(quán)限,稱之為運(yùn)行時權(quán)限。

  • 安裝時權(quán)限,就是在安裝app時賦予該app的權(quán)限。比如:Normal和Signature級別的權(quán)限都是安裝時權(quán)限。賦予app Normal和Signature權(quán)限時,不會給用戶提示界面,系統(tǒng)自動決定權(quán)限的賦予。


這里需要注意一點(diǎn),對于Signature權(quán)限,如果使用權(quán)限的app與聲明權(quán)限的app的簽名不一致,則系統(tǒng)拒絕賦予該Signature權(quán)限。這句話怎么理解呢?

聲明權(quán)限是指在AndroidManifest.xml中使用<permission>標(biāo)簽的權(quán)限
使用權(quán)限是指在AndroidManifest.xml中使用<uses-permission>標(biāo)簽的權(quán)限。

舉個場景例子:
app A中聲明了權(quán)限PermissionA,app B中想要使用權(quán)限PermissionA。
那么app B在清單文件中配置了PermissionA。如果這個PermissionA的protectionLevel(風(fēng)險級別)屬性設(shè)置為Normal,那么app B完全可以獲得PermissionA使用,但如果PermissionA的protectionLevel屬性設(shè)置為Signature,因?yàn)閍pp A 與app B簽名文件不一樣,那么app B不會獲得PermissionA的使用

還不怎么理解的同學(xué)給你們兩個網(wǎng)頁,結(jié)合著看,受益挺多
Android聲明和使用權(quán)限 、Android 權(quán)限的一些細(xì)節(jié)


  • 運(yùn)行時權(quán)限,是指在app運(yùn)行過程中,賦予app的權(quán)限。這個過程中,會顯示明顯的權(quán)限授予界面,讓用戶決定是否授予權(quán)限。比如:Dangerous級別的權(quán)限,如果運(yùn)行在Android 6.0及以上的手機(jī)系統(tǒng)中,app在運(yùn)行時必須主動申請這些Dangerous權(quán)限,否則app就不會獲取到dangerous權(quán)限。

注意一點(diǎn),這種權(quán)限有點(diǎn)特殊,和上面的分類不同,它的分類具體來說和app有關(guān)系:如果app的targetSdkVersion是22及以下,Dangerous權(quán)限歸到安裝時權(quán)限,如果app的targetSdkVersion是23及以上Dangerous權(quán)限歸到運(yùn)行時權(quán)限


說到這里了,總結(jié)一下共說了幾種權(quán)限:

  • Normal: 低風(fēng)險的,不會對系統(tǒng)、用戶或其他應(yīng)用程序造成危害,這類權(quán)限不涉及個人隱私,不需要用戶進(jìn)行授權(quán),比如手機(jī)震動,訪問網(wǎng)絡(luò)。
  • Dangerous 高風(fēng)險的,系統(tǒng)將可能要求用戶輸入相關(guān)信息,才會授予此權(quán)限,這類權(quán)限涉及個人隱私,需要用戶進(jìn)行授權(quán),比如讀取SD卡,訪問通訊錄等。
  • Signature 只有當(dāng)應(yīng)用程序所用數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序所用數(shù)字簽名相同時,才能將權(quán)限授給它。
  • SignatureOrSystem 將權(quán)限授給具有相同數(shù)字簽名的應(yīng)用程序或Android包類,這一級別適用于非常特殊的情況,比如多個供應(yīng)商需要通過系統(tǒng)影像共享功能時(簡單了解即可,幾乎用不到)

RxPermissions的好處

  • 開發(fā)者不用擔(dān)心Android運(yùn)行環(huán)境的版本,如果系統(tǒng)是Android 6.0之前的版本,RxPermissions返回的結(jié)果是true,即app請求的每個權(quán)限都被允許

RxPermissions內(nèi)部已經(jīng)對Android版本進(jìn)行了判斷:

boolean isMarshmallow() { 
  return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; 
} 
public boolean isGranted(String permission) { 
  // 如果是Android 6.0 (Api 23)之前,則權(quán)限被允許使用。 
  return !isMarshmallow() || mRxPermissionsFragment.isGranted(permission); 
}

  • 將權(quán)限申請的代碼和請求結(jié)果的代碼放在一起管理,避免了代碼的分散。

權(quán)限的申請?jiān)瓉碓趓equestPermissions()方法中,請求的結(jié)果放在onRequestPermissionsResult()方法中。
而RxPermissions通過request(需要的權(quán)限)與subscribe(Action)統(tǒng)一進(jìn)行管理操作


  • RxPermissions具備Rx(RxJava)的特性,例如可以使用鏈?zhǔn)讲僮?,可以?zhí)行filter操作、transform操作、lambda表達(dá)式等等。

RxPermissions獲取運(yùn)行時權(quán)限的步驟

  • 準(zhǔn)備工作
    ⑴ 安卓手機(jī)必須是Android 6.0 (API level >= 23)以上,因?yàn)?.0以下安卓手機(jī)沒有運(yùn)行時權(quán)限這個概念
    ⑵ 使用這個庫的時候,項(xiàng)目文件build.gradle中的targetSdkVersion >= 23
    ⑶ 使用這個庫的時候,項(xiàng)目文件build.gradle中的minSdkVersion >= 11
  • 添加依賴
    因?yàn)橐玫絉xPermissions,所以先加入依賴,而RxPermissions又屬于RX系列,所以也要加入對rxjava的依賴
compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar'
compile 'io.reactivex:rxjava:1.1.3'
  • 添加權(quán)限
    在AndroidManifest.xml加入項(xiàng)目所需的運(yùn)行時權(quán)限,例如:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
  • Activity代碼操作
    細(xì)分的化可以分成三種操作:
    ①.請求單個權(quán)限
//場景模擬是點(diǎn)擊button,用RxPermissions申請讀取日程提醒權(quán)限
mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                RxPermissions.getInstance(TestActivity.this)
                        .request(Manifest.permission.READ_CALENDAR)//這里填寫所需要的權(quán)限
                        .subscribe(new Action1<Boolean>() {
                            @Override
                            public void call(Boolean aBoolean) {
                                if (aBoolean) {//true表示獲取權(quán)限成功(如果手機(jī)為android6.0以下的話這里總是返回true,不會彈框權(quán)限提示)
                                    Log.i("permissions", Manifest.permission.READ_CALENDAR + ":獲取成功");
                                } else {
                                    Log.i("permissions", Manifest.permission.READ_CALENDAR + ":獲取失敗");
                                }
                            }
                        });
            }
        });

效果圖如下:


Activity界面

點(diǎn)擊開啟日程權(quán)限按鈕

②.一次申請多個權(quán)限

RxPermissions.getInstance(TestActivity.this)
                .request(Manifest.permission.RECEIVE_MMS, Manifest.permission.READ_CALL_LOG)//多個權(quán)限用","隔開
                .subscribe(new Action1<Boolean>() {
                    @Override
                    public void call(Boolean aBoolean) {
                        if (aBoolean) {
                            //當(dāng)所有權(quán)限都允許之后,返回true
                            Log.i("permissions", "btn_more_sametime:" + aBoolean);
                        } else {
                            //只要有一個權(quán)限禁止,返回false,下一次申請只申請沒通過申請的權(quán)限
                            Log.i("permissions", "btn_more_sametime:" + aBoolean);
                        }
                    }
                });

效果圖如下:


Activity界面
點(diǎn)擊開啟彩信和通話記錄權(quán)限按鈕
點(diǎn)擊開啟彩信和通話記錄權(quán)限按鈕

③.分別申請多個權(quán)限

//分別申請多個權(quán)限時,使用requestEach
RxPermissions.getInstance(TestActivity.this)
                .requestEach(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
                .subscribe(new Action1<Permission>() {
                    @Override
                    public void call(Permission permission) {
                        if (permission.name.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
                            //當(dāng)ACCESS_FINE_LOCATION權(quán)限獲取成功時,permission.granted=true
                            Log.i("permissions", Manifest.permission.ACCESS_FINE_LOCATION + ":" + permission.granted);
                        }
                        if (permission.name.equals(Manifest.permission.RECORD_AUDIO)) {
                            //當(dāng)RECORD_AUDIO 權(quán)限獲取成功時,permission.granted=true
                            Log.i("permissions", Manifest.permission.RECORD_AUDIO + ":" + permission.granted);
                        }
                        if (permission.name.equals(Manifest.permission.CAMERA)) {
                            //當(dāng)CAMERA權(quán)限獲取成功時,permission.granted=true
                            Log.i("permissions", Manifest.permission.CAMERA + ":" + permission.granted);
                        }
                    }
                });

效果圖如下:


Activity界面
點(diǎn)擊開啟定位+視頻+相機(jī)權(quán)限按鈕
點(diǎn)擊開啟定位+視頻+相機(jī)權(quán)限按鈕
點(diǎn)擊開啟定位+視頻+相機(jī)權(quán)限按鈕

注意

⑴由于在請求權(quán)限的過程中app有可能會被重啟,所以權(quán)限請求必須放在初始化的階段,比如在Activity.onCreate/onResume, 或者
View.onFinishInflate方法中。如果不這樣處理,那么如果app在請求過程中重啟的話,權(quán)限請求結(jié)果將不會發(fā)送給訂閱者即subscriber。
⑵上圖可知多次權(quán)限申請的兩種方式效果圖完全一樣,都是一項(xiàng)項(xiàng)彈出,一項(xiàng)項(xiàng)讓用戶選擇。它倆的區(qū)別“同時”和“分別”是體現(xiàn)在代碼結(jié)果的獲取上的.“同時”是等用戶把每項(xiàng)權(quán)限彈框都選擇完后執(zhí)行結(jié)果回調(diào),所有的權(quán)限統(tǒng)一處理,只要有一項(xiàng)不允許,就走false,“分別”也是等用戶把每項(xiàng)權(quán)限彈框都選擇完后執(zhí)行結(jié)果回調(diào),但所有的權(quán)限不統(tǒng)一處理,每項(xiàng)權(quán)限都有一個結(jié)果回調(diào)處理方法。這塊不要弄混
⑶權(quán)限的彈框樣式不同的手機(jī)不同的效果圖,這些Dialog是各個手機(jī)廠商定制的,不能由開發(fā)者定制。

關(guān)鍵部分就這些了,挺簡單的。動態(tài)權(quán)限授權(quán)雖然給程序員帶來了一些麻煩, 但是對用戶的安全性來講還是很有必要的, 我們也應(yīng)該歡迎, 畢竟每個程序員都是半個產(chǎn)品經(jīng)理。

在開發(fā)過程中遇到一個問題,就是在vivo(垃圾! 胡改八改?。9 (6.0.1 API 23)手機(jī)上運(yùn)行程序,測試出的結(jié)果是6.0以下手機(jī)的邏輯,現(xiàn)在還在研究中,有知道的大神麻煩告知一下。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容