Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之6.0運行時權限處理

Android開發之6.0運行時權限處理

編輯:關於Android編程

Permission概述

權限分組

權限分為九組,讓用戶授予所有權限由一個單一的行動包括。例如,授權聯系人包括視圖的能力和編輯聯系人。

\

默示許可權限:用戶可能會執行一個動作表明明確的意圖,如:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxlbT7FxNXVPC9lbT4gPGVtPtGh1PHSu7j2warPtcjLPC9lbT4gPGVtPr+qyrzSu7j2tee7sLvytszQxTwvZW0+DQo8cD48ZW0+1NrV4tCpx+m/9s/Co6zTw7untcTQ0Lavx+Wz/rXYse3D98HL19S8urXE0uLNvKOstviyu9Do0qrQ7b/Ju/LK2siottS7sL/yoaM8L2VtPjwvcD4NCjxoNSBpZD0="運行時權限">運行時權限

應用程序可以請求權限訪問信息或使用設備在安裝後的任何能力。當用戶需要執行一個應用程序中的一個動作,如使用設備的攝像頭,應用程序可以請求對應的權限。用戶也可以允許或拒絕任何安裝後從Android設置任何應用程序的權限。

\

請求模式

您的權限策略取決於你要求的權限類型的清晰性和重要性。這些模式提供不同權限用戶的方式介紹。指示功能權限直接請求就可以了,有些權限需要附帶說明具體用途,還有一些權限提供記住選擇,避免再次授權,影響用戶體驗

Android的權限請求方式使請求取決於系統版本和系統版本的應用程序的目標:

如果設備運行的是Android 6(API Level 23)或更高,和應用程序的targetSdkVersion是23或更高,應用程序要求的權限由用戶在運行時。用戶可以在任何時間撤銷權限,所以應用程序需要檢查是否有權限在每次運行時。

如果設備運行的是Android 5.1(API Level 22)或更低,或應用程序的targetSdkVersion等於或低於22時,系統會要求用戶授予權限,當用戶安裝的應用程序。如果你添加一個新的權限的應用程序的更新版本,系統會要求用戶授予權限時,用戶的應用程序更新。一旦用戶安裝該應用程序,他們可以撤銷許可的唯一途徑是通過卸載應用程序。

拒絕權限

每當一個權限被拒絕應向用戶解釋使用該權限的具體用途,保證功能要求的權限總是行為為目的,應用程序應該說是需要許可,允許它提供了一種方法。而權限被拒絕的途徑以下兩種:

由用戶拒絕權限請求 權限是默默的否認沒有警告因為一旦選擇“不要再問”從以前的權限請求用戶

Permission分組

adb查看權限分組

在概述裡面提過一點權限分組,其實我們可以通過adb命令查看更為詳盡的分組信息,cmd切換到adb目錄下運行下面命令

adb shell pm list permissions -d -g

group:android.permission-group.PHONE_CALLS

permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS

group:android.permission-group.WALLPAPER

//…………..略………….

group:android.permission-group.MICROPHONE

ungrouped:
permission:org.simalliance.openmobileapi.SMARTCARD permission:com.android.permission.ENABLE_HWQRCODEDISPATCHER permission:com.huawei.phoneservice.permission.SMART_FAQS_ACCESS permission:com.huawei.camera.permission.QRCODE_SCAN permission:com.huawei.phoneservice.permission.CENTER_SERVICE_ACCESS

權限還可以分為危險權限和正常權限,還有特殊權限、自定義權限。正常權限(PROTECTION_NORMAL)沒有大的風險對用戶的隱私或安全讓應用程序的權限。例如,用戶可以合理地想知道一個應用程序可以讀取他們的聯系信息,所以用戶必須授予該權限的明確。相比之下,在允許一個應用程序,使設備沒有很大的風險,因此,允許指定為正常

如果一個應用程序聲明其表現,它需要一個正常的權限,系統自動授予應用程序的權限在安裝的時候。該系統不提示用戶給予正常的權限,用戶不能撤銷這些權限。

正常權限列表

\


Permission申請

Manifest添加權限申請聲明

Permission官方實踐

官網上提供了Permission相關的兩個zip文件

