2017-12-21 Android runtime permission 收尾1

Android runtime permission 收尾1

demo 環境:

  • OS: mac OS 10.13.1 (17B1003)
  • android studio 3.0
  • gradle 4.1
  • Code Base

RuntTimePermissionTestActivity.java html

classpath 'com.android.tools.build:gradle:3.0.0'
compile 'com.android.support:appcompat-v7:26.+'java

背景

問題:app 在 Android 6.0 (API level 23)上,使用SmsManager發送短信出錯:

ERROR:   java.lang.SecurityException: Sending SMS message: uid 10078 does not have android.permission.SEND_SMS.

問題緣由:

查android 官網發現,When run on Android 6.0 (API 23),or targetSdkVersion Android 6.0 (API 23),danger permission須要動態申請權限。android

問題解決:

  • 方案1 將targetSDKVersion人爲地降到小於23,這樣就變成了仍是默認使用權限,可是這種並非Google所推薦使用的。治標不治本。
  • 方案2 When Android 6.0 (API 23),or targetSdkVersion Android 6.0 (API 23),實現APP支持運行時權限

正文

1. normal permissions and dangerous permissions

Android 6.0 (API 23) 開始,引入了normal permissions(普通權限) 和dangerous permissions(危險權限) 的概念。git

  • [PASS]不管是normal permissions ,仍是dangerous permissions ,都必須list在manifest文件中。
  • [PASS]For dangerous permissions,當運行OS <=Android 5.1 (API level 22),或者targetSdkVersion <=22時,不須要作特殊的處理。由於在安裝時會用戶必須受權權限。若是用戶不受權,system不會安裝app。

安裝時用戶必須受權權限

  • [PASS]For dangerous permissions,當運行OS >= Android 6.0 (API level 23),或者targetSdkVersion >=23時,要作特殊的處理。使用每一個dangerous permissions以前, 必須request。

運行時受權權限

  • From android 官網:

On all versions of Android, your app needs to declare both the normal and the dangerous permissions it needs in its app manifest, as described in Declaring Permissions. However, the effect of that declaration is different depending on the system version and your app's target SDK level:github

● If the device is running Android 5.1 (API level 22)or lower, or your app's target SDKis 22 or lower(targetSdkVersion <=22) : If you list a dangerous permission in your manifest, the user has to grant the permission when they install the app; if they do not grant the permission, the system does not install the app at all.
● If the device is running Android 6.0 (API level 23) or higher, and your app's target SDK is 23 or higher (targetSdkVersion >=23): The app has to list the permissions in the manifest, and it must request each dangerous permission it needs while the app is running. The user can grant or deny each permission, and the app can continue to run with limited capabilities even if the user denies a permission request.
Note: Beginning with Android 6.0 (API level 23), users can revoke permissions from any app at any time, even if the app targets a lower API level. You should test your app to verify that it behaves properly when it's missing a needed permission, regardless of what API level your app targets.

Note: Your app still needs to explicitly request every permission it needs, even if the user has already granted another permission in the same group. In addition, the grouping of permissions into groups may change in future Android releases. Your code should not rely on the assumption that particular permissions are or are not in the same group.安全

https://developer.android.google.cn/training/permissions/requesting.htmlapp

2. dangerous permissions

https://developer.android.com/guide/topics/permissions/requesting.html#normal-dangerousless

 dangerous permissions

3. For dangerous permissions,when not needs request runtime permission?

When device is running Android 6.0 (API level 23) or higher, and your app's target SDK is 23 or higher (targetSdkVersion >=23)異步

  • Your app only needs permissions for actions that it performs directly.
  • Your app does not need permission if it is requesting that another app perform the task or provide the information.

4. How to request dangerous permissions?

重要的函數:

  • checkSelfPermission():檢查是否已經具備了相關權限。
  • shouldShowRequestPermissionRationale():判斷是否須要向用戶解釋,爲何須要這些權限。
  • requestPermissions() :申請相關權限

Sample

RuntTimePermissionTestActivity.javaide

RuntTimePermissionTestActivity.java 以SEND_SMS(發送短信)、RECEIVE_SMS(使用SMSReceiver接收短信)、permission_group.SMS 爲例子。

android SMS 是什麼縮寫

SMS是Short Messaging Service(短消息服務)的縮寫,是一種使用移動設備能夠發送和接收文本信息的技術。

以例子說明如何request runtime permission.

Step1,檢查Permission是否granted。

PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(getContext(), permission)

true,已經受權,直接執行doSendMessage。
false,沒有受權,執行下一步檢查是否須要顯示請求權限緣由。

  • (1) 檢查一個Permission是否granted:
PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(getContext(), Manifest.permission.SEND_SMS)
  • (2) 檢查多個Permissions是否granted:
PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(getContext(), Manifest.permission.SEND_SMS) 
&&
PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECEIVE_SMS)
  • (3) 檢查permission_group是否granted:
PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(getContext(), Manifest.permission_group.SMS)

Step2,檢查Permission是否顯示請求權限緣由。

true,須要顯示請求權限緣由,顯示一個dialog/Snarkbar,容許用戶作選擇。
false,不須要顯示請求權限緣由,直接執行requestPermissions。

  • (1)檢查一個Permission是否顯示請求權限緣由:
ActivityCompat.shouldShowRequestPermissionRationale(getContext(), Manifest.permission.SEND_SMS)
  • (2)檢查多個Permissions是否顯示請求權限緣由:
