和之前在安装 APP 的是就申请了权限不一样,Google 在 API 23,也就 6.0 以后加入了动态权限。对于一些敏感的权限,决定权交还给了用户,再也不是强制申请了。由于这个缘由,若是 APP 须要支持 6.0 以上的系统,就须要进行一下适配,不然 APP 就会崩溃。html
好比,下面拨打电话.。已经在在 AndroidManifest.xml
中申明权限了java
<uses-permission android:name="android.permission.CALL_PHONE"/>
这是拨打电话的代码实现android
private void callPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "12312341234"); intent.setData(data); startActivity(intent); }
可是程序若是是在 Android 6.0 的设备中运行的话,一运行就崩溃了,报错信息以下git
java.lang.SecurityException: Permission Denial: ... 后面的信息省略
那么要怎么才能适配呢?github
首先要先知道动态权限有哪些?ide
Andriod 中的动态权限是按组来分的,下面的表格来自官网ui
每一个组中的权限不用所有申请,申请了其中一个,组中其余权限也就一块儿申请到了。this
有个前提,咱们的 Activity 好比 MainActivity 再也不是继承于 Acticity
了,而是继承于 AppCompatActivity
,由于动态权限的一些方法是只有 AppCompatActivity
才有的。google
首先是检测是否已经赋予了权限,能够调用 ContextCompat.checkSelfPermission(Context, permissionName)
方法,经过方法的返回值来判断spa
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) { // 已经赋予权限,直接调用拨打电话的代码 callPhone(); } else { // 没有赋予权限,那就去申请权限 }
要申请权限可使用 requestPermissions (Activity activity, String[] permissions, int requestCode)
进行申请
private static final int REQUEST_CALL_PHONE = 456; ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE);
因而在界面上就会弹出一个请求拨打电话权限的对话框(这个对话框是系统弹出来的,样式不可修改)
无论用户选择赞成仍是拒绝,都会在 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
方法中接收到信息
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限请求成功 callPhone(); } else { // 用户拒绝了 showTipDialog(); } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void showTipDialog() { new AlertDialog.Builder(this) .setMessage("该程序须要电话权限,不然没法正常运行") .setPositiveButton(android.R.string.ok, null) .create() .show(); }
若是用户选择”赞成“,就能够调用拨打电话的按钮,同时这个对话框以后都不会出现了。
若是用户拒绝了,那就没法调用拨打电话的代码了。为了用户体验,能够弹出一个对话框,告知用户我须要这个权限,没有这个权限程序没法正常运行
用户看到了这个信息,因而再次点击“拨打电话”的按钮,程序又进行了权限检查,弹出申请权限的对话框,这时候的对话框的和以前第一次进行权限申请时弹出的对话框的样式不一样,多出了一个“再也不提醒”的勾选项
若是用户勾选“再也不询问”,对话框是这样的
意味着用户永远地拒绝拨打电话的权限,同时这个对话框不会再弹出来了,这就形成程序没法正常运行。还有办法能够补救一下,就是跳转到设置页面,让用户手动开启权限。Anroid 提供了一个方法 ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)
能够用来判断是否选择了“再也不提示”
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限请求成功 callPhone(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) { // 用户选择了“拒绝” showTipDialog(); } else { // 用户勾选了“再也不提示” goToSetting(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void goToSetting() { new AlertDialog.Builder(this) .setMessage("该程序须要电话权限,不然没法正常运行") .setPositiveButton("去打开权限", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }) .create() .show(); }
public class MainActivity extends AppCompatActivity { private static final int REQUEST_CALL_PHONE = 456; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { callPhoneWithCheck(); } }); } private void callPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "12312341234"); intent.setData(data); startActivity(intent); } private void callPhoneWithCheck() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) { // 已经赋予权限 callPhone(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限请求成功 callPhone(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) { // 用户选择了“拒绝” showTipDialog(); } else { // 用户勾选了“再也不提示” goToSetting(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void goToSetting() { new AlertDialog.Builder(this) .setMessage("该程序须要电话权限,不然没法正常运行") .setPositiveButton("去打开权限", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }) .create() .show(); } private void showTipDialog() { new AlertDialog.Builder(this) .setMessage("该程序须要电话权限,不然没法正常运行") .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE); } }) .create() .show(); } }
为了申请一个拨打电话的权限,要写好多代码。网上有个一个流行的开源库 PermissionsDispatcher,采用注解来快速实现以上的逻辑。用法很简单,就是须要注意到是,写完代码是须要编译一下 project,这样才能生成相应的代码。
http://blog.csdn.net/zhangqinghuazhangzhe/article/details/52801202
http://www.jianshu.com/p/d6b3e16cc1d9
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1110/3670.html
http://www.jianshu.com/p/b4a8b3d4f587
http://blog.csdn.net/quan356270259/article/details/50876272
http://blog.csdn.net/u010483016/article/details/50401605
http://www.jianshu.com/p/e1ab1a179fbb#
http://blog.csdn.net/u011200604/article/details/52874599
http://blog.csdn.net/yangqingqo/article/details/48371123