如何避免为请求权限编写重复的样板代码?

Tim*_*Tim 9 android android-permissions android-6.0-marshmallow runtime-permissions

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

对于我需要的每一个许可,都有一个

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.PERMISSION) !=
                PackageManager.PERMISSION_GRANTED) {
} else {
}
Run Code Online (Sandbox Code Playgroud)

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

我现在正在更新我的第二个活动,并且权限代码与第一个类似,它几乎看起来像是复制粘贴的.线条很长,代码很相似,看起来很丑陋.

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

我可以做些什么来避免在我的所有活动中出现一堆重复的代码?

Tim*_*Tim 9

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

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

它的工作原理是你的活动电话

if (checkHasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { }
Run Code Online (Sandbox Code Playgroud)

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


父类

这个类可以从开关块保持不变,除了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);
}
Run Code Online (Sandbox Code Playgroud)

->符号是lambda表达式.

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

public interface RequestCode {
    int PERMISSION_READ_PHONE_STATE = 0;
    int PERMISSION_FINE_LOCATION = 1;
    int PERMISSION_SEND_SMS = 2;
    int PERMISSION_MULTIPLE = 3;
}
Run Code Online (Sandbox Code Playgroud)

你可以随意改变它.不要使用超过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.
}
Run Code Online (Sandbox Code Playgroud)

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

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


限制

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