直播系統關於Android動態權限的一些問題和糾正

使用ActivityCompat處理權限需要用到的方法:

ActivityCompat.checkSelfPermission(...)
ActivityCompat.requestPermissions(...)
ActivityCompat.shouldShowRequestPermissionRationale(...)

第一個是檢查某個權限是否授權
第二個是申請權限
第三個是判斷是否需要合理顯示授權對話框。具體含義和用法文末會詳解。

Activity自身也可以處理動態權限,也有這些方法,爲什麼要使用ActivityCompat來處理動態權限?
這也就是爲什麼要引入兼容包的原因。

第一,如果應用的minSdkVersion小於23(我們一般會設置成15或16左右),直接使用Activity來處理動態權限,那麼IDE會提示你這些方法在6.0以下不存在,編譯會不通過。

第二,ActivityCompat做了一些兼容性判斷,比如requestPermissions的實現里加了判斷api 23,在23以下的機器方法不做處理,不彈出對話框。onRequestPermissionsResult回調在23以下是沒有的,除非Activity實現了OnRequestPermissionsResultCallback接口。

看看ActivityCompat.requestPermissions函數的源碼就明白了:

  public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
        if (sDelegate != null
                && sDelegate.requestPermissions(activity, permissions, requestCode)) {
            // Delegate has handled the permission request.
            return;
        }

        if (Build.VERSION.SDK_INT >= 23) {
            if (activity instanceof RequestPermissionsRequestCodeValidator) {
                ((RequestPermissionsRequestCodeValidator) activity)
                        .validateRequestPermissionsRequestCode(requestCode);
            }
            activity.requestPermissions(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);
                }
            });
        }
    }

可以看到,如果系統是23以下就調用activity的申請權限方法,如果系統是23以下的,並且activity又沒有實現OnRequestPermissionsResultCallback接口,那麼這個requestPermissions方法就什麼都沒做。

所謂兼容包,其實就是幫我們判斷了系統版本,針對性地進行處理,防止高版本的api在低版本上不存在而出錯。

ActivityCompat.checkSelfPermission函數源碼:

  public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
    }

可以看到這個方法其實調用的是Context的方法。需要注意的是,Context的checkPermission方法一直都是存在的,並不是23以後才加入的。

因此如果只是檢查權限,不需要使用兼容包,直接調用context的方法即可。

ActivityCompat.shouldShowRequestPermissionRationale函數源碼:

  public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
            @NonNull String permission) {
        if (Build.VERSION.SDK_INT >= 23) {
            return activity.shouldShowRequestPermissionRationale(permission);
        }
        return false;
    }

shouldShowRequestPermissionRationale其實也是調用了Context的方法。

Android 23以下如何檢查權限呢?
上面已經說了,檢查權限方法並不是23以後出現的新方法。一種檢查方法是使用ActivityCompat,調用checkSelfPermission方法。
另一種方法直接使用context提供的方法:checkPermission(String permission, int pid, int uid),需要傳入當前進程id和uid:

context.checkPermission(permission, android.os.Process.myPid(), Process.myUid())

動態權限處理代碼示例

package com.devnn.testdemo;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.widget.Toast;

public class MainActivity extends Activity {
    String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{permission}, 100);
        }else{
            Toast.makeText(this, "已有讀SDK卡權限", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode==100){
            if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
                Toast.makeText(this, "授權成功", Toast.LENGTH_SHORT).show();
            }else if(ActivityCompat.shouldShowRequestPermissionRationale(this,permission)==false){
                Toast.makeText(this, "拒絕並勾選了不再提醒,不會彈了,需要引導用戶去設置頁", Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(this, "拒絕了授權,下次還會再彈", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

記得先要在AndroidManifest.xml裏聲明權限。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

在彈出系統的權限對話框前,彈出自已的對話框,用來說明權限用途,會更加友好一點。直接使用Materia風格的Dialog。

public void showPermissionDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this, android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth);
    builder.setMessage("需要讀SD卡權限,用來獲取照片\n需要獲取手機狀態權限,用來獲取設備號\n需要獲取聯繫人權限,用來...");
    builder.setTitle("權限說明");
    builder.setCancelable(true);
    builder.setPositiveButton("申請", new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, 100);
        }
    });
    builder.show();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章