ActivityCompat.shouldShowRequestPermissionRationale(getContext(), Manifest.permission.SEND_SMS) 
&&
ActivityCompat.shouldShowRequestPermissionRationale(getContext(), Manifest.permission.RECEIVE_SMS)
  • (2)檢查permission_group是否顯示請求權限緣由:
ActivityCompat.shouldShowRequestPermissionRationale(getContext(), Manifest.permission_group.SMS)

Step3,Request permission

ActivityCompat.requestPermissions 調用後,在onRequestPermissionsResult()中根據requestCode判斷是否grandted,並作對應處理。

public static void requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)

說明:
(1) permissions:是一個String[],能夠放一個或多個permission。
(2) requestCode:在onRequestPermissionsResult()中處理。每次請求request permissions時requestCode設置要不一樣。

  • (1) request一個permission:
ActivityCompat.requestPermissions(getContext(), new String[]{Manifest.permission.SEND_SMS}, REQUEST_CODE_4_REQUEST_PERMISSIONS_4_SEND_MESSAGE);
  • (2) request多個permissions:
ActivityCompat.requestPermissions(getContext(), new String[]{Manifest.permission.SEND_SMS,Manifest.permission.RECEIVE_SMS}, REQUEST_CODE_4_REQUEST_PERMISSIONS_4_SEND_RECEIVE_MESSAGE);
  • (3)使用permission_group申請:
ActivityCompat.requestPermissions(getContext(), new String[]{Manifest.permission_group.SMS}, REQUEST_CODE_4_REQUEST_PERMISSIONS_GROUP_4_SMS)

Step4 處理 request Permission返回結果

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.d(TAG, "onRequestPermissionsResult: requestCode=" + requestCode);
        switch (requestCode) {
            case REQUEST_CODE_4_REQUEST_PERMISSIONS_4_SEND_MESSAGE: {
                onRequestPermissionsResult4SendMessage(requestCode, permissions, grantResults);
                break;
            }

            default:
                // other 'case' lines to check for other permissions this app might request
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                break;
        }
    }

  private void onRequestPermissionsResult4SendMessage(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (REQUEST_CODE_4_REQUEST_PERMISSIONS_4_SEND_MESSAGE != requestCode) {
            Log.e(TAG, "onRequestPermissionsResult4SendMessage: requestCode is wrong");
            return;
        }
        // Check if the only required permission has been granted
        // If request is cancelled, the result arrays are empty.
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
           // 受權了,執行task,此處是發送短信操做。
            doSendMessage();
            // Send message permission has been granted, Message can be sent.
            Log.i(TAG, "Send message permission has now been granted. Can send message.");

            // 受權了,給出對應提示信息。
            showPermissionAvailable4SendMessage();

        } else {
            // permission denied, boo! Disable the functionality that depends on this permission.
            Log.i(TAG, "Send message permission was NOT granted.");
           // 沒有被受權,給出對應提示信息。
            showPermissionsNotGranted();
        }
    }

5. request runtime permission 流程總結

process chart for request runtime permission

6 總結

    • 不要使用Permission Group來request dangerous permissions。緣由是未來API 不一樣,permissions groups可能不一樣。
    • 僅僅申請須要的權限,申請太多不要的dangerous permissions可能會使得用戶感受app不安全,致使用戶卸載app。
    • Show dangerous permissions rationale,簡潔明瞭。
    • 任什麼時候候APP都要在執行危險權限前去檢查是否具備相關權限,即便剛剛執行過這項操做,由於用戶頗有可能去設置應用中關閉了相關權限。
    • [pass]申請權限時,若是組內有別的權限已經得到了用戶受權,系統再也不彈出詢問對話框,而是自動受權該權限。

    例如,申請Manifest.permission.SEND_SMS權限時,用戶已經受權了Manifest.permission.SEND_SMS權限,系統則會自動受權group SMS中的全部權限,再也不詢問用戶;

    • 兼容問題:Use the Android Support Library to check for, and request, permissions.

    checkSelfPermission和requestPermissions從API 23才加入,低於23版本,須要在運行時判斷。

    使用Support Library v4中提供的方法,能夠避免判斷。

    ContextCompat.checkSelfPermission
    ActivityCompat.requestPermissions
    ActivityCompat.shouldShowRequestPermissionRationale

    例如:使用ContextCompat.checkSelfPermission , 而不是Activity.checkSelfPermission()。google 官網推薦這種方式。

    6 遺留的問題:

    1. Android官方開發指導還提到一點,爲避免給用戶帶來糟糕的用戶體驗,shouldShowRequestPermissionRationale 這裏的解釋說明應該是異步的,不要阻塞用戶的操做。
      沒有看懂是什麼意思。
      你們知道了,能夠告訴我,我會很是感謝。

    7 下一步的計劃:

    • 準備建立一個BaseActivity,或一個工具類,把request runtime permission 代碼抽離。
    • 寫關於Runtime permission library的總結。

    Ref

    -https://developer.android.com/guide/topics/permissions/requesting.html#normal-dangerous
    PS: 中國訪問, 把com -> google.cn 就能夠打開了。

    謝謝瀏覽

    歡迎你們交流,留言、指點。

    文中有誤,歡迎你們指出來。我會更正過來。

    個人GitHub

    相關文章
    相關標籤/搜索