RuntimePermissionsBasic項目源碼大概過了一遍,核心部分用到了ActivityCompat、ContextCompat、OnRequestPermissionsResultCallback。源碼中以Camera為例,請求打開照相機,先要檢測是否擁有Camera權限,返回對應的code值,在PackageManager裡面有注解PermissionResult

 /** @hide */
    @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED})
    @Retention(RetentionPolicy.SOURCE)
    public @interface PermissionResult {}

    /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has been granted to the given package.
     * 允許了請求的權限
     */
    public static final int PERMISSION_GRANTED = 0;

    /**
     * Permission check result: this is returned by {@link #checkPermission}
     * if the permission has not been granted to the given package.
     * 拒絕了請求的權限
     */
    public static final int PERMISSION_DENIED = -1;

    /**
     * Signature check result: this is returned by {@link #checkSignatures}
     * if all signatures on the two packages match.
     * 簽名匹配
     */
    public static final int SIGNATURE_MATCH = 0;

   //...........略................

請求權限先判斷是否已經獲得了該權限如果沒有,需要闡述獲取權限理由,官方demo用的Snackbar彈出提示,如果ok確認了獲取權限

 private void showCameraPreview() {
        // BEGIN_INCLUDE(startCamera)
        // Check if the Camera permission has been granted
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            // Permission is already available, start camera preview
            Snackbar.make(mLayout,
                    "Camera permission is available. Starting preview.",
                    Snackbar.LENGTH_SHORT).show();
            startCamera();
        } else {
            // Permission is missing and must be requested.
            requestCameraPermission();
        }
        // END_INCLUDE(startCamera)
    }

ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)檢查權限方法源自於父類ContextCompat,我們也可以直接調用它。如果某些權限必須獲取,那麼我們需要附加闡述理由,Snackbar彈出讓用戶選擇確認了重新獲取選項,如果非必須權限,則Snackbar提示information即可

 private void requestCameraPermission() {
        // Permission has not been granted and must be requested.
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.CAMERA)) {
            // Provide an additional rationale to the user if the permission was not granted
            // and the user would benefit from additional context for the use of the permission.
            // Display a SnackBar with a button to request the missing permission.
            Snackbar.make(mLayout, "Camera access is required to display the camera preview.",
                    Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // Request the permission
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.CAMERA},
                            PERMISSION_REQUEST_CAMERA);
                }
            }).show();

        } else {
            Snackbar.make(mLayout,
                    "Permission is not available. Requesting camera permission.",
                    Snackbar.LENGTH_SHORT).show();
            // Request the permission. The result will be received in onRequestPermissionResult().
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
                    PERMISSION_REQUEST_CAMERA);
        }
    }

透過請求權限方法我們可以發現,Activity需要實現interface OnRequestPermissionsResultCallback,回調函數onRequestPermissionsResult處理回調結果

 public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final int requestCode) {
        if (Build.VERSION.SDK_INT >= 23) {
            ActivityCompatApi23.requestPermissions(activity, permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    final int[] grantResults = new int[permissions.length];

                    PackageManager packageManager = activity.getPackageManager();
                    String packageName = activity.getPackageName();

                    final int permissionCount = permissions.length;
                    for (int i = 0; i < permissionCount; i++) {
                        grantResults[i] = packageManager.checkPermission(
                                permissions[i], packageName);
                    }

                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                            requestCode, permissions, grantResults);
                }
            });
        }
    }

Activity實現接口 處理回調代碼如下:

  @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,
            int[] grantResults) {
        // BEGIN_INCLUDE(onRequestPermissionsResult)
        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            // Request for camera permission.
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                Snackbar.make(mLayout, "Camera permission was granted. Starting preview.",
                        Snackbar.LENGTH_SHORT)
                        .show();
                startCamera();
            } else {
                // Permission request was denied.
                Snackbar.make(mLayout, "Camera permission request was denied.",
                        Snackbar.LENGTH_SHORT)
                        .show();
            }
        }
        // END_INCLUDE(onRequestPermissionsResult)
    }

RuntimePermissions項目是一個關於Camera和讀寫相關的權限申請的綜合運用,對我們來說提取有用信息非常必要的,就一個PermissionUtil類用於檢測請求權限結果參數


public abstract class PermissionUtil {

