股票场内基金交易,没时间盯盘?
权限介绍
权限是一种安全机制。Android 权限主要用于限制应用程序内部某些具有限制性特性的功能使用以及应用程序之间的组件访问。说白了就是,你想使用某些功能,必须先声明出来。在 Android6.0 以后,对于不“危险”的权限可以在“声明后”使用,而对于像摄像头这样的“危险”的权限则需要运行时授权。
基本权限
所谓基本权限就是 Android 原生权限,例如网络链接权限,外部文件读写权限,蓝牙权限等等,软件与硬件方面都有。由于基本权限数量较多,此处就不再一一列举了,权限的详细列表可在开发者手册中查询 Manifest.permission | Android Developers。
自定义权限
除去 Android 系统提供的权限,开发者也可以自定义权限。使用自定义权限可以实现应用程序之间的组建访问,在 AndroidManifest 文件中来进行权限的自定义。例如:
1 2 3 4 5 |
<permission android:name="qiji.tech.permission.CUSTOM_PERMISSION" android:protectionLevel="normal" > </permission> |
这样便实现了一个自定义权限,注意 Permission 的各项属性,Name 和
Protection Level(风险等级)是必须的,其它项都可不写。
Name 字段表示权限的名字
Protection Level 字段表示权限的等级:normal dangerous signature signatureOrSystem 对于普通和危险级别的权限,我们称之为低级权限,应用申请即授予。其他两级权限,我们称之为高级权限或系统权限。当应用试图在没有权限的情况下做受限操作,应用将被系统杀掉以警示。系统应用可以使用任何权限。权限的声明者可无条件使用该权限。
以喵喵附近为例
由于推送服务用到了多进程,因此声明了一个名为MIPUSH_SERVICE自定义权限。
权限使用
权限声明
对于需要使用的权限,需要在 AndroidManifest 中的 manifest 标签内使用 uses-permission 声明使用某一个权限 例如:
1 2 3 4 5 |
<manifest> <uses-permission android:name="android.permission.INTERNET”/> ... </manifest> |
这样便声明了一个网络连接的权限。关于自定义权限的声明与普通的权限声明完全相同此处不再展开。
Android6.0 运行时权限
上面提到,对于 Android6.0(API23) 引入了运行时权限的概念,即对于“危险”的权限,仅单纯声明也无法使用相应的功能,需要在第一次用户用到此权限的时候运行时确认,同意或拒绝该权限的使用。这些所谓的“危险”权限包括一下几个:
在API23以上的系统使用以上权限,如果不进行运行时动态申请权限则会造成应用闪退,切记。
注意:上图中权限组的概念,同一权限组的权限只要授权了一个,其他也就默认授权了,例如,授权了外部存储读取,那么外部存储的写入也会相应的授权,无须另行请求。
因此对于 API23 以上的运行时权限除了在 Manifest 文件中进行权限声明还需要在代码中作出如下处理:
1.在使用相关功能时判断相关权限是否已经获取:
1 2 3 4 5 6 7 8 |
private boolean isGrand(String permission){ //当 Android 版本低于23时,无须判断声明即可用 if (Build.VERSION.SDK_INT<Build.VERSION_CODES.M) return true; //判断是否进行了授权 int checkPermission = checkSelfPermission(permission); return checkPermission == PackageManager.PERMISSION_GRANTED; } |
2.若未授权择要申请权限:
1 2 3 |
private static final int PERMISSION_REQUEST_CODE = 1024; ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); |
注意:这里使用的是 ActivityCompat 的方法,不是 Activity 本身的方法,这是由于运行时权限是在 API23 时才加入的,因此 Activity 的
requestPermissions 方法最低版本是API23,因此要调用ActivityCompat的方法,以达到统一。
requestPermissions 方法的第二个参数是一个 String 的数组,因此可以一次申请多个权限。
3.申请权限结果处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode != PERMISSION_REQUEST_CODE) return; if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // TODO: 权限申请通过,继续进行逻辑处理 } else if (grantResults[0] == PackageManager.PERMISSION_DENIED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { // TODO: 权限申请未通过,可进行相关说明,为什么需要此权限,然后继续申请 } else { // TODO: 权限申请未通过,且无法再次询问。 } } } |
第一次请求权限时,用户拒绝了,shouldShowRequestPermissionRationale() 返回 true,这时候可以显示一些为什么需要这个权限的说明;
第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时shouldShowRequestPermissionRationale() 返回 false,此后应用无法再次申请此权限,对于相当重要的权限没有通过,例如下载软件的“外部存储”权限,应用可以强制关闭,避免闪退现象发生。
注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale() 也会一直返回true。
RxPermissions介绍
可以看到运行时权限在提升用户安全的同时,对于开发造成了一定的复杂程度的提升。
那么有没有一个高效的运行时权限的封装库呢??
RxPermissions 是用RxJava实现的一个运行时权限的封装库,使用方便,代码实现清晰,一目了然。Github
基本用法
1.创建一个实例:
1 2 |
RxPermissions rxPermissions = new RxPermissions(Activity.this); |
2.申请权限:
1 2 3 4 5 6 7 8 9 |
rxPermissions.request(Manifest.permission.CAMERA) .subscribe(granted -> { if (granted) { // API23 之前总是 true // 权限允许 } else { // 权限拒绝 } }); |
这样便获取了照相头的权限,进行相关的操作。同样,由于 request 方法接受可变参数,我们还可以一次申请多个权限。例如:
1 2 3 4 5 6 7 8 9 |
rxPermissions.request(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE ) .subscribe(granted -> { if (granted) { // 这里只有两个权限同时允许才会返回true } else { // 有一个权限不允许就会返回false } }); |
假如我们想获得每个权限的申请情况,而不仅仅是一股脑儿告诉你“你申请的权限没通过”,可以使用 requestEach 方法。
1 2 3 4 5 6 7 |
rxPermissions.requestEach(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE).subscribe( new Action1<Permission>() { @Override public void call(Permission permission) { //这里便可以对每一个返回的权限来进行判断状态。 } }); |
源码分析
知其然知其所以然。
既然 RxPermissions 这么好用我们来简单的了解下其原理,这里我们不深究细节不然会涉及到太多的 RxJava 的技术。我们只需要知道它是个怎么实现的,以及它的原理是什么就好了。(说明一点:为了和项目的RxJava的版本相符,本次采用1.0的源码)
首先我们看一下它的主结构图
1 2 3 4 5 6 7 8 9 10 |
├── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── tbruyelle │ └── rxpermissions │ ├── Permission.java //权限封装类 │ ├── RxPermissions.java //核心类 │ └── RxPermissionsFragment.java //实际请求的Fragment |
RxPermissions.java 是我们这次的关键所在。在看代码之前,我们可以根据上述的调用,来思考一些问题:
1.对于多个权限,可以只返回一个bool值,因此它可能是把几个权限作为一个 Observable 进行判断,封装成一个 Observable
2.同时对于多个权限,可以分别返回 Observable
带着问题,看源码:
首先是创建实例:
1 2 3 4 |
public RxPermissions(@NonNull Activity activity) { mRxPermissionsFragment = getRxPermissionsFragment(activity); } |
创建并保存了一个 RxPermissionsFragment,然后是 request(String … ) 方法
1 2 3 4 5 |
@SuppressWarnings({"WeakerAccess", "unused"}) public Observable<Boolean> request(final String... permissions) { return Observable.just(null).compose(ensure(permissions)); } |
这里面先 Observable.just(null) 来生成一个空的 Observable 对象。然后通过 compose 操作符来进行转换 将某个参数转换为 Observable
然后我们看 ensure(String…) 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@SuppressWarnings("WeakerAccess") public Observable.Transformer<Object, Boolean> ensure(final String... permissions) { return new Observable.Transformer<Object, Boolean>() { @Override public Observable<Boolean> call(Observable<Object> o) { return request(o, permissions) // Transform Observable<Permission> to Observable<Boolean> .buffer(permissions.length) .flatMap(new Func1<List<Permission>, Observable<Boolean>>() { @Override public Observable<Boolean> call(List<Permission> permissions) { if (permissions.isEmpty()) { // Occurs during orientation change, when the subject receives onComplete. // In that case we don't want to propagate that empty list to the // subscriber, only the onComplete. return Observable.empty(); } // Return true if all permissions are granted. for (Permission p : permissions) { if (!p.granted) { return Observable.just(false); } } return Observable.just(true); } }); } }; } |
可以看到 ensure 方法较长,我们一步步分析。首先是定义一个 Transformer
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Observable<Permission> request(final Observable<?> trigger, final String... permissions) { if (permissions == null || permissions.length == 0) { throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission"); } return oneOf(trigger, pending(permissions)) .flatMap(new Func1<Object, Observable<Permission>>() { @Override public Observable<Permission> call(Object o) { return requestImplementation(permissions); } }); } |
这个方法通过 flatMap 操作符将 Object 转换成一个 Observable> ,最后通过 flatMap 方法对List
有了上面的基础我们来看后面的代码就方便了许多。首先是 requesEach(String …) 方法
1 2 3 4 |
public Observable<Permission> requestEach(final String... permissions) { return Observable.just(null).compose(ensureEach(permissions)); } |
调用了 ensureEach(String …) 方法。
1 2 3 4 5 6 7 8 9 |
public Observable.Transformer<Object, Permission> ensureEach(final String... permissions) { return new Observable.Transformer<Object, Permission>() { @Override public Observable<Permission> call(Observable<Object> o) { return request(o, permissions); } }; } |
完全一样的思路,不过是对每个 permissions 拆来分别进行Observable封装操作,并且其返回值封装为了 Observable
Rxpermissions 的核心大致就是这些内容。RxPermissionsFragment 封装了一个 Fragment 用于申请权限,没有什么复杂的内容。Permission 封装了权限的相关内容包括是否允许再次请求和是否同意,同样只是一个简单的Bean,不再分析。
综上 Rxpermissions 是个可靠的运行时权限库,对于日常开发帮助很大。
想获得去掉 5 元限制的证券账户吗?

如果您想去掉最低交易佣金 5 元限制,使用微信扫描左边小程序二维码,访问微信小程序「优财助手」,点击底部菜单「福利」,阅读文章「通过优财开证券账户无最低交易佣金 5 元限制」,按照文章步骤操作即可获得免 5 元证券账户,股票基金交易手续费率万 2.5。
请注意,一定要按照文章描述严格操作,如错误开户是无法获得免 5 元证券账户的。