1. 程式人生 > >Android 動態許可權適配方案總結

Android 動態許可權適配方案總結

        從Android6.0 棉花糖開始,Google調整了應用的許可權申請方案。調整之後的使用者授權,不僅僅體現在使用者安裝應用之時,更為重要的是,對於許可權層級為危險的使用者許可權,需要在應用執行時向其授權,簡稱,動態授權。動態授權方案不僅可以簡化應用安裝過程,使用者在安裝或者更新過程中可以不需要授予許可權,而且更為重要的是可以實現使用者對應用功能進行更多的控制,從而達到增強使用者隱私的保護。

      許可權分類:

        動態授權方案將系統許可權進行了分級。簡單點說可以分為普通許可權和危險許可權。普通許可權的授權方式跟Android6.0版本之前一樣,在Manifest檔案中宣告,使用者在安裝或者更新軟體過程中一次性實現授權。危險許可權授權方案需要在Manifest檔案中宣告,在需要使用的地方,通過呼叫官方提供的Api主動申請。具體許可權定義如下:

        正常許可權:不會直接給使用者隱私權帶來風險。如果您的應用在其清單中列出了正常許可權,系統將會自動授予該許可權。
        危險許可權:會授予應用訪問使用者機密資料的許可權。如果您的應用在其清單中列出了危險許可權,則使用者必須明確批准您的應用使用這些許可權。

        動態授權是針對危險許可權的,那麼危險許可權有哪些呢?通過adb 命令可以

