7

我正在更新我的应用程序以实现与Android 6兼容。理论上许可模式并不复杂,但现在我正在实施它,并且我发现自己在每个需要权限的活动中都编写了同样丑陋的样板代码。如何避免为请求权限编写重复的样板代码?

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

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

然后在onRequestPermissionsResult我要检查/过滤每个请求的结果,那意思就是我的东西了解的活动。

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

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

我能做些什么来避免我所有活动中的一堆重复代码?

回答

7

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

我需要一个或多个权限的所有活动都从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 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中列出的权限将被自动阻止,而不会向用户显示对话框,因此请务必在清单中添加您必须请求的任何权限。


限制

该解决方案是不是片段或服务不兼容。

+0

非常好的解决方案的代码。 Android的模块化并不经常被讨论。这几乎就好像忘记了它是OOP –

+0

这非常有帮助。非常感谢!如果用户完全阻止了权限,您最喜欢的回退是什么?您是否禁用了功能并且再也不会触摸它,或者您是否有办法指示用户进行设置以授予权限? – AdamMc331

+0

@ McAdam331是的,我禁用了这个功能,这里建议https://developer.android.com/training/permissions/requesting.html *处理权限请求响应* –

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; 
+0

你能格式化代码并解释它是如何工作的吗? –

+0

@TimCastelijns添加了询问权限的代码 –

+0

您可以格式化代码吗?这是有点难以阅读 –