Android6.0动态申请权限的两种办法

来源:互联网 发布:巴洛克的艺术风格 知乎 编辑:程序博客网 时间:2024/06/05 14:47

Android 6.0 动态申请权限的两种办法

最近在写项目中被安卓6.0动态申请权限所困惑,有一些自己纠结的问题,今天闲着没事就想着写篇博客来分享自己遇到的问题和解决办法。
一开始我在网上找到的申请权限的代码是ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS);(在android developer中的注释中解释的是Determine whether you have been granted a particular permission.确定你是否已被授予特定权限。)用来判断是否拥有安卓某个隐私权限
这个方法有两个返回值一个是PackageManager.PERMISSION_GRANTED,(授予)此值为0;另一个是PackageManager.PERMISSION_DENIED,(否认)值为-1。我在我的项目中写上了这种语句并判断返回值,但是意外的发现每次返回的都是PERMISSION_GRANTED,即使我在安全中心修改了对应权限的状态(允许,询问,拒绝)。后来发现因为vlc的缘故我把targetSdkVersion设为22了,所以出现的这种情况,希望能看到这篇博客的人注意到这个问题。

一般我们开发对这些隐私权限都是两种操作办法:
第一种 在程序开始时便是向用户提示对话框申请多种隐私权限
至于怎么做的呢?看下代码(这个代码是环信的代码,在集成的时候看到他是这么写的,便在这里分享给大家)

