- 浏览: 2204413 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (1240)
- mac/IOS (287)
- flutter (1)
- J2EE (115)
- android基础知识 (582)
- android中级知识 (55)
- android组件(Widget)开发 (18)
- android 错误 (21)
- javascript (18)
- linux (70)
- 树莓派 (18)
- gwt/gxt (1)
- 工具(IDE)/包(jar) (18)
- web前端 (17)
- java 算法 (8)
- 其它 (5)
- chrome (7)
- 数据库 (8)
- 经济/金融 (0)
- english (2)
- HTML5 (7)
- 网络安全 (14)
- 设计欣赏/设计窗 (8)
- 汇编/C (8)
- 工具类 (4)
- 游戏 (5)
- 开发频道 (5)
- Android OpenGL (1)
- 科学 (4)
- 运维 (0)
- 好东西 (6)
- 美食 (1)
最新评论
-
liangzai_cool:
请教一下,文中,shell、C、Python三种方式控制led ...
树莓派 - MAX7219 -
jiazimo:
...
Kafka源码分析-序列5 -Producer -RecordAccumulator队列分析 -
hp321:
Windows该命令是不是需要安装什么软件才可以?我试过不行( ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
hp321:
Chenzh_758 写道其实直接用一下代码就可以解决了:JP ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
huanghonhpeng:
大哥你真强什么都会,研究研究。。。。小弟在这里学到了很多知识。 ...
android 浏览器
一、概述
随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化。对于6.0的几个主要的变化,查看查看官网的这篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中当然包含Runtime Permissions。
ok,本篇文章目的之一就是对运行时权限处理的一个介绍,以及对目前权限相关的库的一些了解。
当然非常推荐阅读官网权限相关文章:
本文也是在上述文章基础上理解、实验以及封装。
二、运行时权限的变化及特点
对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
Normal Permissions如下
Dangerous Permissions:
可以通过adb shell pm list permissions -d -g进行查看。
看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的,恩,没错,的确是这样的,
那么有个问题:分组对我们的权限机制有什么影响吗?
的确是有影响的,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
三、相关API
好在运行时相关的API也比较简单,所以适配起来并不会非常痛苦。
API的讲解就跟着申请权限步骤一起了:
1. 在AndroidManifest文件中添加需要的权限。这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。
2. 检查权限
这里涉及到一个API,ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。
3. 申请授权
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
4. 处理权限申请回调
ok,对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。如果申请成功,就可以做你的事情了~
当然,到此我们的权限申请的不走,基本介绍就如上述。不过还有个API值得提一下:
这个API主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。
那么将上述几个步骤结合到一起就是:
四、简单的例子
这里写一个简单的例子,针对于运行时权限。相信大家在最开始接触Android的时候,都利用Intent实验了打电话、发短信等功能。
我们看看直接拨打电话在Android 6.x的设备上权限需要如何处理。
当然代码非常简单:
在Android 6.x上运行是,点击testCall,即会弹出授权窗口,如何你Allow则直接拨打电话,如果Denied则Toast弹出”Permission Denied”。
例子很简单,不过需要注意的是,对于Intent这种方式,很多情况下是不需要授权的甚至权限都不需要的,比如:你是到拨号界面而不是直接拨打电话,就不需要去申请权限;打开系统图库去选择照片;调用系统相机app去牌照等。更多请参考[url= Consider Using an Intent
]Consider Using an Intent [/url]
.
当然,上例也说明了并非所有的通过Intent的方式都不需要申请权限。一般情况下,你是通过Intent打开另一个app,让用户通过该app去做一些事情,你只关注结果(onActivityResult),那么权限是不需要你处理的,而是由打开的app去处理。
五、封装
(1)申请权限
虽然权限处理并不复杂,但是需要编写很多重复的代码,所以目前也有很多库对用法进行了封装,大家可以在github首页搜索:android permission,对比了几个库的使用方式,发现https://github.com/lovedise/PermissionGen这个库据我所见相比较而言使用算是比较简单的,那么就以这个库的源码为基础来讲解,大家有兴趣可以多看几个库的源码。
封装的代码很简单,正如大家的对上面的权限代码所见,没有特别复杂的地方。
对于申请权限的代码,原本的编写为:
大家可以看到,对于其他的权限,其实申请的逻辑是类似的;唯一不同的肯定就是参数,那么看上面的代码,我们需要3个参数:Activity|Fragment、权限字符串数组、int型申请码。
也就是说,我们只需要写个方法,接受这几个参数即可,然后逻辑是统一的。
Utils.findDeniedPermissions其实就是check没有授权的权限。
那么上述的逻辑就很清晰了,需要的3种参数传入,先去除已经申请的权限,然后开始申请权限。
ok,我相信上面代码,大家扫一眼就可以了解了。
(2)处理权限回调
对于回调,因为要根据是否授权去执行不同的事情,所以很多框架也需要些一连串的代码,或者和前面的申请代码耦合。不过这个框架还是比较方便的,也是我选择它来讲解的原因。
回调主要做的事情:
对于第一条逻辑都一样;对于第二条,因为涉及到两个分支,每个分支执行不同的方法。
对于第二条,很多框架选择将两个分支的方法在申请权限的时候进行注册,然后在回调中根据requestCode进行匹配执行,不过这样需要在针对每次申请进行对象管理。
不过这个框架采取了一种很有意思的做法,它利用注解去确定需要执行的方法,存在两个注解:
利用反射根据授权情况+requestCode即可找到注解标注的方法,然后直接执行即可。
大致的代码为:
首先根据grantResults进行判断成功还是失败,对于成功则:
根据注解和requestCode找到方法,然后反射执行即可。失败的逻辑类似,不贴代码了。
ok,到此我们的运行时权限相对于早起版本的变化、特点、以及如何处理和封装都介绍完了。
不过对于上面讲解的库,肯定有人会说:运行时反射会影响效率,没错,不过我已经在上述代码的基础上将运行时注解改成Annotation Processor的方式了,即编译时注解,这样的话,就不存在反射损失效率的问题了。本来准备fork修改,然后PR,结果写完,改动太大,估计PR是不可能通过了,所以另起项目了,也方便后面的做一些扩展和维护。
详见库:https://github.com/hongyangAndroid/MPermissions.
六、MPermissions用法
对外的接口和PermissionGen基本一致,因为申请只需要三个参数,抛弃了使用原本类库的单例的方式,直接一个几个静态方法,简单整洁暴力。
贴一个用法:
是不是简单明了~~对于onRequestPermissionsResult所有的Activity都是一致的,所以可以放到BaseActivity中去。此外,在Fragment中使用的方式一致,详见demo。
详见库:https://github.com/hongyangAndroid/MPermissions.
至于为什么不直接介绍MPermission的源码,因为主要涉及到Annotation Processor,所以以这种方式引出,后面考虑单篇博文介绍下我目前所会的编译时注解的相关做法以及API的使用。
最后感谢PermissionGen库!
随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化。对于6.0的几个主要的变化,查看查看官网的这篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中当然包含Runtime Permissions。
ok,本篇文章目的之一就是对运行时权限处理的一个介绍,以及对目前权限相关的库的一些了解。
当然非常推荐阅读官网权限相关文章:
本文也是在上述文章基础上理解、实验以及封装。
二、运行时权限的变化及特点
对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
Normal Permissions如下
ACCESS_LOCATION_EXTRA_COMMANDS ACCESS_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_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
可以通过adb shell pm list permissions -d -g进行查看。
看到上面的dangerous permissions,会发现一个问题,好像危险权限都是一组一组的,恩,没错,的确是这样的,
那么有个问题:分组对我们的权限机制有什么影响吗?
的确是有影响的,如果app运行在Android 6.x的机器上,对于授权机制是这样的。如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
三、相关API
好在运行时相关的API也比较简单,所以适配起来并不会非常痛苦。
API的讲解就跟着申请权限步骤一起了:
1. 在AndroidManifest文件中添加需要的权限。这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。
2. 检查权限
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { }else{ // }
这里涉及到一个API,ContextCompat.checkSelfPermission,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。
3. 申请授权
ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS);
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
4. 处理权限申请回调
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted, yay! Do the // contacts-related task you need to do. } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return; } } }
ok,对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。如果申请成功,就可以做你的事情了~
当然,到此我们的权限申请的不走,基本介绍就如上述。不过还有个API值得提一下:
// Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. }
这个API主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。
那么将上述几个步骤结合到一起就是:
// Here, thisActivity is the current activity if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // Show an expanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an // app-defined int constant. The callback method gets the // result of the request. } }
四、简单的例子
这里写一个简单的例子,针对于运行时权限。相信大家在最开始接触Android的时候,都利用Intent实验了打电话、发短信等功能。
我们看看直接拨打电话在Android 6.x的设备上权限需要如何处理。
当然代码非常简单:
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void testCall(View view) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, MY_PERMISSIONS_REQUEST_CALL_PHONE); } else { callPhone(); } } public void callPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "10086"); intent.setData(data); startActivity(intent); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { callPhone(); } else { // Permission Denied Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
在Android 6.x上运行是,点击testCall,即会弹出授权窗口,如何你Allow则直接拨打电话,如果Denied则Toast弹出”Permission Denied”。
例子很简单,不过需要注意的是,对于Intent这种方式,很多情况下是不需要授权的甚至权限都不需要的,比如:你是到拨号界面而不是直接拨打电话,就不需要去申请权限;打开系统图库去选择照片;调用系统相机app去牌照等。更多请参考[url= Consider Using an Intent
]Consider Using an Intent [/url]
.
当然,上例也说明了并非所有的通过Intent的方式都不需要申请权限。一般情况下,你是通过Intent打开另一个app,让用户通过该app去做一些事情,你只关注结果(onActivityResult),那么权限是不需要你处理的,而是由打开的app去处理。
五、封装
(1)申请权限
虽然权限处理并不复杂,但是需要编写很多重复的代码,所以目前也有很多库对用法进行了封装,大家可以在github首页搜索:android permission,对比了几个库的使用方式,发现https://github.com/lovedise/PermissionGen这个库据我所见相比较而言使用算是比较简单的,那么就以这个库的源码为基础来讲解,大家有兴趣可以多看几个库的源码。
封装的代码很简单,正如大家的对上面的权限代码所见,没有特别复杂的地方。
对于申请权限的代码,原本的编写为:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, MY_PERMISSIONS_REQUEST_CALL_PHONE); } else { callPhone(); }
大家可以看到,对于其他的权限,其实申请的逻辑是类似的;唯一不同的肯定就是参数,那么看上面的代码,我们需要3个参数:Activity|Fragment、权限字符串数组、int型申请码。
也就是说,我们只需要写个方法,接受这几个参数即可,然后逻辑是统一的。
public static void needPermission(Fragment fragment, int requestCode, String[] permissions) { requestPermissions(fragment, requestCode, permissions); } @TargetApi(value = Build.VERSION_CODES.M) private static void requestPermissions(Object object, int requestCode, String[] permissions) { if (!Utils.isOverMarshmallow()) { doExecuteSuccess(object, requestCode); return; } List<String> deniedPermissions = Utils.findDeniedPermissions(getActivity(object), permissions); if (deniedPermissions.size() > 0) { if (object instanceof Activity) { ((Activity) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode); } else if (object instanceof Fragment) { ((Fragment) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode); } else { throw new IllegalArgumentException(object.getClass().getName() + " is not supported"); } } else { doExecuteSuccess(object, requestCode); } }
Utils.findDeniedPermissions其实就是check没有授权的权限。
#Utils @TargetApi(value = Build.VERSION_CODES.M) public static List<String> findDeniedPermissions(Activity activity, String... permission) { List<String> denyPermissions = new ArrayList<>(); for (String value : permission) { if (activity.checkSelfPermission(value) != PackageManager.PERMISSION_GRANTED) { denyPermissions.add(value); } } return denyPermissions; }
那么上述的逻辑就很清晰了,需要的3种参数传入,先去除已经申请的权限,然后开始申请权限。
ok,我相信上面代码,大家扫一眼就可以了解了。
(2)处理权限回调
对于回调,因为要根据是否授权去执行不同的事情,所以很多框架也需要些一连串的代码,或者和前面的申请代码耦合。不过这个框架还是比较方便的,也是我选择它来讲解的原因。
回调主要做的事情:
- 了解是否授权成功。
- 根据授权情况进行回调。
对于第一条逻辑都一样;对于第二条,因为涉及到两个分支,每个分支执行不同的方法。
对于第二条,很多框架选择将两个分支的方法在申请权限的时候进行注册,然后在回调中根据requestCode进行匹配执行,不过这样需要在针对每次申请进行对象管理。
不过这个框架采取了一种很有意思的做法,它利用注解去确定需要执行的方法,存在两个注解:
@PermissionSuccess(requestCode = 100) @PermissionFail(requestCode = 100)
利用反射根据授权情况+requestCode即可找到注解标注的方法,然后直接执行即可。
大致的代码为:
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults); } private static void requestResult(Object obj, int requestCode, String[] permissions, int[] grantResults) { List<String> deniedPermissions = new ArrayList<>(); for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permissions[i]); } } if (deniedPermissions.size() > 0) { doExecuteFail(obj, requestCode); } else { doExecuteSuccess(obj, requestCode); } }
首先根据grantResults进行判断成功还是失败,对于成功则:
private static void doExecuteSuccess(Object activity, int requestCode) { Method executeMethod = Utils.findMethodWithRequestCode(activity.getClass(), PermissionSuccess.class, requestCode); executeMethod(activity, executeMethod); } #Utils public static <A extends Annotation> Method findMethodWithRequestCode(Class clazz, Class<A> annotation, int requestCode) { for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(annotation)) { if (isEqualRequestCodeFromAnntation(method, annotation, requestCode)) { return method; } } } return null; }
根据注解和requestCode找到方法,然后反射执行即可。失败的逻辑类似,不贴代码了。
ok,到此我们的运行时权限相对于早起版本的变化、特点、以及如何处理和封装都介绍完了。
不过对于上面讲解的库,肯定有人会说:运行时反射会影响效率,没错,不过我已经在上述代码的基础上将运行时注解改成Annotation Processor的方式了,即编译时注解,这样的话,就不存在反射损失效率的问题了。本来准备fork修改,然后PR,结果写完,改动太大,估计PR是不可能通过了,所以另起项目了,也方便后面的做一些扩展和维护。
详见库:https://github.com/hongyangAndroid/MPermissions.
六、MPermissions用法
对外的接口和PermissionGen基本一致,因为申请只需要三个参数,抛弃了使用原本类库的单例的方式,直接一个几个静态方法,简单整洁暴力。
贴一个用法:
public class MainActivity extends AppCompatActivity { private Button mBtnSdcard; private static final int REQUECT_CODE_SDCARD = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard); mBtnSdcard.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE); } }); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @PermissionGrant(REQUECT_CODE_SDCARD) public void requestSdcardSuccess() { Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show(); } @PermissionDenied(REQUECT_CODE_SDCARD) public void requestSdcardFailed() { Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show(); } }
是不是简单明了~~对于onRequestPermissionsResult所有的Activity都是一致的,所以可以放到BaseActivity中去。此外,在Fragment中使用的方式一致,详见demo。
详见库:https://github.com/hongyangAndroid/MPermissions.
至于为什么不直接介绍MPermission的源码,因为主要涉及到Annotation Processor,所以以这种方式引出,后面考虑单篇博文介绍下我目前所会的编译时注解的相关做法以及API的使用。
最后感谢PermissionGen库!
发表评论
-
带你深入理解 FLUTTER 中的字体“冷”知识
2020-08-10 23:40 635本篇将带你深入理解 Flutter 开发过程中关于字体和文 ... -
Flutter -自定义日历组件
2020-03-01 17:56 1111颜色文件和屏幕适配的文件 可以自己给定 import ... -
Dart高级(一)——泛型与Json To Bean
2020-02-23 19:13 1005从 Flutter 发布到现在, 越来越多人开始尝试使用 Da ... -
flutter loading、Progress进度条
2020-02-21 17:03 1181Flutter Progress 1 条形无固定值进度条 ... -
Flutter使用Https加载图片
2020-02-21 01:39 1020Flutter使用Https加载图片 使用http加载图片出 ... -
flutter shared_preferences 异步变同步
2020-02-21 00:55 848前言 引用 在开发原生iOS或Native应用时,一般有判断上 ... -
Flutter TextField边框颜色
2020-02-19 21:31 937监听要销毁 myController.dispose(); T ... -
flutter Future的正确用法
2020-02-18 21:55 808在flutter中经常会用到异步任务,dart中异步任务异步处 ... -
记一次Flutter简单粗暴处理HTTPS证书检验方法
2020-02-18 14:13 979最近在做Flutter项目到了遇到一个无解的事情,当使用Ima ... -
flutter 获取屏幕宽度高度 通知栏高度等屏幕信息
2019-07-27 08:39 1344##MediaQuery MediaQuery.of(con ... -
关于flutter RefreshIndicator扩展listview下拉刷新的问题
2019-07-10 19:40 1143当条目过少时listview某些嵌套情况下可能不会滚动(条目 ... -
flutter listview 改变状态的时候一直无限添加
2019-07-10 16:01 791setstate的时候会一直无限的调用listview.bui ... -
Flutter Android端启动白屏问题的解决
2019-07-09 00:51 1526问题描述 Flutter 应用在 Android 端上启动时 ... -
Flutter中SnackBar使用
2019-07-08 23:43 781底部弹出,然后在指定时间后消失。 注意: build(Bui ... -
Flutter 之点击空白区域收起键盘
2019-07-08 18:43 1792点击空白处取消TextField焦点这个需求是非常简单的,在学 ... -
Flutter 弹窗 Dialog ,AlertDialog,IOS风格
2019-07-08 18:04 1383import 'package:flutter/mate ... -
flutter ---TextField 之 输入类型、长度限制
2019-07-08 14:30 2337TextField想要实现输入类型、长度限制需要先引入impo ... -
【flutter 溢出BUG】键盘上显示bottom overflowed by 104 PIXELS
2019-07-08 11:13 1567一开始直接使用Scaffold布局,body:new Colu ... -
解决Flutter项目卡在Initializing gradle...界面的问题
2019-07-07 12:53 880Flutter最近很火,我抽出了一点时间对Flutter进行了 ... -
关于android O 上 NotificationChannel 的一些注意事项
2019-07-04 11:47 941最近在适配android O,遇到个问题,应用中原本有设置界面 ...
相关推荐
在Android M(Android 6.0)中,系统引入了重要的安全更新——运行时权限(Runtime Permissions)。这一改变显著增强了用户对应用权限的控制,使得应用程序在安装后首次需要使用特定敏感权限时,才会请求用户的许可...
在Android 6.0(API级别23)及以上版本,系统引入了运行时权限的概念,改变了之前在安装应用时一次性授予所有权限的传统模式。这一变化是Android为了提高用户对隐私控制的灵活性,使得用户能够在应用运行过程中根据...
《深入解析Android 6.0源码:Marshmallow的秘密》 Android 6.0,代号Marshmallow,是Google发布的一款重要操作系统版本,为开发者和用户带来了诸多改进和新特性。源码作为软件开发的核心,揭示了系统运行的底层机制...
在Android 6.0中,开发者需要在`AndroidManifest.xml`中声明所需的所有权限,并且需要编写代码来处理运行时权限的请求和响应。为了简化这个过程,可以进行自定义封装,创建一个工具类或者库,用于处理权限请求和结果...
本资料“2016-12-27郭霖-6.0运行时权限源码”可能包含了对这一机制的深入解析,以及相关的代码示例。 运行时权限的核心在于` ActivityCompat`和`PermissionChecker`类,它们帮助开发者处理权限请求。当应用需要使用...
1. **权限管理**:Android 6.0引入了运行时权限模型,用户可以在安装应用后对个别敏感权限进行授权或拒绝,而不再是在安装时一次性全部同意。 2. **Doze模式**:这是一个电池优化功能,当设备闲置时,系统会进入...
#### Android 6.0权限管理变化 - **按需授权**:用户不必在安装应用时同意所有的权限请求,而是在应用首次运行或特定功能被调用时才请求所需的权限。 - **权限组**:将相关的权限分组,比如位置权限组包含粗略位置...
本篇将围绕“android6.0 动态申请权限问题”进行深入解析,并结合提供的“permissiondemo”示例,探讨如何解决这一问题。 首先,理解Android 6.0权限模型的变化至关重要。在Android 6.0之前,应用在安装时会一次性...
- **变化**:Android 6.0引入了运行时权限的概念,允许用户在应用运行过程中动态地授予或撤销特定权限。 - **实现方法**: - 使用`ContextCompat.checkSelfPermission()`检查当前应用是否已获取所需权限。 - ...
在Android 6.0(API级别23)中,谷歌引入了一项名为“运行时权限”的重大更改,以增强用户对应用权限的控制。在此之前,应用在安装时会一次性请求所有必要的权限,而在Android 6.0及更高版本中,应用在运行时根据...
- **运行时权限**:Android 6.0 引入了运行时权限模型,应用在运行时请求敏感权限,`Launcher3` 需要适配这一变化。 - **Doze模式**:提高了设备的电池续航,`Launcher3` 需要在 Doze 模式下优化其后台行为。 - **...
在Android 6.0中,对权限管理进行了重大改进,引入了运行时权限的概念。这使得应用在安装后需要在运行时请求用户授权敏感权限,如访问联系人、位置等,提高了用户隐私保护。这部分源码可以在`frameworks/base/...
6. **权限管理**:遵循 Android 的权限模型,确保用户只能访问和操作他们有权操作的元素。 在 Android 6.0 中,对 Launcher2 进行了优化,以提高性能和用户体验。例如,可能包含了更快的启动时间、更流畅的滚动效果...
Android 6.0引入了运行时权限,对BLE的使用需要在运行时请求`ACCESS_COARSE_LOCATION`或`ACCESS_FINE_LOCATION`权限,因为BLE扫描可能涉及地理位置信息。 总的来说,Android 6.0的BLE搜索涉及多个步骤,包括初始化...
Android 6.0引入了多项新特性,如运行时权限管理、Doze模式以提高电池续航、App Links以增强应用间链接的处理、以及对指纹识别的支持等。这些特性都可能在海康监控的demo中有所体现,比如安全访问权限的管理,可能...
自Android 6.0(API级别23)起,系统引入了运行时权限的概念,这意味着开发者需要在应用运行时请求敏感权限,而不是在安装时。对于获取运行中的APP信息,可能需要`GET_TASKS`权限,这是一个危险权限,因此需要在运行...
在Android 6.0(API级别23)及以上版本,系统引入了运行时权限管理机制,这改变了以往在AndroidManifest.xml文件中一次性声明所有权限的传统方式。为了更好地保护用户隐私,应用现在必须在运行时请求敏感权限,即使...
3. 摄像头权限:处理Android 6.0及以上版本的运行时权限请求,确保应用可以访问摄像头。 4. 扫描回调:实现ZXing的扫描回调接口,处理扫描成功或失败的情况,展示扫描结果。 5. 生成二维码:调用ZXing的编码接口,将...
这需要在AndroidManifest.xml中添加权限声明,并在运行时动态请求用户授权。 3. **指纹检测**:首先,我们需要通过`FingerprintManager`的`hasEnrolledFingerprints()`方法检查设备上是否已经注册了指纹。如果用户...
《农场自动化系统FAS_Android在Android 6.0及更高版本中的运行时权限解析》 随着移动设备的发展,Android操作系统也在不断进步,从早期版本的静态权限模型进化到Android 6.0(Marshmallow)引入的运行时权限管理...