Microsoft Windows [版本 6.1.7601]
版權所有 (c) 2009 Microsoft Corporation。保留所有權利。
C:\Users\Administrator>adb shell
[email protected]
:/ $ pm list permissions -g -d Dangerous Permissions: // 涉及讀寫聯絡人,訪問賬戶 group:android.permission-group.CONTACTS permission:android.permission.WRITE_CONTACTS permission:android.permission.GET_ACCOUNTS permission:android.permission.READ_CONTACTS // 涉及電話操作 group:android.permission-group.PHONE permission:android.permission.READ_CALL_LOG permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.WRITE_CALL_LOG permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS permission:com.android.voicemail.permission.ADD_VOICEMAIL // 涉及日曆資訊的操作(使用者日程安排) group:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDAR // 涉及相機操作 group:android.permission-group.CAMERA permission:android.permission.CAMERA // 涉及使用手機感測器操作 group:android.permission-group.SENSORS permission:android.permission.BODY_SENSORS // 涉及使用者地理位置資訊的操作 group:android.permission-group.LOCATION permission:android.permission.ACCESS_FINE_LOCATION permission:android.permission.ACCESS_COARSE_LOCATION // 涉及儲存卡的讀寫操作 group:android.permission-group.STORAGE permission:android.permission.READ_EXTERNAL_STORAGE permission:android.permission.WRITE_EXTERNAL_STORAGE // 涉及多媒體資訊的操作哦 group:android.permission-group.MICROPHONE permission:android.permission.RECORD_AUDIO // 涉及SMS卡的操作 group:android.permission-group.SMS permission:android.permission.READ_SMS permission:android.permission.RECEIVE_WAP_PUSH permission:android.permission.RECEIVE_MMS permission:android.permission.RECEIVE_SMS permission:android.permission.SEND_SMS permission:android.permission.READ_CELL_BROADCASTS ungrouped: permission:com.xiaomi.xmsf.permission.PAYMENT // 小米 下載而不顯示通知 permission:miui.permission.ACCESS_BLE_SETTINGS // 小米 涉及到使用者設定的操作
[email protected]
:/ $

        以上這些就是危險許可權,他們都以許可權組的形式組織。所謂的許可權組的意思是,只要改組中有任何一個許可權被授予,則改組中的其他許可權也同樣會被授予。在表中的最後兩個許可權是小米ROM自己的,Google官方的危險許可權表中沒有涉及到。

      動態授權的過程可以大概分為以下幾個步驟:

      1.檢查許可權:ContextCompat.checkSelfPermission();

        如果您的應用中需要危險許可權,則每次執行需要這一許可權的操作時,都必須檢查是否具有該許可權,使用者始終可以自由的呼叫次許可權,因此,即使應用昨天使用了相應,他不能假設自己今天仍然具有該許可權。

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

      2.申請許可權:ContextCompat.requestPermissions();

        如果應用需要Manifest清單中的危險許可權,那麼他必須要求使用者授予該許可權。並且在某些情況下,我們需要讓使用者瞭解應用為什麼需要某些許可權。最簡單的例子是,當我們需要相機拍照的時候需要相機許可權,這是很正常的需求,但是當我們需要儲存照片資訊的時候,需要使用者的地理位置資訊,這個許可權對於使用者來說就顯得很不理解。針對這種現狀,Google也為我們提供了很實用的Api ,ContextCompat.shouldShowRequestPermissionRationab-如果應用之前請求過次許可權但使用者拒絕了,則該方法返回true。如果使用者不僅拒絕上次的請求許可權,而且勾選了“不再提示”,則該方法返回false。

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

       3.處理申請結果:onRequestPermissionResult();

      當應用請求許可權時候,系統將向用戶顯示一個對話方塊。當用戶相應的時候,系統將呼叫onRequstPermissionsResult() 方法。向其傳遞使用者的相應。我們需要在我們自己Activity中複寫該方法。並且對使用者操作的反饋做處理。

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

        實際上的動態授權方案

        如果你瞭解了上面這些內容就以為了解了動態許可權申請。那隻能說明你太天真。需要說明的是,以上這些內容全部都是來自Google的官方Demo。國內真正的Android開發環境,你心裡沒有點數嗎?國內Rom客製化太多了,Google 原生API被改的很嚴重,這對於我們要去完美適配,真的是一種噩夢。舉幾個簡單的例子,你就知道當前的現狀有多麼的慘不忍睹。以小米為例:

        1.小米系統在Android原生的動態許可權申請的基礎下,還有自己的使用者授權模組。(感覺很正常?)。什麼意思?先看小米的授權模組UI:


        可以看到小米的授權模組中,對許可權操作可以分為允許,詢問,拒絕。當我們第一次開啟應用的時候,預設是詢問狀態,在該狀態下,我們呼叫requestPermission()方法會彈出系統詢問框.在彈出系統授權框後,只要你操作了(拒絕或者允許),你永遠也不要想著在以後能看到授權框了,除非你過來設定這邊更改為“詢問”模式。不然無論你再呼叫幾次requestPermissions(),都是直接走回調OnRequestPermissionResult。總結為一句話,小米客制的授權模組是凌駕於Google的授權模組之上的,具體表現為以下幾個方面:

    1.所謂的動態授權,Google的原意為應用每次需要涉及到敏感許可權,都是需要動態申請的。(原話:即使應用昨天使用了相應,他不能假設自己今天仍然具有該許可權)。但是在小米系統中,只要通過一次授權之後,每次檢查許可權都是返回第一次授權的結果,換句話說,只要昨天你第一次同意了使用相機,那之後每一次我都是有許可權使用相機的。如果有一天你不在想賦予應用相機許可權,那麼你必須要到相應的設定介面去操作。什麼叫做動態。小米的做法是“一勞永逸”.

    2.Google動態授權還表現在如果使用者上次拒絕授權,或者說使用者上次關閉了授權提示,我們是可以通過官方提供的api shouldShowRequestPermissionRationab(),來判斷上次使用者的操作的,並且根據結果在增加相應的提示,或者引導使用者下一步的操作。這其實是豐富了我們與使用者的互動過程,極大提升了使用者的體驗。但是這些在小米的系統裡面全沒了,shouldShowRequestPermissionRationab()這個方法在小米系統中沒有用的,永遠返回的是false。

    3.在處理授權回撥中,對於授權的結果無非是PackageManager.PERMISSION_GRANTED,PackageManage-