    /**
     * Check that all given permissions have been granted by verifying that each entry in the
     * given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
     *
     * @see Activity#onRequestPermissionsResult(int, String[], int[])
     */
    public static boolean verifyPermissions(int[] grantResults) {
        // At least one result must be checked.
        if(grantResults.length < 1){
            return false;
        }

        // Verify that each required permission has been granted, otherwise return false.
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

}

Permission開源庫

關於Permission這一塊呢在github上有幾個比較有名氣的開源庫

MPermissions

Dexter

EasyPermissions

PermissionsDispatcher

洪洋的MPermissions采用的代理、注解方式實現,注解了三種方式:請求權限被允許、請求權限被拒絕、闡述請求權限理由,根據請求權限結果代理的方式執行對應回調,代碼中具體調用方式:

public class MainActivity extends AppCompatActivity
{

    private Button mBtnSdcard;
    private static final int REQUECT_CODE_SDCARD = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);
        mBtnSdcard.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {
        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @PermissionGrant(REQUECT_CODE_SDCARD)
    public void requestSdcardSuccess()
    {
        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }

    @PermissionDenied(REQUECT_CODE_SDCARD)
    public void requestSdcardFailed()
    {
        Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();
    }
}

先不說好壞,我也說不出什麼,只能接著再看,找到最適合自己的就好。PermissionsDispatcher庫過了一遍,也采用了注解的方式,該庫應該是參考了官方源碼,PermissionUtil相比較更為完善。不過對於我來說還不是我喜歡得方式,繼續往下找Dexter,該庫把請求權限結果封裝成對象,並提供了定制Listener通過Builder方式構建,Application初始化,相對來說使用也挺方便,下面是調用方式:

{
PermissionListener dialogPermissionListener =
    DialogOnDeniedPermissionListener.Builder
        .withContext(context)
        .withTitle("Camera permission")
        .withMessage("Camera permission is needed to take pictures of your cat")
        .withButtonText(android.R.string.ok)
        .withIcon(R.mipmap.my_icon)
        .build();
Dexter.checkPermission(dialogPermissionListener, Manifest.permission.CAMERA);

PermissionListener snackbarPermissionListener =
    SnackbarOnDeniedPermissionListener.Builder
        .with(rootView, "Camera access is needed to take pictures of your dog")
        .withOpenSettingsButton("Settings")
        .build();
Dexter.checkPermission(snackbarPermissionListener, Manifest.permission.CAMERA);

//.............略...............
}

EasyPermissions庫根據官方demo,定制Callback回調,同時也有用到注解,在請求權限裡面獲取傳入接口直接判斷滿足條件調用callback回調函數

public static void onRequestPermissionsResult(int requestCode, String[] permissions,
                                                  int[] grantResults, Object object) {

        checkCallingObjectSuitability(object);
        PermissionCallbacks callbacks = (PermissionCallbacks) object;

        // Make a collection of granted and denied permissions from the request.
        ArrayList granted = new ArrayList<>();
        ArrayList denied = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
            String perm = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                granted.add(perm);
            } else {
                denied.add(perm);
            }
        }

        // Report granted permissions, if any.
        if (!granted.isEmpty()) {
            // Notify callbacks
            callbacks.onPermissionsGranted(requestCode, granted);
        }

        // Report denied permissions, if any.
        if (!denied.isEmpty()) {
            callbacks.onPermissionsDenied(requestCode, denied);
        }

        // If 100% successful, call annotated methods
        if (!granted.isEmpty() && denied.isEmpty()) {
            runAnnotatedMethods(object, requestCode);
        }
    }

對於我個人編碼習慣而言,該庫最適合我,在BaseActivity實現回調接口PermissionCallbacks ,並提供protect 方法配置權限列表,我們具體代碼調用(Activity、Fragment)需要就配置一下就ok了,在BaseActivity裡面我們還可以預處理結果,在某種情況下我們可以忽略這些方法的實現。


裝逼獨白

以前,我只需要隨便找到一個可用庫連接,直接使用就可以了,然而並不關心怎麼實現,而今,我覺得我們非常有必要了解代碼的具體實現,還可以學到很多知識,擴展視野,學習大神的編碼方式。也許你會說好困難啊,看不懂!!!其實不然,不懂得google baidu查資料總會明白的,官網也有的是資料,如果你又要說英文好難好難,你可以用有道翻譯谷歌翻譯啊,更何況還有百度翻譯整個網頁,一句話:借口就是借口,只要你想學,總會有辦法,不要讓借口為自己的未來買單。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved