Andrid6.0权限机制介绍及处理

在Android M添加的权限机制中,Google将权限分为两类:

  • Normal Permissions(普通权限):不涉及用户隐私,不需要用户进行授权,比如访问网络等
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_WIMAX_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.CHANGE_WIMAX_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET_PACKAGE_SIZE
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.SUBSCRIBED_FEEDS_READ
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
  • Dangerous Permission(危险权限):涉及到用户隐私,需要用户进行授权,比如相机访问、读取SD卡等
group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS    
  permission:android.permission.READ_CONTACTS

group: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_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group: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以上的机器,对于危险权限Google官方也对其进行了分组,只要一组中的某一权限被授权,同组的权限也同样会被授权。

操作步骤

新的权限机制操作步骤如下

  • 在Androidmanifest.xml文件声明相关权限

  • 通过ContextCompat.checkSelfPermission方法检查某项权限被授予情况

  • 通过ActivityCompat.shouldShowRequestPermissionRationale方法检查权限是否被永久拒绝需要向用户说明

  • 通过Activity.requestPermissions申请授权

  • 处理Activity.onRequestPermissionsResult权限回调

Google给出的Demo实现方式

/**
 * Check that all given permissions have been granted by verifying that each entry in the
 * given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
 *
 * @see Activity#onRequestPermissionsResult(int, String[], int[])
 */
public static boolean verifyPermissions(int[] grantResults) {
    // At least one result must be checked.
    if(grantResults.length < 1){
        return false;
    }

    // Verify that each required permission has been granted, otherwise return false.
    for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

public void showContacts(View v) {
    Log.i(TAG, "Show contacts button pressed. Checking permissions.");

    // 验证所有联系人权限是否已经获得
    if (ActivityCompat.checkSelfPermission(this,             Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED
            || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
        // 通讯录权限未获得
        Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions.");
        requestContactsPermissions();

    } else {
        // 通讯录权限已获得,显示通讯录Fragment
        Log.i(TAG,
                "Contact permissions have already been granted. Displaying contact details.");
        showContactDetails();
    }
}

/**
 * 请求通讯录权限
 * 如果权限已经被拒绝了,那么会弹出一个弹框提醒用户去授予权限,否则会直接请求
 */
private void requestContactsPermissions() {
    // BEGIN_INCLUDE(contacts_permission_request)
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.READ_CONTACTS)
            || ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.WRITE_CONTACTS)) {

        // 如果没有授予许可,请向用户提供额外的理由来让用户理解请求此权限的原因
        // 例如,如果以前已经拒绝了权限请求
        Log.i(TAG,"Displaying contacts permission rationale to provide additional context.");

        // 显示一个带有说明和按钮的SnackBar来触发请求。
        Snackbar.make(mLayout, R.string.permission_contacts_rationale,
        Snackbar.LENGTH_INDEFINITE)
                .setAction(R.string.ok, new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_CONTACT,
                        REQUEST_CONTACTS);
                    }
                })
                .show();
    } else {
        // 通讯录权限尚未授予,直接请求
        ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT,
        REQUEST_CONTACTS);
    }
    // END_INCLUDE(contacts_permission_request)
}

//权限处理结果的回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull      String[] permissions,@NonNull int[] grantResults) {

    if (requestCode == REQUEST_CONTACTS) {
        Log.i(TAG, "Received response for contact permissions request.");

        // 对请求的所有联系人权限再核实一遍
        if (PermissionUtil.verifyPermissions(grantResults)) {
            // 所有权限都已经获得,显示联系人页面
            Snackbar.make(mLayout, R.string.permision_available_contacts,
                    Snackbar.LENGTH_SHORT)
                    .show();
        } else {
            Log.i(TAG, "Contacts permissions were NOT granted.");
            Snackbar.make(mLayout, R.string.permissions_not_granted,
                    Snackbar.LENGTH_SHORT)
                    .show();
        }

    } else {
        super.onRequestPermissionsResult(requestCode, permissions,grantResults);
    }
}

关键的几个方法

1.检查是否拥有指定权限

android.support.v4.content.ContextCompat.java

