【问题标题】:How to avoid writing duplicate boilerplate code for requesting permissions?如何避免为请求权限编写重复的样板代码?
【发布时间】:2016-12-29 00:54:52
【问题描述】:

我正在更新我的应用以兼容 Android 6。权限模型在理论上并不是很复杂,但现在我正在实现它,我发现自己在每一个需要权限的活动中都编写了相同的丑陋样板代码。

对于我需要的每个权限,都有一个

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.PERMISSION) !=
                PackageManager.PERMISSION_GRANTED) {
} else {
}

然后在onRequestPermissionsResult 中,我必须检查/过滤每个请求的结果并将其转换为我的活动可以理解的内容。

我现在正在更新我的第二个活动,权限代码与第一个活动非常相似,几乎看起来像是复制粘贴的。行长,代码大同小异,简直难看。

我不想使用第三方解决方案,我已经尝试了一些,但我更愿意完全控制代码。例如,一些库不支持我在项目中使用的 Java 8。

如何避免在我的所有活动中出现大量重复代码?

【问题讨论】:

    标签: android android-6.0-marshmallow android-permissions runtime-permissions


    【解决方案1】:

    由于问题中解释的原因,我不想使用任何可用的库,所以我自己开发了一些东西。

    我所有需要一个或多个权限的活动都继承自处理所有权限相关任务的PermissionActivity

    它的工作原理是您的活动调用

    if (checkHasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { }
    

    来自父类。如果已经授予权限,则代码可以继续。如果没有,父类将请求权限并使用抽象方法和/或一个或多个可重写方法将结果发送给子类。


    父类

    除了messageForRationale()requestCodeForPermission() 中的开关块之外,这个类可以保持不变。更新这些以获得您的应用所需的权限。

    /**
     * An activity that can be extended to simplify handling permissions.
     * <p>
     * Deriving classes will not have to write boilerplate code and code duplication between activities
     * that share this functionality is avoided.
     */
    public abstract class PermissionActivity extends AppCompatActivity {
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
            // If multiple permissions were requested in one call, check if they were all granted.
            if (requestCode == RequestCode.PERMISSION_MULTIPLE) {
                boolean allPermissionsGranted = true;
                for (int grantResult : grantResults) {
                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
                        allPermissionsGranted = false;
                    }
                }
    
                if (allPermissionsGranted) {
                    onAllPermissionsGranted(permissions);
                    return;
                }
            }
    
            // Else, check each one if it was granted/denied/blocked.
            for (int i = 0; i < permissions.length; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    // User granted permission.
                    onPermissionGranted(permissions[i]);
                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
                        // User denied permission.
                        onPermissionDenied(permissions[i]);
                    } else {
                        // User denied permission and checked 'never ask again'.
                        onPermissionBlocked(permissions[i]);
                    }
                }
            }
        }
    
        /**
         * Checks if the app has the given permission(s).
         * <p>
         * If not, it will request them.
         * <p>
         * The method is called `checkHasPermission` to avoid the linter showing a warning in the
         * child class when it's delegating permission checks to its parent class. See
         * http://stackoverflow.com/questions/36031218/check-android-permissions-in-a
         * -method/36193309#36193309 for details.
         */
        public boolean checkHasPermission(int requestCode, String... permissions) {
            if (!(permissions.length > 0)) {
                throw new IllegalArgumentException("must request at least one permission");
            }
    
            if (requestCode == RequestCode.PERMISSION_MULTIPLE) {
                List<String> permissions_ = new ArrayList<>();
    
                for (String permission : permissions) {
                    if (ActivityCompat.checkSelfPermission(this, permission) !=
                            PackageManager.PERMISSION_GRANTED) {
                        permissions_.add(permission);
                    }
                }
    
                if (!permissions_.isEmpty()) {
                    requestPermissions(this, permissions_.toArray(new String[permissions_.size()]), requestCode);
                    return false;
                } else {
                    return true;
                }
            } else {
                if (ActivityCompat.checkSelfPermission(this, permissions[0]) !=
                        PackageManager.PERMISSION_GRANTED) {
                    requestPermissions(this, permissions, requestCode);
                    return false;
                } else {
                    return true;
                }
            }
        }
    
        /**
         * Requests the given permissions.
         */
        private void requestPermissions(Activity activity, String permissions[], int resultCode) {
            showRequestPermissionsDialog(activity, permissions, resultCode);
        }
    
        /**
         * Called when a rationale (explanation why a permission is needed) should be shown to the user.
         * <p>
         * If the user clicks the positive button, the permission is requested again, otherwise the
         * dialog is dismissed.
         */
        public void showRationaleDialog(Activity activity, String permission, String message,
                                        int resultCode) {
            new AlertDialog.Builder(activity)
                    .setMessage(message)
                    .setPositiveButton("ok", (dialog, which) ->
                            showRequestPermissionDialog(activity, permission, resultCode))
                    .setNegativeButton("not now", (dialog, which) -> { /* Do nothing */ })
                    .show();
        }
    
        /**
         * Requests a single permission.
         */
        private void showRequestPermissionDialog(Activity activity, String permission, int resultCode) {
            ActivityCompat.requestPermissions(activity, new String[]{permission}, resultCode);
        }
    
        /**
         * Requests multiple permissions in one call.
         */
        private void showRequestPermissionsDialog(Activity activity, String[] permissions,
                                                  int resultCode) {
            ActivityCompat.requestPermissions(activity, permissions, resultCode);
        }
    
        /**
         * Returns a message to be shown to the user that explains why a specific permission is
         * required.
         */
        public String messageForRationale(String permission) {
            String s;
            switch (permission) {
                case Manifest.permission.READ_PHONE_STATE:
                    s = "access this device's state";
                    break;
                case Manifest.permission.ACCESS_FINE_LOCATION:
                    s = "access the location of this device";
                    break;
                case Manifest.permission.SEND_SMS:
                    s = "send text messages";
                    break;
                default:
                    throw new IllegalArgumentException("Permission not handled: " + permission);
            }
    
            return String.format("MyApp needs permission to %s.", s);
        }
    
        /**
         * Get the RequestCode for the given permission.
         */
        public int requestCodeForPermission(String permission) {
            int code;
            switch (permission) {
                case Manifest.permission.READ_PHONE_STATE:
                    code = RequestCode.PERMISSION_READ_PHONE_STATE;
                    break;
                case Manifest.permission.ACCESS_FINE_LOCATION:
                    code = RequestCode.PERMISSION_FINE_LOCATION;
                    break;
                case Manifest.permission.SEND_SMS:
                    code = RequestCode.PERMISSION_SEND_SMS;
                    break;
                // TODO: add required permissions for your app
                default:
                    throw new IllegalArgumentException("Permission not handled: " + permission);
            }
    
            return code;
        }
    
        /**
         * Called if all requested permissions were granted in the same dialog.
         * E.g. FINE_LOCATION and SEND_SMS were requested, and both were granted.
         * <p>
         * Child class can override this method if it wants to know when this happens.
         * <p>
         * Linter can show an unjust "call requires permission" warning in child class if a method that
         * requires permission(s) is called. Silence it with `@SuppressWarnings("MissingPermission")`.
         */
        protected void onAllPermissionsGranted(String[] permissions) {
    
        }
    
        /**
         * Called for all permissions that were granted in the same dialog, in case not all were
         * granted. E.g. if FINE_LOCATION, COARSE_LOCATION and SEND_SMS were requested and FINE_LOCATION
         * was not granted but COARSE_LOCATION and SEND_SMS were, it will be called for COARSE_LOCATION
         * and SEND_SMS.
         * <p>
         * Child class can override this method if it wants to know when this happens.
         * <p>
         * Linter can show an unjust "call requires permission" warning in child class if a method that
         * requires permission(s) is called. Silence it with `@SuppressWarnings("MissingPermission")`.
         */
        protected void onPermissionGranted(String permission) {
    
        }
    
        /**
         * Called for all permissions that were denied in the same dialog, handled one by one.
         * <p>
         * Child class should not override this general behavior.
         */
        protected void onPermissionDenied(String permission) {
            String message = messageForRationale(permission);
            showRationaleDialog(this, permission, message, requestCodeForPermission(permission));
        }
    
        /**
         * Called for all permissions that were blocked in the same dialog, handled one by one.
         * <p>
         * Blocked means a user denied a permission with the 'never ask again' checkbox checked.
         * <p>
         * Child class must override and decide what to do when a permission is blocked.
         */
        protected abstract void onPermissionBlocked(String permission);
    }
    

    -&gt; 表示法是 lambda expressions

    RequestCode 是一个仅用于抽象数字的接口:

    public interface RequestCode {
        int PERMISSION_READ_PHONE_STATE = 0;
        int PERMISSION_FINE_LOCATION = 1;
        int PERMISSION_SEND_SMS = 2;
        int PERMISSION_MULTIPLE = 3;
    }
    

    你可以随意改变它。不要使用超过 256 的数字。如果这样做,会抛出异常

    请求代码只能使用低 8 位。


    在需要权限的活动中,可以这样使用(只是一个例子)。一定要有活动extend PermissionActivity

    private void callThisSomewhere() {
        if (checkHasPermission(RequestCode.PERMISSION_READ_PHONE_STATE,
                Manifest.permission.READ_PHONE_STATE)) {
            tryDoStuffWithPhoneState();
        }
    }
    
    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
    private void doStuffWithPhoneState() {
        // Do stuff.
    }
    
    @Override
    public void onPermissionGranted(String permission) {
        tryDoStuffWithPhoneState();
    }
    
    @Override
    public void onPermissionBlocked(String permission) {
        // Disable parts of app that require this permission.
    }
    

    如果您一次请求多个权限,则应使用RequestCode.PERMISSION_MULTIPLE。否则只会请求第一个权限。

    AndroidManifest.xml 中未列出的权限将被自动阻止,不会向用户显示对话框,因此请务必在清单中添加您必须请求的任何权限。


    限制

    此解决方案与片段或服务不兼容。

    【讨论】:

    • 这非常有帮助。太感谢了!如果用户完全阻止权限,您首选的后备方案是什么?您是否禁用功能并且不再触摸它,或者您是否有办法将用户引导至设置以授予权限?
    • @McAdam331 是的,我禁用了该功能,如 处理权限请求响应 下的developer.android.com/training/permissions/requesting.html 建议
    【解决方案2】:

    这是我在我的应用中用于管理权限的。

    权限的父类

        public class PermissionManager extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
        private static final int REQUEST_CODE = 200;
        private Activity context;
    
        private String[] permissions;
        private ArrayList<String> grantedPermissions = new ArrayList<>();
        private RequestedPermissionResultCallBack callBack;
    
        @Override
        public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
            super.onCreate(savedInstanceState, persistentState);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            setContentView(R.layout.actvity_permission);
            checkForRequiredPermission(getIntent().getStringArrayExtra(getString(R.string.permission)));
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
            switch (requestCode) {
    
                case REQUEST_CODE:
    
                    checkForGrantedPermissions(permissions, grantResults);
                    break;
            }
    
        }
    
    
        @TargetApi(Build.VERSION_CODES.M)
        private void checkForRequiredPermission(String[] permissions) {
            ArrayList<String> requiredPermissionList = new ArrayList<>();
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    requiredPermissionList.add(permission);
                } else {
                    grantedPermissions.add(permission);
                }
            }
    
            if (requiredPermissionList.size() > 0) {
                (this).requestPermissions(requiredPermissionList.toArray(new String[requiredPermissionList.size()]), REQUEST_CODE);
            } else {
    
                setResult(grantedPermissions);
            }
    
        }
    
    
        public void checkForGrantedPermissions(String[] permissions, int[] grantResults) {
    
            for (int i = 0; i < permissions.length; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    grantedPermissions.add(permissions[i]);
                }
    
            }
            setResult(grantedPermissions);
    
        }
    
        private void setResult(ArrayList<String> grantedPermissions) {
            Intent intent = new Intent();
            intent.putStringArrayListExtra(getString(R.string.granted_permission), grantedPermissions);
            setResult(Activity.RESULT_OK, intent);
            this.finish();
        }
    }
    

    当你想检查权限时,像这样调用这个类

    private void checkForPermissions() {
            Intent intent = new Intent(this, PermissionManager.class);
            intent.putExtra(getString(R.string.permission), permission);
            startActivityForResult(intent, AppConstants.PERMSION_REQUEST_CODE);
        }
    

    这里的权限是你要请求的权限数组 像这样的

        private String permission[] = new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS};
    

    这是 onActivityResult

    的代码
    case AppConstants.PERMSION_REQUEST_CODE:
                        ArrayList<String> grantedPermissionList = data.getStringArrayListExtra(getString(R.string.granted_permission));
    
                        if (grantedPermissionList != null && grantedPermissionList.size() > 0 && grantedPermissionList.contains(permission[0])) {
                            createRequest();
    
                        } else {
                            showSettingsDialog(getString(R.string.permission_required));
                        }
    
                        break;
    

    【讨论】:

    • @Yvette 我总是想澄清我对编程或这方面的各种疑问
    猜你喜欢
    • 2011-03-22
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    相关资源
    最近更新 更多