对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
Normal Permissions如下
ACCESS_LOCATION_EXTRA_COMMANDSACCESS_NETWORK_STATE ACCESS_NOTIFICATION_POLICY ACCESS_WIFI_STATE BLUETOOTH BLUETOOTH_ADMIN BROADCAST_STICKY CHANGE_NETWORK_STATE CHANGE_WIFI_MULTICAST_STATE CHANGE_WIFI_STATE DISABLE_KEYGUARD EXPAND_STATUS_BAR GET_PACKAGE_SIZE INSTALL_SHORTCUT INTERNET KILL_BACKGROUND_PROCESSES MODIFY_AUDIO_SETTINGS NFC READ_SYNC_SETTINGS READ_SYNC_STATS RECEIVE_BOOT_COMPLETED REORDER_TASKS REQUEST_INSTALL_PACKAGES SET_ALARM SET_TIME_ZONE SET_WALLPAPER SET_WALLPAPER_HINTS TRANSMIT_IR UNINSTALL_SHORTCUT USE_FINGERPRINT VIBRATE WAKE_LOCK WRITE_SYNC_SETTINGS
Dangerous Permissions:
group:android.permission-group.CONTACTS permission:android.permission.WRITE_CONTACTS permission:android.permission.GET_ACCOUNTS permission:android.permission.READ_CONTACTSgroup:android.permission-group.PHONE permission:android.permission.READ_CALL_LOG permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.WRITE_CALL_LOG permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS permission:com.android.voicemail.permission.ADD_VOICEMAILgroup:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDARgroup:android.permission-group.CAMERA permission:android.permission.CAMERAgroup:android.permission-group.SENSORS permission:android.permission.BODY_SENSORSgroup:android.permission-group.LOCATION permission:android.permission.ACCESS_FINE_LOCATION permission:android.permission.ACCESS_COARSE_LOCATIONgroup:android.permission-group.STORAGE permission:android.permission.READ_EXTERNAL_STORAGE permission:android.permission.WRITE_EXTERNAL_STORAGEgroup:android.permission-group.MICROPHONE permission:android.permission.RECORD_AUDIOgroup:android.permission-group.SMS permission:android.permission.READ_SMS permission:android.permission.RECEIVE_WAP_PUSH permission:android.permission.RECEIVE_MMS permission:android.permission.RECEIVE_SMS permission:android.permission.SEND_SMS permission:android.permission.READ_CELL_BROADCASTS
如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS
已经授权了,当你的app申请WRITE_CONTACTS
时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
说白了就是,android权限由三种状态:询问、允许、禁止,一般我们的应用下载下来后,默认的状态都是询问状态,在使用的时候会 提示出来是否允许,比如在扫码的时候,调用相机,如果询问的话,使用扫一扫会弹出提示框,是否允许xxxAPP调用相机,选择只能选择禁止或者允许,在android6.0的手机上 就算设置中的权限状态时询问状态,他也不会询问(不同的机型经过二次封装会有不同,一般不会询问),这就导致我们的的app直接崩溃,错误就是权限的错误,所以这个问题就让人很纠结,今天在这里给大家来一段处理6.0权限问题的代码,我这也是在github上找的,省的以后你们也找半天,还不一定管用,下面上代码
首先是个枚举,里面有三个状态
package com.anthonycr.grant; /** * Enum class to handle the different states * of permissions since the PackageManager only * has a granted and denied state. */ enum Permissions { GRANTED, DENIED, NOT_FOUND }
对应允许、禁止、询问
其次是权限管理了
package com.anthonycr.grant; 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<>(1); private final Set<String> mPermissions = new HashSet<>(1); private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<>(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<>(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<>(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<>(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; } }
这里封装了申请请所有权限,申请某个权限两种方法,
然后是处理权限结果了
package com.anthonycr.grant; 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<>(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); } }
以上这些就是权限的管理封装
调用的时候
1、申请所有权限
PermissionsManager.getInstance().requestAllManifestPermissionsIfNecessary(this, new PermissionsResultAction() { @Override public void onGranted() { Toast.makeText(MainActivity.this, R.string.message_granted, Toast.LENGTH_SHORT).show(); writeToStorage("Hello, World!"); readFromStorage(); } @Override public void onDenied(String permission) { String message = String.format(Locale.getDefault(), getString(R.string.message_denied), permission); Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show(); } });
2、申请某个权限或多个权限
PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE}, new PermissionsResultAction() { @Override public void onGranted() { Log.i(TAG,"执行onGranted"); double[] location = getCoordinates(); for (double coord : location) { Log.d(TAG, "Coordinate: " + coord); writeToStorage("Coordinate: " + coord); } } @Override public void onDenied(String permission) { Log.i(TAG,"执行onDenied"); Log.e(TAG, "Unable to get location without permission"); } });
以上就是对安卓6.0的权限的一下处理