import android.Manifest;import android.app.Activity;import android.content.Context;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.os.Build;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.v4.app.ActivityCompat;import android.support.v4.app.Fragment;import android.util.Log;import java.lang.ref.WeakReference;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;/** * A class to help you manage your permissions simply. */public class PermissionsManager {  private static final String TAG = PermissionsManager.class.getSimpleName();  private final Set<String> mPendingRequests = new HashSet<String>(1);  private final Set<String> mPermissions = new HashSet<String>(1);  private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);  private static PermissionsManager mInstance = null;  public static PermissionsManager getInstance() {    if (mInstance == null) {      mInstance = new PermissionsManager();    }    return mInstance;  }  private PermissionsManager() {    initializePermissionsMap();  }  /**   * This method uses reflection to read all the permissions in the Manifest class.   * This is necessary because some permissions do not exist on older versions of Android,   * since they do not exist, they will be denied when you check whether you have permission   * which is problematic since a new permission is often added where there was no previous   * permission required. We initialize a Set of available permissions and check the set   * when checking if we have permission since we want to know when we are denied a permission   * because it doesn't exist yet.   */  private synchronized void initializePermissionsMap() {    Field[] fields = Manifest.permission.class.getFields();    for (Field field : fields) {      String name = null;      try {        name = (String) field.get("");      } catch (IllegalAccessException e) {        Log.e(TAG, "Could not access field", e);      }      mPermissions.add(name);    }  }  /**   * This method retrieves all the permissions declared in the application's manifest.   * It returns a non null array of permisions that can be declared.   *   * @param activity the Activity necessary to check what permissions we have.   * @return a non null array of permissions that are declared in the application manifest.   */  @NonNull  private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {    PackageInfo packageInfo = null;    List<String> list = new ArrayList<String>(1);    try {      Log.d(TAG, activity.getPackageName());      packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);    } catch (PackageManager.NameNotFoundException e) {      Log.e(TAG, "A problem occurred when retrieving permissions", e);    }    if (packageInfo != null) {      String[] permissions = packageInfo.requestedPermissions;      if (permissions != null) {        for (String perm : permissions) {          Log.d(TAG, "Manifest contained permission: " + perm);          list.add(perm);        }      }    }    return list.toArray(new String[list.size()]);  }  /**   * This method adds the {@link PermissionsResultAction} to the current list   * of pending actions that will be completed when the permissions are   * received. The list of permissions passed to this method are registered   * in the PermissionsResultAction object so that it will be notified of changes   * made to these permissions.   *   * @param permissions the required permissions for the action to be executed.   * @param action      the action to add to the current list of pending actions.   */  private synchronized void addPendingAction(@NonNull String[] permissions,      @Nullable PermissionsResultAction action) {    if (action == null) {      return;    }    action.registerPermissions(permissions);    mPendingActions.add(new WeakReference<PermissionsResultAction>(action));  }  /**   * This method removes a pending action from the list of pending actions.   * It is used for cases where the permission has already been granted, so   * you immediately wish to remove the pending action from the queue and   * execute the action.   *   * @param action the action to remove   */  private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {    for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();        iterator.hasNext(); ) {      WeakReference<PermissionsResultAction> weakRef = iterator.next();      if (weakRef.get() == action || weakRef.get() == null) {        iterator.remove();      }    }  }  /**   * This static method can be used to check whether or not you have a specific permission.   * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)}   * and will simply return a boolean whether or not you have the permission. If you pass   * in a null Context object, it will return false as otherwise it cannot check the permission.   * However, the Activity parameter is nullable so that you can pass in a reference that you   * are not always sure will be valid or not (e.g. getActivity() from Fragment).   *   * @param context    the Context necessary to check the permission   * @param permission the permission to check   * @return true if you have been granted the permission, false otherwise   */  @SuppressWarnings("unused")  public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {    return context != null && (ActivityCompat.checkSelfPermission(context, permission)        == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));  }  /**   * This static method can be used to check whether or not you have several specific permissions.   * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)}   * for each permission and will simply return a boolean whether or not you have all the permissions.   * If you pass in a null Context object, it will return false as otherwise it cannot check the   * permission. However, the Activity parameter is nullable so that you can pass in a reference   * that you are not always sure will be valid or not (e.g. getActivity() from Fragment).   *   * @param context     the Context necessary to check the permission   * @param permissions the permissions to check   * @return true if you have been granted all the permissions, false otherwise   */  @SuppressWarnings("unused")  public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {    if (context == null) {      return false;    }    boolean hasAllPermissions = true;    for (String perm : permissions) {      hasAllPermissions &= hasPermission(context, perm);    }    return hasAllPermissions;  }  /**   * This method will request all the permissions declared in your application manifest   * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable   * all permissions to be requested at one shot. The PermissionsResultAction is used to notify   * you of the user allowing or denying each permission. The Activity and PermissionsResultAction   * parameters are both annotated Nullable, but this method will not work if the Activity   * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case   * that you call this from a Fragment where {@link Fragment#getActivity()} could yield   * null. Additionally, you will not receive any notification of permissions being granted   * if you provide a null PermissionsResultAction.   *   * @param activity the Activity necessary to request and check permissions.   * @param action   the PermissionsResultAction used to notify you of permissions being accepted.   */  @SuppressWarnings("unused")  public synchronized void requestAllManifestPermissionsIfNecessary(final @Nullable Activity activity,      final @Nullable PermissionsResultAction action) {    if (activity == null) {      return;    }    String[] perms = getManifestPermissions(activity);    requestPermissionsIfNecessaryForResult(activity, perms, action);  }  /**   * This method should be used to execute a {@link PermissionsResultAction} for the array   * of permissions passed to this method. This method will request the permissions if   * they need to be requested (i.e. we don't have permission yet) and will add the   * PermissionsResultAction to the queue to be notified of permissions being granted or   * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.   * The Activity variable is nullable, but if it is null, the method will fail to execute.   * This is only nullable as a courtesy for Fragments where getActivity() may yeild null   * if the Fragment is not currently added to its parent Activity.   *   * @param activity    the activity necessary to request the permissions.   * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.   * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.   */  @SuppressWarnings("unused")  public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity,      @NonNull String[] permissions,      @Nullable PermissionsResultAction action) {    if (activity == null) {      return;    }    addPendingAction(permissions, action);    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {      doPermissionWorkBeforeAndroidM(activity, permissions, action);    } else {      List<String> permList = getPermissionsListToRequest(activity, permissions, action);      if (permList.isEmpty()) {        //if there is no permission to request, there is no reason to keep the action int the list        removePendingAction(action);      } else {        String[] permsToRequest = permList.toArray(new String[permList.size()]);        mPendingRequests.addAll(permList);        ActivityCompat.requestPermissions(activity, permsToRequest, 1);      }    }  }  /**   * This method should be used to execute a {@link PermissionsResultAction} for the array   * of permissions passed to this method. This method will request the permissions if   * they need to be requested (i.e. we don't have permission yet) and will add the   * PermissionsResultAction to the queue to be notified of permissions being granted or   * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.   * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method   * will fail to work as the activity reference is necessary to check for permissions.   *   * @param fragment    the fragment necessary to request the permissions.   * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.   * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.   */  @SuppressWarnings("unused")  public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment,      @NonNull String[] permissions,      @Nullable PermissionsResultAction action) {    Activity activity = fragment.getActivity();    if (activity == null) {      return;    }    addPendingAction(permissions, action);    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {      doPermissionWorkBeforeAndroidM(activity, permissions, action);    } else {      List<String> permList = getPermissionsListToRequest(activity, permissions, action);      if (permList.isEmpty()) {        //if there is no permission to request, there is no reason to keep the action int the list        removePendingAction(action);      } else {        String[] permsToRequest = permList.toArray(new String[permList.size()]);        mPendingRequests.addAll(permList);        fragment.requestPermissions(permsToRequest, 1);      }    }  }  /**   * This method notifies the PermissionsManager that the permissions have change. If you are making   * the permissions requests using an Activity, then this method should be called from the   * Activity callback onRequestPermissionsResult() with the variables passed to that method. If   * you are passing a Fragment to make the permissions request, then you should call this in   * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method.   * It will notify all the pending PermissionsResultAction objects currently   * in the queue, and will remove the permissions request from the list of pending requests.   *   * @param permissions the permissions that have changed.   * @param results     the values for each permission.   */  @SuppressWarnings("unused")  public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {    int size = permissions.length;    if (results.length < size) {      size = results.length;    }    Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();    while (iterator.hasNext()) {      PermissionsResultAction action = iterator.next().get();      for (int n = 0; n < size; n++) {        if (action == null || action.onResult(permissions[n], results[n])) {          iterator.remove();          break;        }      }    }    for (int n = 0; n < size; n++) {      mPendingRequests.remove(permissions[n]);    }  }  /**   * When request permissions on devices before Android M (Android 6.0, API Level 23)   * Do the granted or denied work directly according to the permission status   *   * @param activity    the activity to check permissions   * @param permissions the permissions names   * @param action      the callback work object, containing what we what to do after   *                    permission check   */  private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity,      @NonNull String[] permissions,      @Nullable PermissionsResultAction action) {    for (String perm : permissions) {      if (action != null) {        if (!mPermissions.contains(perm)) {          action.onResult(perm, Permissions.NOT_FOUND);        } else if (ActivityCompat.checkSelfPermission(activity, perm)            != PackageManager.PERMISSION_GRANTED) {          action.onResult(perm, Permissions.DENIED);        } else {          action.onResult(perm, Permissions.GRANTED);        }      }    }  }  /**   * Filter the permissions list:   * If a permission is not granted, add it to the result list   * if a permission is granted, do the granted work, do not add it to the result list   *   * @param activity    the activity to check permissions   * @param permissions all the permissions names   * @param action      the callback work object, containing what we what to do after   *                    permission check   * @return a list of permissions names that are not granted yet   */  @NonNull  private List<String> getPermissionsListToRequest(@NonNull Activity activity,      @NonNull String[] permissions,      @Nullable PermissionsResultAction action) {    List<String> permList = new ArrayList<String>(permissions.length);    for (String perm : permissions) {      if (!mPermissions.contains(perm)) {        if (action != null) {          action.onResult(perm, Permissions.NOT_FOUND);        }      } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {        if (!mPendingRequests.contains(perm)) {          permList.add(perm);        }      } else {        if (action != null) {          action.onResult(perm, Permissions.GRANTED);        }      }    }    return permList;  }}
enum Permissions {      GRANTED,      DENIED,      NOT_FOUND}
import android.content.pm.PackageManager;import android.os.Handler;import android.os.Looper;import android.support.annotation.CallSuper;import android.support.annotation.NonNull;import android.util.Log;import java.util.Collections;import java.util.HashSet;import java.util.Set;/** * This abstract class should be used to create an if/else action that the PermissionsManager * can execute when the permissions you request are granted or denied. Simple use involves * creating an anonymous instance of it and passing that instance to the * requestPermissionsIfNecessaryForResult method. The result will be sent back to you as * either onGranted (all permissions have been granted), or onDenied (a required permission * has been denied). Ideally you put your functionality in the onGranted method and notify * the user what won't work in the onDenied method. */public abstract class PermissionsResultAction {  private static final String TAG = PermissionsResultAction.class.getSimpleName();  private final Set<String> mPermissions = new HashSet<String>(1);  private Looper mLooper = Looper.getMainLooper();  /**   * Default Constructor   */  public PermissionsResultAction() {}  /**   * Alternate Constructor. Pass the looper you wish the PermissionsResultAction   * callbacks to be executed on if it is not the current Looper. For instance,   * if you are making a permissions request from a background thread but wish the   * callback to be on the UI thread, use this constructor to specify the UI Looper.   *   * @param looper the looper that the callbacks will be called using.   */  @SuppressWarnings("unused")  public PermissionsResultAction(@NonNull Looper looper) {mLooper = looper;}  /**   * This method is called when ALL permissions that have been   * requested have been granted by the user. In this method   * you should put all your permissions sensitive code that can   * only be executed with the required permissions.   */  public abstract void onGranted();  /**   * This method is called when a permission has been denied by   * the user. It provides you with the permission that was denied   * and will be executed on the Looper you pass to the constructor   * of this class, or the Looper that this object was created on.   *   * @param permission the permission that was denied.   */  public abstract void onDenied(String permission);  /**   * This method is used to determine if a permission not   * being present on the current Android platform should   * affect whether the PermissionsResultAction should continue   * listening for events. By default, it returns true and will   * simply ignore the permission that did not exist. Usually this will   * work fine since most new permissions are introduced to   * restrict what was previously allowed without permission.   * If that is not the case for your particular permission you   * request, override this method and return false to result in the   * Action being denied.   *   * @param permission the permission that doesn't exist on this   *                   Android version   * @return return true if the PermissionsResultAction should   * ignore the lack of the permission and proceed with exection   * or false if the PermissionsResultAction should treat the   * absence of the permission on the API level as a denial.   */  @SuppressWarnings({"WeakerAccess", "SameReturnValue"})  public synchronized boolean shouldIgnorePermissionNotFound(String permission) {    Log.d(TAG, "Permission not found: " + permission);    return true;  }  @SuppressWarnings("WeakerAccess")  @CallSuper  protected synchronized final boolean onResult(final @NonNull String permission, int result) {    if (result == PackageManager.PERMISSION_GRANTED) {      return onResult(permission, Permissions.GRANTED);    } else {      return onResult(permission, Permissions.DENIED);    }  }  /**   * This method is called when a particular permission has changed.   * This method will be called for all permissions, so this method determines   * if the permission affects the state or not and whether it can proceed with   * calling onGranted or if onDenied should be called.   *   * @param permission the permission that changed.   * @param result     the result for that permission.   * @return this method returns true if its primary action has been completed   * and it should be removed from the data structure holding a reference to it.   */  @SuppressWarnings("WeakerAccess")  @CallSuper  protected synchronized final boolean onResult(final @NonNull String permission, Permissions result) {    mPermissions.remove(permission);    if (result == Permissions.GRANTED) {      if (mPermissions.isEmpty()) {        new Handler(mLooper).post(new Runnable() {          @Override          public void run() {            onGranted();          }        });        return true;      }    } else if (result == Permissions.DENIED) {      new Handler(mLooper).post(new Runnable() {        @Override        public void run() {          onDenied(permission);        }      });      return true;    } else if (result == Permissions.NOT_FOUND) {      if (shouldIgnorePermissionNotFound(permission)) {        if (mPermissions.isEmpty()) {          new Handler(mLooper).post(new Runnable() {            @Override            public void run() {              onGranted();            }          });          return true;        }      } else {        new Handler(mLooper).post(new Runnable() {          @Override          public void run() {            onDenied(permission);          }        });        return true;      }    }    return false;  }  /**   * This method registers the PermissionsResultAction object for the specified permissions   * so that it will know which permissions to look for changes to. The PermissionsResultAction   * will then know to look out for changes to these permissions.   *   * @param perms the permissions to listen for   */  @SuppressWarnings("WeakerAccess")  @CallSuper  protected synchronized final void registerPermissions(@NonNull String[] perms) {    Collections.addAll(mPermissions, perms);  }}

以上代码共有三个类: Permissions,PermissionsManager,PermissionsResultAction
再来看怎么执行的
在MainActivity的OnCreate()方法中

requestPermissions();
@TargetApi(23)    private void requestPermissions() {        PermissionsManager.getInstance().requestAllManifestPermissionsIfNecessary(this, new PermissionsResultAction() {            @Override            public void onGranted() {//              Toast.makeText(MainActivity.this, "All permissions have been granted", Toast.LENGTH_SHORT).show();            }            @Override            public void onDenied(String permission) {                //Toast.makeText(MainActivity.this, "Permission " + permission + " has been denied", Toast.LENGTH_SHORT).show();            }        });    }
@Override     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,            @NonNull int[] grantResults) {        PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults);    }

That’s all

第二种 在用户操作到隐私权限的位置时,提示用户授权
这里只给个例子

int permissionCode = ContextCompat.checkSelfPermission(thisActivity,        Manifest.permission.READ_CONTACTS);if (permissionCode!= PackageManager.PERMISSION_GRANTED) {    // Should we show an explanation?    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,            Manifest.permission.READ_CONTACTS)) {      // 显示给用户解释授权    } else {        // No explanation needed, we can request the permission.        ActivityCompat.requestPermissions(thisActivity,                new String[]{Manifest.permission.READ_CONTACTS},                request_Code);    }}
@Overridepublic void onRequestPermissionsResult(int requestCode,        String permissions[], int[] grantResults) {    if (requestCode == request_Code) {            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {                 // 检查该权限是否已经获取                int i = ContextCompat.checkSelfPermission(this, permissions[0]);                // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝                if (i != PackageManager.PERMISSION_GRANTED) {                    // 提示用户应该去应用设置界面手动开启权限                    ...                } else {                                Toast.makeText(this, "权限获取成功", Toast.LENGTH_SHORT).show();                }            }        }}

这里网上有很多写的不错的代码,我也看到了,有些我看了认为好的地址分享给大家:
http://www.jianshu.com/p/a51593817825
http://www.cnblogs.com/xmcx1995/p/5870191.html

还有大家可以引用开源工具集成到自己的项目中,像PermissionsDispatcher,HiPermission,RxPermission,Github上的star还是很多的

欢迎指教,我会跟进的

未完待续