Android_动态权限管理的解决方案

1.前言

(1).由于MIUI等部分国产定制系统也有权限管理,没有相关api,故无法判断用户是否允许获取联系人等隐私。在Android 6.0之后,新增权限管理可以通过官方api判断用户的运行状态;

(2).我们指定targetSdkVersion为23或者之后我们还需要在运行时请求这些所需的权限。这很重要,因为已经出现了很多开发者把targetSdkVersion飙到了最新,然后发现自己的app疯狂的崩溃,这是由于他们没有实现执行运行时权限请求的代码。当你已经把一个targeting API 为23或者之后的app发布到了Google Play上,这更是一个问题,你无法立即把那个apk的targeting API替换成更早的版本。


2.权限分析

从Android6.0开始,权限分为普通权限和许可权限。许可权限分类归组,一个权限授权之后,该组下的权限均可使用。

(1)普通权限

只需要在xml申请即可,使用方法和之前6.0以前的一样。在应用安装应用时,会默认获得许可。

(2)许可权限

可执行 $adb shell pm list permissions -d -g

Permission GroupPermissions
android.permission-group.CALENDAR
  • android.permission.READ_CALENDAR
  • android.permission.WRITE_CALENDAR
android.permission-group.CAMERA
  • android.permission.CAMERA
android.permission-group.CONTACTS
  • android.permission.READ_CONTACTS
  • android.permission.WRITE_CONTACTS
  • android.permission.GET_ACCOUNTS
android.permission-group.LOCATION
  • android.permission.ACCESS_FINE_LOCATION
  • android.permission.ACCESS_COARSE_LOCATION
android.permission-group.MICROPHONE
  • android.permission.RECORD_AUDIO
android.permission-group.PHONE
  • android.permission.READ_PHONE_STATE
  • android.permission.CALL_PHONE
  • android.permission.READ_CALL_LOG
  • android.permission.WRITE_CALL_LOG
  • com.android.voicemail.permission.ADD_VOICEMAIL
  • android.permission.USE_SIP
  • android.permission.PROCESS_OUTGOING_CALLS
android.permission-group.SENSORS
  • android.permission.BODY_SENSORS
android.permission-group.SMS
  • android.permission.SEND_SMS
  • android.permission.RECEIVE_SMS
  • android.permission.READ_SMS
  • android.permission.RECEIVE_WAP_PUSH
  • android.permission.RECEIVE_MMS
  • android.permission.READ_CELL_BROADCASTS
android.permission-group.STORAGE
  • android.permission.READ_EXTERNAL_STORAGE
  • android.permission.WRITE_EXTERNAL_STORAGE

同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。
源码中被用来检查和请求权限的方法分别是Activity的checkSelfPermission和requestPermissions,这些方法api23引入。


3.相关方法

(1).ContextCompat.checkSelfPermission()

检查应用是否拥有该权限,被授权返回值为PERMISSION_GRANTED,否则返回PERMISSION_DENIED

(2).ActivityCompat.requestPermissions()

将弹出请求授权对话框,这个方法在M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。

(3).AppCompatActivity.onRequestPermissionsResult()

该方法类似于Activity的OnActivityResult()的回调方法,主要接收请求授权的返回值

//版本判断
if (Build.VERSION.SDK_INT >= 23) {
    //减少是否拥有权限
    int checkCallPhonePermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission);
    if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
        //弹出对话框接收权限
        ActivityCompat.requestPermissions(BaseActivity.this, new String[]{permission}, id);
        return;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        //TODO:已授权
    } else {
       //TODO:用户拒绝
    }
}


4.封装