_PERMISSION_DENIED,但是在小米的一些機型中,OnRequestPermissionsResult()回撥中返回的結果並不是這兩者。

    4.在小米的系統中,有些許可權顯得很混亂。比如說儲存卡讀寫許可權。沒有動態去申請,直接檢查許可權,居然是已經授權了。但是有時真正的去讀寫內部儲存卡的時候又會跑出異常,讓人很捉摸不透。

    當然小米的系統缺陷遠遠不止這些。針對這些情況,總結了自己的一套許可權申請方案。具體邏輯如下。


           跟著思路走

        1.定義許可權回撥

    // 這是許可權回撥介面;
    public interface OnPermissionCallback {
        // 授權;
        void onGranted();

        // 拒絕;
        void onDenied();
    }

        2.請求許可權,這個是供外界呼叫的方法。引數為需要申請的許可權,和許可權回撥。如果申請的許可權已經被使用者授權,則直接走授權回撥,否則去申請許可權。

/**
     * 要求授權;
     */
    public void requestPermissions(@NonNull String[] permissions, @NonNull OnPermissionCallback
            callback) {
        if (permissions.length < 1) {
            return;
        }
        mPermissions = permissions;
        mPermissionCallback = callback;
        if (checkPermissions(permissions)) {
            // 有許可權
            if (mPermissionCallback != null) {
                mPermissionCallback.onGranted();
            }
        } else {
            // 無許可權;
            ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
        }
    }

        3.申請許可權後,程式會走OnRequestPermissionsResult()的回撥。在回撥中繼續我們的邏輯;

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (checkPermissionResult(grantResults)) {
                // 全部授權
                if (mPermissionCallback != null) {
                    mPermissionCallback.onGranted();
                }
            } else {
                if (shouldShowRequestPermissionsRationale(permissions)) {
                    // 使用者選擇了拒絕;
                    ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
                } else {
                    // 使用者選擇了不再提示;
                    showPermissionRationale(null, null);
                }
                // 沒有全部授權;
                if (mPermissionCallback != null) {
                    mPermissionCallback.onDenied();
                }
            }

        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

        在onRequestPermissionResult方法中,如果申請的所有許可權都已經授權,則走授權回撥。沒有的話,再通過shouldRequestPermissionRationale()方法繼續分析使用者的針對我們的上次授權的操作。需要說明的一點是shouldRequestPermissonsRationale()這個方法的返回值,如果使用者針對我們的上次請求許可權拒絕的話,會返回true,如果使用者針對我們的上次請求許可權不僅做了拒絕的操作,而且還點選了“不再提醒”(就是以後不會再跳出授權框)的選擇框,則該方法返回false。針對這個情況,如果使用者只是單純的拒絕授權,我們在這裡進行第二次的請求許可權。如果使用者選擇“不再提醒”,我們就彈出一個自定的Dialog,引導使用者去設定介面授權。

        這裡的Dialog,我直接用了SweetDialog的普通樣式。

    // 跳出 提示介面;
    private void showPermissionRationale(@Nullable String title, @NonNull String content) {
        SweetAlertDialog dialog = new SweetAlertDialog(this, SweetAlertDialog.NORMAL_TYPE);
        dialog.setCancelable(false);
        dialog.setTitleText(TextUtils.isEmpty(title) ? getString(R.string.permission_title) : title)
                .setContentText(TextUtils.isEmpty(content) ? getString(R.string
                        .permission_content) : content)
                .setConfirmButton(R.string.dialog_ok, new SweetAlertDialog.OnSweetClickListener() {
                    @Override
                    public void onClick(SweetAlertDialog sweetAlertDialog) {
                        sweetAlertDialog.dismissWithAnimation();
                        gotoSetting();
                    }
                })
                .setCancelButton(R.string.permission_cancle, new SweetAlertDialog
                        .OnSweetClickListener() {
                    @Override
                    public void onClick(SweetAlertDialog sweetAlertDialog) {
                        sweetAlertDialog.dismissWithAnimation();
                        if(mPermissionCallback!=null){
                            mPermissionCallback.onDenied();
                        }
                    }
                })
                .show();

    }

    

        在Dialog中如果使用者選擇取消的話,則表示這次授權失敗了,直接走失敗的回撥。如果使用者選擇"ok”的話,則引導使用者到設定詳情介面去手動授權。

  // 跳轉到設定介面;
    private void gotoSetting() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        intent.setData(Uri.fromParts("package", getPackageName(), null));
        startActivityForResult(intent, PERMISSION_REQUEST_CODE);
    }

       需要注意的是這裡引導使用者到設定介面的方式是通過startActivityForResult()的方式。使用者在從設定介面回到應用的時候,會走onActivityResult()方法,在這裡我們需要再一次的對許可權做判斷。

  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (mPermissions != null && checkPermissions(mPermissions)) {
                if (mPermissionCallback != null) {
                    mPermissionCallback.onGranted();
                } else {
                    mPermissionCallback.onDenied();
                }
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }

    }

    以上的這些都是在我的Activity的基類中完成的。我的所有activity繼承這個基類。在需要動態許可權的地方只需要一句程式碼就可以完成動態許可權的申請了。

        requestPermissions(new String[]{Manifest.permission.CAMERA}, new OnPermissionCallback() {
            @Override
            public void onGranted() {
                Log.d("Permission","onGranted");
            }

            @Override
            public void onDenied() {
                Log.d("Permission","onDenied");
            }
        });
       一句程式碼搞定。大功告成。後面的事情就是看看在各個版本的手機上適配問題了。這些問題太頭疼了。當然如果覺得用自己的方法去適配動態許可權有困難的話,也可以用第三方的開源框架。github上還是有挺多的。


