前言
Android6.0之后开始对权限进行控制。
如果你把targetSdkVersion设置大于等于23时,需要运行时获取权限。
设置targetSdkVersion设置小于23,系统默认开启所有权限。
这个也是Android给出的一个的兼容方案。
鉴于现在市面上大部分手机都是4.x,5.x,6.x的手机占有量还是少的。
大部分的开发者做法:将targetSdkVersion设置为22,默认全部开启权限
我以targetSdkVersion设置为23举例
代码如下:
// REQUEST_CODE
private static final int REQUEST_CODE = 1;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 判断是否开启权限,没有则去申请权限
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
//申请WRITE_EXTERNAL_STORAGE权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE);
}
}
// 权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.d(TAG,">>>>>granted");
}else{
Log.d(TAG,">>>>>deny");
}
}
}
通过上面可以知道,在启动的时候需要去检查权限,申请权限,处理返回结果
上面用的是v4包的兼容方式调用,为了兼容Android6.0以下的手机
不是所有权限都要首页进行申请,有些可以到指定页面在申请,如拍照权限
关于使用到的API
1.ActivityCompat.checkSelfPermission(权限名)
检查是否有权限
2.ActivityCompat.shouldShowRequestPermissionRationale(权限名)
返回true表示在询问用户权限的对话框 用户未选择 不要再询问,反之则已选择不要再询问。
3.ActivityCompat.requestPermissions
请求授权
4.onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
回调获取授权结果,判断是否授权
回到本文的重心,RxPermissions
RxPermission介绍
RxPermissions是基于Rxjava实现的Android 6.0中处理运行时权限检测的框架。
吐槽:
现在RxJava很火,有很多它的实现框架,RxAndroid RxBus RxPermissions Rxbinding RxLifeCircle等
说白了,就是用RxJava的方式实现上述方法的使用,将方法用的更简洁。如果你觉得理解RxPermissions麻烦的话,我建议还是按照Android的API方式调用会更好
RxPermission的git地址:https://github.com/tbruyelle/RxPermissions
RxPermissions的使用姿势
发现网上说的RxPermissions的介绍都是关于老版本的介绍。
repositories {
jcenter() // If not already there
}
dependencies {
compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0'
}
// Must be done during an initialization phase like onCreate
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(new Subscriber<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Boolean granted) {
if (granted) {
// All requested permissions are granted
} else {
// At least one permission is denied
}
}
});
这个分支是fix46,大家可以去看下。
作为一个程序员,当然要以最新的为准了。
最新的使用姿势
repositories {
jcenter() // If not already there
}
dependencies {
compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4'
}
RxPermissions rxPermissions = new RxPermissions(this);
// Must be done during an initialization phase like onCreate
rxPermissions
.request(Manifest.permission.CAMERA)
.subscribe(new Subscriber<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Boolean granted) {
if (granted) {
// All requested permissions are granted
} else {
// At least one permission is denied
}
}
});
// 多个权限申请
rxPermissions
.request(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(new Subscriber<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Boolean granted) {
if (granted) {
// All requested permissions are granted
} else {
// At least one permission is denied
}
}
});
具体可以看github介绍
新版本主要是将RxPermissions.getInstance(this) -> new RxPermissions(this),现在不再使用单例
至于原因:我猜应该是单例持有当前的Activity,容易造成内存泄漏吧
姿势讲完了,看代码吧。
.
├── AndroidManifest.xml
└── java
└── com
└── tbruyelle
└── rxpermissions
├── Permission.java。--封装了的权限的基类,包含权限名称,是否授权等
├── RxPermissions.java --核心处理类,进行rxjava转换
└── RxPermissionsFragment.java --实际权限请求的Fragment
因为代码较少,我直接就贴代码了。添加了注释,大家就就这样将就看吧
- Permission.java
package com.tbruyelle.rxpermissions;
/**
* 权限的基类
* 封装了权限名称 是否授权 受否显示请求授权的对话框
*
* ActivityCompat.checkSelfPermission --> granted
* ActivityCompat.shouldShowRequestPermissionRationale -> shouldShowRequestPermissionRationale
*/
public class Permission {
public final String name; // 权限名称
public final boolean granted; // 是否授权
public final boolean shouldShowRequestPermissionRationale; // 受否显示请求授权的对话框
public Permission(String name, boolean granted) {
this(name, granted, false);
}
public Permission(String name, boolean granted, boolean shouldShowRequestPermissionRationale) {
this.name = name;
this.granted = granted;
this.shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale;
}
@Override
@SuppressWarnings("SimplifiableIfStatement")
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Permission that = (Permission) o;
if (granted != that.granted) return false;
if (shouldShowRequestPermissionRationale != that.shouldShowRequestPermissionRationale)
return false;
return name.equals(that.name);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (granted ? 1 : 0);
result = 31 * result + (shouldShowRequestPermissionRationale ? 1 : 0);
return result;
}
@Override
public String toString() {
return "Permission{" +
"name='" + name + '\'' +
", granted=" + granted +
", shouldShowRequestPermissionRationale=" + shouldShowRequestPermissionRationale +
'}';
}
}
- RxPermissionsFragment.java
package com.tbruyelle.rxpermissions;
import android.annotation.TargetApi;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
import rx.subjects.PublishSubject;
/**
* RxPermissionsFragment用于权限的相关操作
* 如调用requestPermission,onRequestPermissionsResult回调等
*
* 此Fragment直接调用Android6.0的fragment,兼容处理在RxPermission中实现
*/
public class RxPermissionsFragment extends Fragment {
/** requestPermission时用的REQUEST_CODE**/
private static final int PERMISSIONS_REQUEST_CODE = 42;
// Contains all the current permission requests.
// Once granted or denied, they are removed from it.
// PublishSubject 是最直接主要的Subject实现,
// 当一个事件值被发送给PublishSubject时,它会将这个事件值发送给订阅它的每个订阅者:
private Map<String, PublishSubject<Permission>> mSubjects = new HashMap<>();
/** 日志开关 **/
private boolean mLogging;
public RxPermissionsFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 在 Activity 重绘时,我们的 Fragment 不会被重复绘制,也就是它会被“保留”。
// 为了验证其作用,我们发现在设置为 true 状态时,旋转屏幕,Fragment 依然是之前的 Fragment。
setRetainInstance(true);
}
/**
* 发起请求Permissions
* @param permissions
*/
@TargetApi(Build.VERSION_CODES.M)
void requestPermissions(@NonNull String[] permissions) {
requestPermissions(permissions, PERMISSIONS_REQUEST_CODE);
}
/**
* 请求Permissions的回调
* @param requestCode
* @param permissions
* @param grantResults
*/
@TargetApi(Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode != PERMISSIONS_REQUEST_CODE) return;
boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
for (int i = 0; i < permissions.length; i++) {
shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);
}
onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);
}
void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {
for (int i = 0, size = permissions.length; i < size; i++) {
log("onRequestPermissionsResult " + permissions[i]);
// Find the corresponding subject
PublishSubject<Permission> subject = mSubjects.get(permissions[i]);
if (subject == null) {
// No subject found
Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");
return;
}
mSubjects.remove(permissions[i]);
boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));
subject.onCompleted();
}
}
/**
* 是否授权
* @param permission
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
boolean isGranted(String permission) {
return getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
/**
* 判断是不是在包中申明
* @param permission
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
boolean isRevoked(String permission) {
return getActivity().getPackageManager().isPermissionRevokedByPolicy(permission, getActivity().getPackageName());
}
public void setLogging(boolean logging) {
mLogging = logging;
}
public PublishSubject<Permission> getSubjectByPermission(@NonNull String permission) {
return mSubjects.get(permission);
}
public boolean containsByPermission(@NonNull String permission) {
return mSubjects.containsKey(permission);
}
public PublishSubject<Permission> setSubjectForPermission(@NonNull String permission, @NonNull PublishSubject<Permission> subject) {
return mSubjects.put(permission, subject);
}
void log(String message) {
if (mLogging) {
Log.d(RxPermissions.TAG, message);
}
}
}
- RxPermissions.java
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.tbruyelle.rxpermissions;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import rx.Observable;
import rx.functions.Func1;
import rx.subjects.PublishSubject;
/**
* RxPermissions处理类
*/
public class RxPermissions {
static final String TAG = "RxPermissions";
/**
* 为什么要使用RxPermissionsFragment???
* 通过这个fragment进行权限的操作
*/
RxPermissionsFragment mRxPermissionsFragment;
public RxPermissions(@NonNull Activity activity) {
mRxPermissionsFragment = getRxPermissionsFragment(activity);
}
/**
* 生成RxPermissionsFragment,用来获取Fragment对象
* @param activity
* @return
*/
private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
// 在当前Activity添加一个Fragment,然后通过这个Fragment进行权限处理
RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
boolean isNewInstance = rxPermissionsFragment == null;
if (isNewInstance) {
rxPermissionsFragment = new RxPermissionsFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(rxPermissionsFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return rxPermissionsFragment;
}
private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {
return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
/**
* 设置打印日志
* @param logging
*/
public void setLogging(boolean logging) {
mRxPermissionsFragment.setLogging(logging);
}
/**
* Map emitted items from the source observable into {@code true} if permissions in parameters
* are granted, or {@code false} if not.
* <p>
* If one or several permissions have never been requested, invoke the related framework method
* to ask the user if he allows the permissions.
*/
@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);
}
});
}
};
}
/**
* Map emitted items from the source observable into {@link Permission} objects for each
* permission in parameters.
* <p>
* If one or several permissions have never been requested, invoke the related framework method
* to ask the user if he allows the permissions.
*/
@SuppressWarnings("WeakerAccess")
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);
}
};
}
/**
* Request permissions immediately, <b>must be invoked during initialization phase
* of your application</b>.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public Observable<Boolean> request(final String... permissions) {
return Observable.just(null).compose(ensure(permissions));
}
/**
* Request permissions immediately, <b>must be invoked during initialization phase
* of your application</b>.
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public Observable<Permission> requestEach(final String... permissions) {
return Observable.just(null).compose(ensureEach(permissions));
}
private 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);
}
});
}
private Observable<?> pending(final String... permissions) {
for (String p : permissions) {
if (!mRxPermissionsFragment.containsByPermission(p)) {
return Observable.empty();
}
}
return Observable.just(null);
}
private Observable<?> oneOf(Observable<?> trigger, Observable<?> pending) {
if (trigger == null) {
return Observable.just(null);
}
return Observable.merge(trigger, pending);
}
@TargetApi(Build.VERSION_CODES.M)
private Observable<Permission> requestImplementation(final String... permissions) {
List<Observable<Permission>> list = new ArrayList<>(permissions.length);
List<String> unrequestedPermissions = new ArrayList<>();
// In case of multiple permissions, we create an Observable for each of them.
// At the end, the observables are combined to have a unique response.
for (String permission : permissions) {
mRxPermissionsFragment.log("Requesting permission " + permission);
if (isGranted(permission)) {
// Already granted, or not Android M
// Return a granted Permission object.
list.add(Observable.just(new Permission(permission, true, false)));
continue;
}
if (isRevoked(permission)) {
// Revoked by a policy, return a denied Permission object.
list.add(Observable.just(new Permission(permission, false, false)));
continue;
}
PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);
// Create a new subject if not exists
if (subject == null) {
unrequestedPermissions.add(permission);
subject = PublishSubject.create();
mRxPermissionsFragment.setSubjectForPermission(permission, subject);
}
list.add(subject);
}
if (!unrequestedPermissions.isEmpty()) {
String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
requestPermissionsFromFragment(unrequestedPermissionsArray);
}
return Observable.concat(Observable.from(list));
}
/**
* Invokes Activity.shouldShowRequestPermissionRationale and wraps
* the returned value in an observable.
* <p>
* In case of multiple permissions, only emits true if
* Activity.shouldShowRequestPermissionRationale returned true for
* all revoked permissions.
* <p>
* You shouldn't call this method if all permissions have been granted.
* <p>
* For SDK < 23, the observable will always emit false.
*/
@SuppressWarnings("WeakerAccess")
public Observable<Boolean> shouldShowRequestPermissionRationale(final Activity activity, final String... permissions) {
if (!isMarshmallow()) {
return Observable.just(false);
}
return Observable.just(shouldShowRequestPermissionRationaleImplementation(activity, permissions));
}
@TargetApi(Build.VERSION_CODES.M)
private boolean shouldShowRequestPermissionRationaleImplementation(final Activity activity, final String... permissions) {
for (String p : permissions) {
if (!isGranted(p) && !activity.shouldShowRequestPermissionRationale(p)) {
return false;
}
}
return true;
}
@TargetApi(Build.VERSION_CODES.M)
void requestPermissionsFromFragment(String[] permissions) {
mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
mRxPermissionsFragment.requestPermissions(permissions);
}
/**
*
* 是否授权兼容处理
* Android6.0以下,都为true
* @param permission
* @return
*/
@SuppressWarnings("WeakerAccess")
public boolean isGranted(String permission) {
return !isMarshmallow() || mRxPermissionsFragment.isGranted(permission);
}
/**
* 权限是否在包中
* 兼容处理
* Android6.0以下都为false
* @param permission
* @return
*/
@SuppressWarnings("WeakerAccess")
public boolean isRevoked(String permission) {
return isMarshmallow() && mRxPermissionsFragment.isRevoked(permission);
}
/**
* 是否为Android6.0以上的包
* @return
*/
boolean isMarshmallow() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
void onRequestPermissionsResult(String permissions[], int[] grantResults) {
mRxPermissionsFragment.onRequestPermissionsResult(permissions, grantResults, new boolean[permissions.length]);
}
}
就像刚开始介绍的一样,只是用Rxjava方式封装了这个权限请求的过程,如果对Rxjava很熟悉,建议使用。
里面使用了很多RxJava的方法,这里不展开讲了。具体API看下方
不是很擅长的话,推荐看下这篇权限请求的封装 android 6.0 Permission权限兼容的封装
网友评论