public class BaseActivity extends AppCompatActivity {
    private Map<Integer, Runnable> allowablePermissionRunnables = new HashMap<>();
    private Map<Integer, Runnable> disallowablePermissionRunnables = new HashMap<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    /**
     * 请求权限
     * @param id 请求授权的id 唯一标识即可
     * @param permission 请求的权限
     * @param allowableRunnable 同意授权后的操作
     * @param disallowableRunnable 禁止权限后的操作
     */
    protected void requestPermission(int id, String permission, Runnable allowableRunnable, Runnable disallowableRunnable) {
        if (allowableRunnable == null) {
            throw new IllegalArgumentException("allowableRunnable == null");
        }

        allowablePermissionRunnables.put(id, allowableRunnable);
        if (disallowableRunnable != null) {
            disallowablePermissionRunnables.put(id, disallowableRunnable);
        }

        //版本判断
        if (Build.VERSION.SDK_INT >= 23) {
            //减少是否拥有权限
            int checkCallPhonePermission = ContextCompat.checkSelfPermission(getApplicationContext(), permission);
            if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
                //弹出对话框接收权限
                ActivityCompat.requestPermissions(BaseActivity.this, new String[]{permission}, id);
                return;
            } else {
                allowableRunnable.run();
            }
        } else {
            allowableRunnable.run();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Runnable allowRun = allowablePermissionRunnables.get(requestCode);
            allowRun.run();
        } else {
            Runnable disallowRun = disallowablePermissionRunnables.get(requestCode);
            disallowRun.run();
        }
    }
}
public class MainActivity extends BaseActivity implements View.OnClickListener{
    private Button btCallPhone;
    private Button btContact;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btCallPhone = (Button) findViewById(R.id.call_phone);
        btContact = (Button) findViewById(R.id.contact);

        btCallPhone.setOnClickListener(this);
        btContact.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v == btCallPhone){
            //拨打电话
            requestPermission(1, Manifest.permission.CALL_PHONE, new Runnable() {
                @Override
                public void run() {
                    callPhone();
                }
            }, new Runnable() {
                @Override
                public void run() {
                    callPhoneDenied();
                }
            });
        }else if(v == btContact){
            //读取联系人信息
            requestPermission(2, Manifest.permission.WRITE_CONTACTS, new Runnable() {
                @Override
                public void run() {
                    readContact();
                }
            }, new Runnable() {
                @Override
                public void run() {
                    readContactDenied();
                }
            });
        }
    }

    private void callPhone() {
        Toast.makeText(MainActivity.this, "CALL_PHONE OK", Toast.LENGTH_SHORT)
                .show();
    }

    private void callPhoneDenied() {
        Toast.makeText(MainActivity.this, "CALL_PHONE Denied", Toast.LENGTH_SHORT)
                .show();
    }

    private void readContact() {
        ContentResolver cr = getContentResolver();
        String str[] = {ContactsContract.CommonDataKinds.Phone.CONTACT_ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER,
                ContactsContract.CommonDataKinds.Phone.PHOTO_ID};
        Cursor cur = cr.query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI, str, null,
                null, null);
        int count = cur.getCount();
        cur.close();

        Toast.makeText(MainActivity.this, String.format("发现%s条", count), Toast.LENGTH_SHORT)
                .show();
    }

    private void readContactDenied() {
        Toast.makeText(MainActivity.this, "Contact Denied", Toast.LENGTH_SHORT)
                .show();
    }
}

源码下载地址>>

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
============================================================== 兼容包实际上是为了让android 2.2、android 2.3 android 3.0支持android 4.0中才有的东西的。 比如说Actionbar。如果你不用兼容包的话,你开发含actionbar的程序就不可以在2.2上运行了 Support v11 v12 v13 v14 v17 Android 4.2.2 v18 Android 4.3.1 v21 Android 5.0.1 v22 Android 5.1.1 v23 Android 6.0 这个工程默认的是v21 Anroid 5.0.1, 如果需要编译(v22 Android 5.1.1 或者 v23 Android 6.0)版本,请 分别将values-v22 或者 values-v23 复制到 appcompat\res. 这是因为编译appcompat 需要SDK的部分资源,如果你需要编译v22版本的 appcompat-v7-v22.jar, 却将values-v23 复制到appcompat\res,那么会提示部分资源不存在,除非你选用v23的编译工具。 导入工程的方法见: http://blog.csdn.net/judyge/article/details/49228579 ============================================================== 出现这样的错误,说明你的工程缺乏appcompat V7 包 使用adt开发新建一个Android app,选择支持的SDK版本如果小于11(Android3.0)就会报如下错误。 error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'. 官网给出的答案是: https://developer.android.com/tools/support-library/setup.html#add-library 简单来说就是新的eclipse默认模版主题UI需要使用比较高版本api,如果需要支持低版本,需要导入appCompact库来支持,网上一般给出的解法: File->Import (android-sdk\extras\android\support\v7). Choose "appcompat" Project-> properties->Android. In the section library "Add" and choose "appCompat" 包括stackoverflow上也有很多人遇到,但很多人通过这个解决,但我就是没办法解决。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值