相關推薦

Android 動態許可權方案總結

        從Android6.0 棉花糖開始,Google調整了應用的許可權申請方案。調整之後的使用者授權,不僅僅體現在使用者安裝應用之時,更為重要的是,對於許可權層級為危險的使用者許可權,需要在應用執行時向其授權,簡稱,動態授權。動態授權方案不僅可以簡化應用安裝過程,

Android O 8.0 執行時許可權方案

一.序 在之前的文章聊聊Android M 6.0 的執行時許可權曾提到過,Google Play開發者明年勢必要努力將targetsdkversion升級到26的過程中。官網的文件:向 Android 8.0 遷移應用 已經給大家了一個升級指南。當然動態許

android屏幕方案

得到 glob src count span imageview ica height void 曾經看了android的屏幕適配方案,有非常多種。當中自己用到的一種是:先找一款主流的分辨率的android機,如:1080*1920的分辨率做基準,然後在這個基準

業務線移動端方案總結

mona space ack 窗口 reset 問題 3.3 calc 防止 /** * sass的基本的使用reset.scss * base.scss * DOMContentLoaded:當Dom加載完成

Android6.0動態許可權&XMPermissions

Android6.0動態許可權 簡介 從 Android 6.0(API 級別 23)開始,使用者開始在應用執行時向其授予許可權,而不是在應用安裝時授予。此方法可以簡化應用安裝過程,因為使用者在安裝或更新應用時不需要授予許可權。它還讓使用者可以對應用的功能進行

Android 通知相關總結

一、高版本適配之渠道        targetVersion為Android 8.0及以上的版本,需要建立通知的渠道(channel),否則就不會顯示通知。(注:渠道的建立不會影響低版本,低版本會忽略渠道) private fun createNotificationC

Android劉海屏方案

隨著iPhone X釋出,國內一些廠商也推出了劉海屏手機,即將釋出的Android p也提供了對劉海屏的支援。so,我們的app也要提前做好適配。 什麼是劉海屏? 螢幕的正上方居中位置(下圖黑色區域)會被挖掉一個孔,螢幕被挖掉的區域無法正常顯示內容,這種型別的螢幕就是劉海屏,也有其他叫法:挖

今日頭條方案解讀及常用方案總結

前段時間今日頭條開源了螢幕適配方案,前段時間大體的看了一下,正好這兩天有時間,仔細研究一下和總結一下適配方案。 在瞭解適配方案之前,先來一遍dp,dpi,density概念吧! px : 是pixel的縮寫,pixel即畫素,平時所說的裝置的解析度是多少,這裡的單位就是px。 d

Android開發螢幕方案

  由於Android系統的開放性,任何使用者、開發者、硬體廠商和運營商都可以對Android系統和硬體進行定製,修改成他們自己所需要的樣子。使得隨著Android裝置的增多,裝置碎片化、系統碎片化、螢幕尺寸碎片化和螢幕碎片化的程度也在不斷加深; 這種碎片化達

一種粗暴快速的Android全螢幕方案

熱文導讀 | 點選標題閱讀作者:firedamp來源:http://www.apkbus.com

[Android] 一種粗暴快速的 Android 全螢幕方案

1 現狀 由於Android碎片化嚴重,螢幕適配一直是開發中較為頭疼的問題。面對市面上五花八門的螢幕大小與解析度,Android基於dp與res目錄名稱來適配的方案已無法滿足一次編寫全螢幕適配的需求,為了達到最優的視覺效果,開發過程中總是需要花費較多資源進行適配。也有開

國產 Android 許可權申請最佳方案——permissions4m

筆者發現國產手機有許多適配缺陷,例如: ActivityCompat.shouldShowRequestPermissionRationale(Activity, String) 無法彈出許可權申請對話方塊 明明使用者點選拒絕授權,卻回撥的是許可權申請成功方法 只能有一次許可權是否授予選擇,拒絕後就

Android 目前最穩定和高效的UI方案

設計師 data ID 設置 評論 系統軟件 放縮 可用 github Android系統發布十多年以來,關於Android的UI的適配一直是開發環節中最重要的問題,但是我看到還是有很多小夥伴對Android適配方案不了解。剛好,近期準備對糗事百科Android客戶端設計一

Android 劉海屏總結

一、簡介 隨著 Apple 釋出 iPhone X 之後,各大手機廠商也開始模仿這種劉海屏的設計,而且劉海屏手機的使用者也是越來越大,前段時間將專案進行了所有主流廠商的劉海屏手機的適配,以便讓劉海屏手機的使用者也能有更好的體驗。 二、劉海屏造成的 UI 顯示問題 劉海屏手機因為比平常的手機多了一塊頂部的

Android動態許可權總結

從Android6.0開始,Android系統提供動態申請許可權的機制, APP在使用危險許可權時,需要使用者的授權才可進一步操作。 許可權申請方式 Android系統中許可權申請的方式有兩種,如下圖所示: 靜態申請 Android6.0以前的系統(API < 23)採用的這種方

Android 開啟WiFi 熱點的一些方案

前言 博主又來更新文章了,有點墨跡哈,很久才來一篇文章,不講究文章量的大小,只在乎內容的實用性,幫助每一個開發者,避過一些不必要的坑,廢話不多說了,文章的內容就是說各種版本手機通過程式碼如何開啟熱點,文章也比較簡潔,不會有太多的囉嗦話 不同版本開啟熱點的方式 首先呢,通過Android 對應A

Android螢幕實踐總結

##名詞解釋 ####px (pixels) 最為熟悉的畫素,設計圖以此為單位標註; ####dp或dip (device independent pixels) 裝置獨立畫素,與裝置螢幕有關,Android的UI標註以此為單位可最大限度適配不同解析度; ####sp (scaled p

rem動態螢幕解決方案

(function (doc, win) { var docEl = doc.documentElement var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize' var reca

Android螢幕方案

目錄介紹 1.螢幕適配定義 2.相關重要的概念 2.1 螢幕尺寸[物理尺寸] 2.2 螢幕解析度[px] 2.3 螢幕畫素密度[dpi] 2.4 dp、dip、dpi、sp、px 2.5 mdpi、hdpi、xdpi、xxdpi 2.6 獲取螢幕解析度[寬

Android 螢幕方案

前言 本文為自身的總結與結合其他文章引用而成,分別為: wangwangli6: Android開發:最全面、最易懂的Android螢幕適配解決方案 https://blog.csdn.net/wangwangli6/article/details/6