9

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

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

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

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

我现在正在更新我的第二个活动,权限代码与第一个活动非常相似,几乎看起来像是复制粘贴的。行很长,代码相似,而且看起来很丑。

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

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

4

2 回答 2

9

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

我所有需要一个或多个权限的活动都继承自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);
}

->符号是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;
}

你可以随意改变它。不要使用超过 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 中未列出的权限将被自动阻止,而不向用户显示对话框,因此请务必在清单中添加您必须请求的任何权限。


限制

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

于 2016-08-22T12:52:28.650 回答
1

这就是我在我的应用程序中用于管理权限的方法。

权限的父类

    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;
于 2016-08-22T13:14:00.160 回答