问题描述
我正在将我的应用程序更新为与Android 6兼容.权限模型在理论上不是很复杂,但是现在我正在实施它,我发现自己在需要权限的每一项活动中都编写了同样的丑陋的样板代码.
I'm updating my app to be Android 6-compatible. The permission model is not very complex in theory, but now I'm in the process of implementing it and I find myself writing the same ugly boilerplate code in every one of my activities that require permissions.
对于我需要的每个许可,都有一个
For every permission I need, there is a
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.PERMISSION) !=
PackageManager.PERMISSION_GRANTED) {
} else {
}
,然后在onRequestPermissionsResult
中,我必须检查/过滤每个请求的结果,并将其转换为我的活动可以理解的内容.
and then in the onRequestPermissionsResult
I have to check/filter the results of each request and translate that to something my activity understands.
我现在正在更新我的第二个活动,并且权限代码与第一个活动非常相似,以至于几乎就像是复制粘贴的一样.这些行很长,代码很相似,而且看起来很丑.
I'm updating my second activity now, and the permission code is so similar to the first that it almost looks like it's copy-pasted. The lines are long, the code is similar, and it simply looks ugly.
我不想使用第三方解决方案,我尝试了一些,但是我希望完全控制代码.例如,某些库不支持我在项目中使用的Java 8.
I don't want to use a third-party solution, I have tried some, but I would prefer to have full control over the code. For instance, some libraries do not support Java 8, which I am using in my project.
如何避免所有活动中出现重复的代码?
What can I do to avoid a bunch of duplicate code in all of my activities?
推荐答案
出于问题中说明的原因,我不想使用任何可用的库,所以我自己开发了一些东西.
I didn't want to use any of the available libraries for the reasons explained in the question, so I developed something myself.
我所有需要一个或多个权限的活动都从处理所有权限相关任务的PermissionActivity
继承.
All my activities that require one or more permissions inherit from a PermissionActivity
that handles all the permission related tasks.
如何通过您的活动调用
if (checkHasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { }
来自父类.如果已经授予该权限,则代码可以继续.如果不是,则父类将请求许可,并使用一种抽象方法和/或一个或多个可重写方法将结果发送给子类.
from the parent class. If the permission is already granted, the code can continue. If not, the parent class will request the permission and send the results to the child class using an abstract method and/or one or multiple overridable methods.
父类
除了messageForRationale()
和requestCodeForPermission()
中的开关块之外,此类可以保持不变.更新您的应用所需的权限.
This class can be left unchanged, apart from the switch blocks in messageForRationale()
and requestCodeForPermission()
. Update those for the permissions your app needs.
/**
* 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 是仅用于提取数字的接口:
RequestCode is an interface solely used for abstracting away the numbers:
public interface RequestCode {
int PERMISSION_READ_PHONE_STATE = 0;
int PERMISSION_FINE_LOCATION = 1;
int PERMISSION_SEND_SMS = 2;
int PERMISSION_MULTIPLE = 3;
}
您可以根据需要进行更改.不要使用超过256的数字.如果这样做,则会抛出一个异常
you can change it as you like. Don't use numbers over 256. If you do, an exception will be thrown saying
在需要许可的活动中,您可以像这样使用它(仅作为示例).确保活动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
.否则,将仅请求第一许可.
If you are requesting multiple permissions in one go, you should use RequestCode.PERMISSION_MULTIPLE
. Otherwise only the first permission will be requested.
AndroidManifest.xml中未列出的权限将被自动阻止,而不会向用户显示对话框,因此请确保在清单中也添加您必须请求的所有权限.
Permissions that are not listed in AndroidManifest.xml will be automatically blocked without showing the user the dialog, so be sure to add any permissions you have to request in the manifest as well.
限制
此解决方案与片段或服务不兼容.
This solution is not compatible with fragments or services.
这篇关于如何避免编写重复的样板代码以请求权限?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!