/**
* Determine whether <em>you</em> have been granted a particular permission.
*
* @param permission The name of the permission being checked.
*
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not.
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
    if (permission == null) {
        throw new IllegalArgumentException("permission is null");
    }

    return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}

2.判断是否彻底拒绝而了某项权限而需要对用户进行说明

android.support.v4.app.ActivityCompat.java

/**
 * Gets whether you should show UI with rationale for requesting a permission.
 * You should do this only if you do not have the permission and the context in
 * which the permission is requested does not clearly communicate to the user
 * what would be the benefit from granting this permission.
 * <p>
 * For example, if you write a camera app, requesting the camera permission
 * would be expected by the user and no rationale for why it is requested is
 * needed. If however, the app needs location for tagging photos then a non-tech
 * savvy user may wonder how location is related to taking photos. In this case
 * you may choose to show UI with rationale of requesting this permission.
 * </p>
 *
 * @param activity The target activity.
 * @param permission A permission your app wants to request.
 * @return Whether you can show permission rationale UI.
 *
 * @see #checkSelfPermission(android.content.Context, String)
 * @see #requestPermissions(android.app.Activity, String[], int)
 */
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
        @NonNull String permission) {
    if (Build.VERSION.SDK_INT >= 23) {
        return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
    }
    return false;
}

3.发出权限请求

android.app.Activity

/**
 * Requests permissions to be granted to this application. These permissions
 * must be requested in your manifest, they should not be granted to your app,
 * and they should have protection level {@link android.content.pm.PermissionInfo
 * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
 * the platform or a third-party app.
 * <p>
 * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
 * are granted at install time if requested in the manifest. Signature permissions
 * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
 * install time if requested in the manifest and the signature of your app matches
 * the signature of the app declaring the permissions.
 * </p>
 * <p>
 * If your app does not have the requested permissions the user will be presented
 * with UI for accepting them. After the user has accepted or rejected the
 * requested permissions you will receive a callback on {@link
 * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
 * permissions were granted or not.
 * </p>
 * <p>
 * Note that requesting a permission does not guarantee it will be granted and
 * your app should be able to run without having this permission.
 * </p>
 * <p>
 * This method may start an activity allowing the user to choose which permissions
 * to grant and which to reject. Hence, you should be prepared that your activity
 * may be paused and resumed. Further, granting some permissions may require
 * a restart of you application. In such a case, the system will recreate the
 * activity stack before delivering the result to {@link
 * #onRequestPermissionsResult(int, String[], int[])}.
 * </p>
 * <p>
 * When checking whether you have a permission you should use {@link
 * #checkSelfPermission(String)}.
 * </p>
 * <p>
 * Calling this API for permissions already granted to your app would show UI
 * to the user to decide whether the app can still hold these permissions. This
 * can be useful if the way your app uses data guarded by the permissions
 * changes significantly.
 * </p>
 * <p>
 * You cannot request a permission if your activity sets {@link
 * android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
 * <code>true</code> because in this case the activity would not receive
 * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
 * </p>
 * <p>
 * The <a href="http://developer.android.com/samples/RuntimePermissions/index.html">
 * RuntimePermissions</a> sample app demonstrates how to use this method to
 * request permissions at run time.
 * </p>
 *
 * @param permissions The requested permissions. Must me non-null and not empty.
 * @param requestCode Application specific request code to match with a result
 *    reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
 *    Should be >= 0.
 *
 * @see #onRequestPermissionsResult(int, String[], int[])
 * @see #checkSelfPermission(String)
 * @see #shouldShowRequestPermissionRationale(String)
 */
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    if (mHasCurrentPermissionsRequest) {
        Log.w(TAG, "Can reqeust only one set of permissions at a time");
        // Dispatch the callback with empty arrays which means a cancellation.
        onRequestPermissionsResult(requestCode, new String[0], new int[0]);
        return;
    }
    Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

4.在Activity中接受用户处理结果回调

android.app.Activity

/**
 * Callback for the result from requesting permissions. This method
 * is invoked for every call on {@link #requestPermissions(String[], int)}.
 * <p>
 * <strong>Note:</strong> It is possible that the permissions request interaction
 * with the user is interrupted. In this case you will receive empty permissions
 * and results arrays which should be treated as a cancellation.
 * </p>
 *
 * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
 * @param permissions The requested permissions. Never null.
 * @param grantResults The grant results for the corresponding permissions
 *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
 *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
 *
 * @see #requestPermissions(String[], int)
 */
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    /* callback - no nothing */
}

为了便于使用可以在此基础上进行封装,可以参考下面这篇文章中的封装方式
http://www.jianshu.com/p/4a60b064a0ab

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值