赞
踩
目录
4.2 AndroidUtilCode中的权限类(本人惯用)
4.4 自己手撸一个零时权限框架推荐学习SoulPermission
安卓系统权限分为两类:
Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上,又新增了运行时权限动态检测。上述的危险权限分为9组如下:
- //日历;摄像头;通讯录;地理位置;麦克风;电话;身体传感器;短信;存储空间
-
-
- group:android.permission-group.CONTACTS //联系人分组
- permission:android.permission.WRITE_CONTACTS //写入联系人
- permission:android.permission.GET_ACCOUNTS //访问GMail账户列表
- 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 //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 //通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米
- permission:android.permission.ACCESS_COARSE_LOCATION //通过GPS芯片接收卫星的定位信息,定位精度达10米以内
-
- 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 //允许程序监控将收到WAP PUSH信息
- permission:android.permission.RECEIVE_MMS //允许一个程序监控将收到MMS彩信
- permission:android.permission.RECEIVE_SMS //允许程序监控一个将收到短信息
- permission:android.permission.SEND_SMS //发送短信
- permission:android.permission.READ_CELL_BROADCASTS //读取广播 需要具体实例
注:onRequestPermissionsResult()方法为使用requestPermissions()方法后的回调
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);
-
- chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission. WRITE_EXTERNAL_STORAGE }, 1);
- } else {
- openAlbum();
- }
- }
- });
- }
-
- private void openAlbum() {
- Intent intent = new Intent("android.intent.action.GET_CONTENT");
- intent.setType("image/*");
- startActivityForResult(intent, CHOOSE_PHOTO); // 打开相册
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- switch (requestCode) {
- case 1:
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- openAlbum();
- } else {
- Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
- }
- break;
- default:
- }
- }
我们在Fragment中申请时不是使用AcyivityCompat而是使用Fragment本身,接着类似于Activity,在Fragement中的onRequestPermissionsResult()方法中进行回调。代码如下:
MyFragment.this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
注:fragement中无法回调onRequestPermissionsResult()方法:
在Fragment中申请权限时发现Fragment中不会回调onRequestPermissionsResult方法,如果我们在Activity中重写onRequestPermissionsResult方法你会发现它会回调到Activity中的onRequestPermissionsResult方法,所以我们需要在Activity中的onRequestPermissionsResult方法中进行处理让它把改事件传递到我们的fragment中:
- // Activity中
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
- {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- // 获取到Activity下的Fragment
- List<Fragment> fragments = getSupportFragmentManager().getFragments();
- if (fragments == null)
- {
- return;
- }
- // 查找在Fragment中onRequestPermissionsResult方法并调用
- for (Fragment fragment : fragments)
- {
- if (fragment != null)
- {
- // 这里就会调用我们Fragment中的onRequestPermissionsResult方法
- fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
- }
- }
之后在我们申请权限的Fragment中重写onRequestPermissionsResult方法时,通过在Activity中遍历所有的Fragment中调用onRequestPermissionsResult方法,就是我们Fragment中重写的onRequestPermissionsResult方法:
- // Fragment 中
- @Override
- public void onRequestPermissionsResult(final int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults)
- {
- // TODO 写我们自己的处理逻辑
- }
检查应用是否有指定权限。返回值为 PackageManager.PERMISSION_GRANTED 表示有权限, PackageManager.PERMISSION_DENIED 表示无权限。
请求指定权限,可以是多个,以数组的方式。
如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
请求权限的结果回调。
因为以上列举的相关API都是在 API 23 才有的,为了适配低版本,官方提供了 v4 v13 兼容包。我们可以直接使用兼容包中的方法进行权限处理。
步骤(以拨打电话为例)
<uses-permission android:name="android.permission.CALL_PHONE"/>
-
- if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
- != PackageManager.PERMISSION_GRANTED) {
- // 应用没有授予拨打电话权限,请求权限
- requestCameraPermission();
- } else {
- // 应用被授予拨打电话权限 PackageManager.PERMISSION_GRANTED
- makeCall();
- }
-
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE);
-
- if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
- // 向用户详细解释申请该权限的原因
- new AlertDialog.Builder(this)
- .setCancelable(false)
- .setMessage("拨打电话需要使用电话权限,如果不授予权限会导致该功能无法正常使用")
- .setPositiveButton("好的", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- ActivityCompat.requestPermissions(
- OriginalActivity.this,
- new String[]{Manifest.permission.CALL_PHONE},
- REQUEST_CALLPHONE
- );
- }
- })
- .setNegativeButton("不给", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .show();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
-
- if (requestCode == REQUEST_CALLPHONE) {
- if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // 授予权限,拨打电话
- makeCall();
- } else {
- Toast.makeText(this, "请求权限被拒绝", Toast.LENGTH_SHORT).show();
- }
- } else {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
- }
github star 9.1k;
在处理运行时权限的时候,虽然官方提供了兼容包不再需要做版本检查,但处理起来依然使代码很杂乱。现在已经出现了很多处理运行时权限的开源库,这里给大家推荐 PermissionsDispatcher。该库在GitHub同比获得 star 最多。而且使用 apt 技术,在编译时期动态生成xxxxPermissionsDispatcher模板代码,效率很高!
API 简介
该库使用 apt 技术,自然使用的就是注解。
注解 | 是否必须 | 作用 |
---|---|---|
@RuntimePermissions | √ | 标记Activity/Fragment,则注解解释器会生成对应类的代码 |
@NeedsPermission | √ | 标记需要授权才能执行的方法 |
@OnShowRationale | 对应shouldShowRequestPermissionRationale(),当应用之前请求过此权限但用户拒绝了请求,再次请求时调用 | |
@OnPermissionDenied | 当请求权限遭拒绝时调用 | |
@OnNeverAskAgain | 当用户勾选不再提示,并拒绝权限时,再次请求时调用 |
步骤(以使用相机为例)
<uses-permission android:name="android.permission.CAMERA" />
-
- @RuntimePermissions
- public class PermissionsDispatcherActivity extends AppCompatActivity {
-
- private ImageView imageView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- imageView = (ImageView) findViewById(R.id.imageView);
-
- findViewById(R.id.btn_camera).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- PermissionsDispatcherActivityPermissionsDispatcher.takePhotoWithCheck(PermissionsDispatcherActivity.this);
- }
- });
-
- }
-
- @NeedsPermission(Manifest.permission.CAMERA)
- void takePhoto() {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 启动系统相机
- startActivityForResult(intent, 100);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK) { // 如果返回数据
- if (requestCode == 100) { // 判断请求码是否为REQUEST_CAMERA,如果是代表是这个页面传过去的,需要进行获取
- Bundle bundle = data.getExtras(); // 从data中取出传递回来缩略图的信息,图片质量差,适合传递小图片
- Bitmap bitmap = (Bitmap) bundle.get("data"); // 将data中的信息流解析为Bitmap类型
- imageView.setImageBitmap(bitmap);// 显示图片
- }
- }
- }
-
- @OnShowRationale(Manifest.permission.CAMERA)
- void showRationaleForRecord(final PermissionRequest request) {
- new AlertDialog.Builder(this)
- .setPositiveButton("好的", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- request.proceed();
- }
- })
- .setNegativeButton("不给", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- request.cancel();
- }
- })
- .setCancelable(false)
- .setMessage("拍照需要相机权限,应用将要申请使用相机权限")
- .show();
- }
-
- @OnPermissionDenied(Manifest.permission.CAMERA)
- void showCameraDenied() {
- Toast.makeText(getApplicationContext(), "权限被拒绝", Toast.LENGTH_LONG).show();
- }
-
- @OnNeverAskAgain(Manifest.permission.CAMERA)
- void onRCameraNeverAskAgain() {
- new AlertDialog.Builder(this)
- .setPositiveButton("好的", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // 打开系统应用设置
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- intent.setData(Uri.parse("package:" + getPackageName()));
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- dialog.cancel();
- }
- })
- .setNegativeButton("取消", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- })
- .setCancelable(false)
- .setMessage("您已经禁止了相机权限,是否现在去开启")
- .show();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- PermissionsDispatcherActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
- }
- }
使用注意
详情可见作者文章:终于等到你--权限工具类
封装一个PermissionHelper.java 类,方便使用:
- import android.app.Activity;
- import android.content.DialogInterface;
- import android.support.v7.app.AlertDialog;
-
- import com.blankj.utilcode.constant.PermissionConstants;
- import com.blankj.utilcode.util.ActivityUtils;
- import com.blankj.utilcode.util.LogUtils;
- import com.blankj.utilcode.util.PermissionUtils;
-
- import java.util.List;
-
-
- public class PermissionHelper {
-
- public static void requestCamera(final OnPermissionGrantedListener listener) {
- request(listener, PermissionConstants.CAMERA);
- }
-
- public static void requestStorage(final OnPermissionGrantedListener listener) {
- request(listener, PermissionConstants.STORAGE);
- }
-
- public static void requestPhone(final OnPermissionGrantedListener listener) {
- request(listener, PermissionConstants.PHONE);
- }
-
- public static void requestPhone(final OnPermissionGrantedListener grantedListener,
- final OnPermissionDeniedListener deniedListener) {
- request(grantedListener, deniedListener, PermissionConstants.PHONE);
- }
-
- public static void requestSms(final OnPermissionGrantedListener listener) {
- request(listener, PermissionConstants.SMS);
- }
-
- public static void requestLocation(final OnPermissionGrantedListener listener) {
- request(listener, PermissionConstants.LOCATION);
- }
-
- private static void request(final OnPermissionGrantedListener grantedListener,
- final @PermissionConstants.Permission String... permissions) {
- request(grantedListener, null, permissions);
- }
-
- private static void request(final OnPermissionGrantedListener grantedListener,
- final OnPermissionDeniedListener deniedListener,
- final @PermissionConstants.Permission String... permissions) {
- PermissionUtils.permission(permissions)
- .rationale(new PermissionUtils.OnRationaleListener() {
- @Override
- public void rationale(ShouldRequest shouldRequest) {
- shouldRequest.again(true);
- }
- })
- .callback(new PermissionUtils.FullCallback() {
- @Override
- public void onGranted(List<String> permissionsGranted) {
- if (grantedListener != null) {
- grantedListener.onPermissionGranted();
- }
- LogUtils.d(permissionsGranted);
- }
-
- @Override
- public void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied) {
- if (!permissionsDeniedForever.isEmpty()) {
- showOpenAppSettingDialog();
- }
- if (deniedListener != null) {
- deniedListener.onPermissionDenied();
- }
- LogUtils.d(permissionsDeniedForever, permissionsDenied);
- }
- })
- .request();
- }
-
- public interface OnPermissionGrantedListener {
- void onPermissionGranted();
- }
-
- public interface OnPermissionDeniedListener {
- void onPermissionDenied();
- }
-
-
- //权限拒绝后(加了不再询问),再要权限的时候需要打开系统设置
- public static void showOpenAppSettingDialog() {
- Activity topActivity = ActivityUtils.getTopActivity();
- if (topActivity == null || topActivity.isFinishing()) return;
- new AlertDialog.Builder(topActivity)
- .setTitle(android.R.string.dialog_alert_title)
- .setMessage("We need some of the permissions you rejected or the system failed to apply failed, please manually set to the page authorize, otherwise the function can\\'t be used normally!")
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- PermissionUtils.launchAppDetailsSettings();
- }
- })
- .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
-
- }
- })
- .setCancelable(false)
- .create()
- .show();
- }
- }
使用举例:
- PermissionHelper.requestCamera(new PermissionHelper.OnPermissionGrantedListener() {
- @SuppressLint("MissingPermission")
- @Override
- public void onPermissionGranted() {
- RxDialogChooseImage dialogChooseImage = new RxDialogChooseImage(mContext, TITLE);
- dialogChooseImage.show();
- }
- });
github star 8.6k的零时权限处理框架,业内熟知,推荐使用。
源码中,作者主要
优雅的避掉onPermissionResult:用一个没有界面的Fragment去完成我们权限请求的操作
舍去Activity:使用Application中的ActivityLifecycleCallbacks,使用它的 registerActivityLifecycleCallbacks,感知Activity声明周期变化,获取到当前应用栈顶的Activity。Application中ActivityLifecycleCallbacks的原理分析请看这篇文章;
最后避掉Application(免初始化):参考Lifecycle组件的初始化,通过自定义ContentProvider来完成库的初始化;
通过阅读学习,完全可以自己手撸仿写一个属于自己的零时权限框架!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。