今天看了一天郭神 的直播,边看边敲,最后总结了下知识点。算是比较详细
郭神直播地址——
http://edu.csdn.net/course/detail/3539
项目已经上传到Github了,方便大家观看视频,地址——
https://github.com/Foolwithgenius/PermissionDemo
API 23 之前都可以
从android 1.0开始 至 android 5.1 关于android 权限机制 没有更改过。
我们开发者只需要在AndroidManifest 中声明权限,就可以
从 API 23 开始,android 权限分为运行时权限(危险权限),和 非运行时权限(安全权限),
非运行时权限,只需按照之前一样生命权限即可
运行时权限,必须在AndroidManifest声明一下,然后在代码中去申请下。才能正常运行功能。
知识点 1:
运行时权限,在6.0手机中不去申请,并不一定会崩溃。 原因:如果在 gradle中 选定 TargetSDKversion 低于23 一下。
就可以运行。
知识点 2:
如果程序没有卸载,你更改了gradle中 TargetSDKversion 的指定23 或者高于23 版本,这时候运行时权限仍然不去申请,程序仍然不会崩溃,
因为,程序向上兼容,程序从23版本之下,更改版本23 或者之上,android默认通过你所申请的权限 。
课外: 访问SD 卡的权限被列入 危险权限。 有一种方法可以不去申请 也可以写入一些文件,
Android- cache 文件放在这个目录下,你是不用去申请写入权限,也可以写入, 但是有一个缺点就是手机有一些清理文件可以被清理掉。
File file = getExternalCacheDir();
file.getPath();//可以拿到 目录文件
如果不想被清理掉的话,可以写入同目录下的files文件下,这个地方的文件被认为是重要文件,一般不会清理这个文件夹内的文件
File file = getExternalFilesDir("aaa");
//如果写入空字符的话,被写入文件会在fils根目录下
//向我这样写入 aaa 是在files 文件中新建一个文件夹
file.getPath();//可以拿到 目录文件
申请一个权限的时候的写法:
//这里是申请单个权限。
//检测应用 每个权限是否 已经开通。如果不开通之后 所做的判断
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 就是没有开通状态,需要去申请权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
makecall();
}
这里需要重写下OnRequestPermissionsResult 方法 (用户选择之后回调)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// requestCode 申请权限所传递状态吗
// permissions 我认为 是所申请的权限
// grantResults 所申请权限认证状态 是一个数组
switch (requestCode) {
case 1:
// 首先判断返回状态数组长度是否为0, 如果不为0 ,在判断状态是否同意了
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//同意权限
makecall();
} else {
//不同意权限
}
break;
default:
break;
}
同时申请多个权限时候,比较好的写法
//申请多个权限的写法
ArrayList<String> permission = new ArrayList<>();// 创建一个集合
//判断 某个权限是否开通, 如果没有开通 就把这个权限添加进入集合
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
permission.add(Manifest.permission.CALL_PHONE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permission.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if (!permission.isEmpty()) {
//申请开通权限
ActivityCompat.requestPermissions(this, permission.toArray(new String[permission.size()]), 1);
} else {
//你所申请的权限都已经开启,可以运行你想要的操作了。
makecall();
}
当然回调也需要更改下:
switch (requestCode) {
case 1:
if (grantResults.length > 0) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
// 如果进来了,就说明 某个权限被关闭了。
return;
}
}
//如果没有被return,说明你所申请的权限都已经被同意,可以去做想做的事情
makecall();
}
break;
default:
break;
}
以上就是 申请 Android6.0权限 的时候的一般写法。但是有的时候,一个app需要在好多地方做一些写入的操作,不可能每次都要写这么多代码。
所以就要对这个工作做一些封装了、
权限对Activity的 耦合性是比较高的,因为每一个权限弹出是需要传入 Activity.this 并且重写的onRequestPermissionsResult.
讨论的三种封装
1、 因为申请权限 对 Activity的依赖比较高,那就开启一个透明的 Activity,这个activity不做其他操作,只是弹出 申请权限的弹框。
而这个activity 对用用户来说是完全不可见的。 对于我们开发者来说就开启一个activity 调用申请权限的方法。
缺点: 我就不能使用, 因为项目中访问网络的方法 我放在了 onRsume 中所以。。。。
2、RxPermissions 的写法。刚开的RxPermissions 的写法类似于第一种,但是之后更改过, 是把 申请权限,和 回调 监听放在 Fragment中,
然后把这个fragment 放在了你所传入的activity中,其实fragment中也提供了 申请权限的API。 (郭神 说这种写法很牛逼。我不是太懂)
一会亲自看下RxPermissions 的源码。
3、封装在BaseActivity
public class BaseActivity extends AppCompatActivity {
private static PermissionListener mListtener;
public static void requestRuntimePermissions(String[] permissions, PermissionListener listener) {
mListtener = listener;
Activity topActivity = ActivityCollector.getTopActivity();
if (topActivity == null) {
return;
} else {
ArrayList<String> permissionsList = new ArrayList<>();// 创建一个集合
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(topActivity, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permission);
}
}
if (!permissionsList.isEmpty()) {
//申请开通权限
ActivityCompat.requestPermissions(topActivity, permissionsList.toArray(new String[permissionsList.size()]), 1);
} else {
//你所申请的权限都已经开启,可以运行你想要的操作了。
mListtener.onGrande();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0) {
List<String> deniedPermissions = new ArrayList<>();
for (int i = 0; i < grantResults.length; i++) {
int grantResult = grantResults[i];
String permission = permissions[i];
if (grantResult != PackageManager.PERMISSION_GRANTED) {
deniedPermissions.add(permission);
}
}
if (!deniedPermissions.isEmpty()) {
mListtener.onGrande();
} else {
mListtener.onDenied(deniedPermissions);
}
//如果没有被return,说明你所申请的权限都已经被同意,可以去做想做的事情
}
break;
default:
break;
}
}
}
可以任何类中使用
public class SendActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void ButtonClick(View view) {
requestRuntimePermissions(new String[]{Manifest.permission.CALL_PHONE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, new PermissionListener() {
@Override
public void onGrande() {
//权限申请通过
}
@Override
public void onDenied(List<String> deniedPermissions) {
for (String deniedPermission :
deniedPermissions
) {
// deniedPermission 权限没有申请通过
}
}
});
}
}
ActivityCollector这是一个activity管理类。郭神的第一行代码第二章
public class ActivityCollector {
public static ArrayList<Activity> activitys = new ArrayList<>();
public static void addActivity(Activity activity) {
activitys.add(activity);
}
;
public static void removeActivity(Activity activity) {
activitys.remove(activity);
}
public static Activity getTopActivity() {
if (activitys.isEmpty()) {
return null;
} else {
return activitys.get(activitys.size() - 1);
}
}
}