博文地址

我的GitHub 我的博客 我的微信 我的邮箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

对运行时权限的一些理解

在旧的权限管理系统中,权限仅仅在App安装时询问用户一次,用户同意了这些权限App才能被安装,App一旦安装后就可以偷偷的做一些不为人知的事情了。

Android6.0开始,App可以直接安装,App在运行时由开发者决定在任一适当的时机一个一个询问用户是否授予权限,系统会弹出一个对话框让用户选择是否授权某个权限给App,这个Dialog是系统定义的,开发者不能定制,当App要求用户授予不恰当的权限的时候,用户可以拒绝(然而,很多APP可能会在请求权限失败后直接退出,所以你往往无法拒绝),用户即使授予或拒绝后也可以在设置页面对每个App的权限进行重新管理。

新的权限策略将权限分为两类,第一类是不涉及用户隐私的,只需要在Manifest中声明即可,比如网络、蓝牙、NFC等;第二类是涉及到用户隐私信息的,需要用户授权后才可使用,比如SD卡读写、联系人、短信读写等。

不需要运行时申请的权限

此类权限都是正常保护的权限,只需要在AndroidManifest.xml中简单声明这些权限即可,安装即授权,不需要每次使用时都检查权限,而且用户不能取消以上授权,除非用户卸载App。

需要运行时申请的权限

所有危险的Android系统权限属于权限组,如果APP运行在Android 6.0(API level 23)或者更高级别的设备中,并且targetSdkVersion>=23时,系统将会自动采用动态权限管理策略,如果你在涉及到特殊权限操作时没有申请权限而直接调用了相关代码,你的App可能就崩溃了。

综上所述,你需要注意:

  • 此类权限也必须在Manifest中申明,否则申请时不提示用户,直接回调开发者权限被拒绝。
  • 同一个权限组的任何一个权限被授权了,这个权限组的其他权限也自动被授权。例如一旦WRITE_CONTACTS被授权了,App也有READ_CONTACTS和GET_ACCOUNTS了。
  • 申请某一个权限的时候系统弹出的Dialog是对整个权限组的说明,而不是单个权限。例如我申请READ_EXTERNAL_STORAGE,系统会提示"允许xxx访问设备上的照片、媒体内容和文件吗?"。

其他情景:

1、targetSdkVersion小于等于22,但是设备系统版本大于等于6.0:

  • app使用旧的权限管理策略
  • 注册文件列出的权限将会在安装时要求用户授予权限
  • 用户可以在设置列表中编辑相关权限,这对app能否正常运行有很大影响

2、targetSdkVersion大于等于23,但是设备系统版本小于6.0:

  • app使用旧的权限管理策略
  • 注册文件列出的权限将会在安装时要求用户授予权限

运行时权限使用案例

public class MainActivity extends ListActivity {

    private static final int REQUEST_CODE = 20094;

    protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"完整的授权过程演示"};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
createFile();
} private void createFile() {
try {
//如果将targetSdkVersion为22或以下,可以成功创建文件;如果改为23或以上,则失败(会抛异常)
File file = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".txt");
Toast.makeText(this, "结果:" + file.createNewFile(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "异常了", Toast.LENGTH_SHORT).show();
}
} @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
requestPermissions();
} private void requestPermissions() {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
int permissionState = ContextCompat.checkSelfPermission(this, permissions[0]); //检查权限
if (permissionState != PackageManager.PERMISSION_GRANTED) { //没有权限,申请权限
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { //是否应该显示请求权限的说明
new AlertDialog.Builder(this)
.setTitle("通过弹一个自定义的对话框告诉用户,我们为什么需要这个权限")
.setMessage("请求SD卡权限,作用是给你保存妹子图片,点击【好嘞】会重新尝试请求用户授权")
.setPositiveButton("好嘞", (dialog, which) -> ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE))
.create()
.show();
} else {
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE); //请求用户授权
}
} else {
Toast.makeText(this, "有权限了", Toast.LENGTH_SHORT).show();
}
} @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0) {
switch (requestCode) {
case REQUEST_CODE: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "有权限了", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "你拒绝了权限,我们已经没法愉快的玩耍了!", Toast.LENGTH_SHORT).show();
}
}
}
}
}
}

开源库:PermissionsDispatcher

PermissionsDispatcher provides a simple annotation-based API to handle runtime permissions.

This library lifts the burden 解除了负担 that comes with writing a bunch of 一系列 check statements whether a permission has been granted or not from you, in order to keep your code clean and safe.

特性:

注解

@RuntimePermissions@NeedsPermission 是必须的,其余注解均为可选。

注意:被注解的方法不能是私有方法,因为这些方法会被自动生成的类调用。

Annotation Required Description
@RuntimePermissions Register an Activity or Fragment to handle permissions
@NeedsPermission Annotate a method which performs the action that requires one or more permissions 当用户给予权限时会执行该方法
@OnShowRationale optional Annotate a method which explains why the permissions are needed.
@OnPermissionDenied optional Annotate a method which is invoked if the user doesn't grant the permissions
@OnNeverAskAgain optional Annotate a method which is invoked if the user chose to have the device "never ask again" about a permission

OnShowRationale 方法的细节

It passes in a PermissionRequest object which can be used to continue or abort the current permission request upon user input. If you don't specify any argument for the method compiler will generate process{方法名}ProcessRequest and cancel{方法名}ProcessRequest. You can use those methods in place of PermissionRequest(ex: with DialogFragment)

使用案例

使用步骤

1、声明权限

<uses-permission android:name="android.permission.CAMERA" />

2、引入框架

//注意:要将targetSdkVersion设为23或以上,否则这个库是无效的
//NOTE: 4.x only supports Jetpack. If you still use appcompat 3.x is the way to go.
implementation 'com.github.hotchemi:permissionsdispatcher:3.3.1'
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.4.0'

3、添加注解

4、点击 菜单栏 → Build → Make Project 重新编译整个项目

编译完成后,会在 app\build\intermediates\classes\debug 目录下生成一个与被注解的Activity同一个包下的辅助类,名称为被注解的Activity名称+PermissionsDispatcher,例如MainActivityPermissionsDispatcher

测试代码

@RuntimePermissions
public class MainActivity extends ListActivity { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"开源库 PermissionsDispatcher"};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
} @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
MainActivityPermissionsDispatcher.performActionWithCheck(this);
} @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void performAction() {
Toast.makeText(this, "有权限了", Toast.LENGTH_SHORT).show();
} @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void doOnPermissionDenied() {
Toast.makeText(this, "你拒绝了权限,我们已经没法愉快的玩耍了!", Toast.LENGTH_SHORT).show();
} @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void doOnShowRationale(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setTitle("通过弹一个自定义的对话框告诉用户,我们为什么需要这个权限")
.setMessage("请求SD卡权限,作用是给你保存妹子图片,点击【好嘞】会重新尝试请求用户授权")
.setPositiveButton("我知道了,继续吧", (dialog, which) -> request.proceed())
.setNegativeButton("我不需要,不要再请求了", (dialog, which) -> request.cancel())
.create()
.show();
} @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void doOnNeverAskAgain() {
Toast.makeText(this, "你拒绝了权限,并勾选了不再提醒,已经没法愉快的玩耍了!", Toast.LENGTH_SHORT).show();
} @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
}

自动生成的类

//final 类型的工具类
final class MainActivityPermissionsDispatcher {
private static final int REQUEST_PERFORMACTION = 0; //请求码
private static final String[] PERMISSION_PERFORMACTION = new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}; //需要的权限 private MainActivityPermissionsDispatcher() {} //私有构造方法 //注解处理器会检索所有带 @NeedsPermission 的方法名,生成相应的名为【方法名+WithCheck】的工具方法
//所有带注解的方法都不能是私有方法,不然只能通过反射调用,而这个库是100%不使用反射的
static void performActionWithCheck(MainActivity target) {
if (PermissionUtils.hasSelfPermissions(target, PERMISSION_PERFORMACTION)) { //检查是否已经有权限
target.performAction(); //有权限时执行
} else {
if (PermissionUtils.shouldShowRequestPermissionRationale(target, PERMISSION_PERFORMACTION)) {
//判断是否需要显示提示,根据结果执行不同的回调
target.doOnShowRationale(new PerformActionPermissionRequest(target));
} else {
//不需要显示提示,所以开始通过系统API请求权限
ActivityCompat.requestPermissions(target, PERMISSION_PERFORMACTION, REQUEST_PERFORMACTION);
}
}
} static void onRequestPermissionsResult(MainActivity target, int requestCode, int[] grantResults) {
switch (requestCode) { //处理授权结果
case REQUEST_PERFORMACTION:
if (PermissionUtils.verifyPermissions(grantResults)) { //授予了权限
target.performAction();
} else { //拒绝了权限
if (!PermissionUtils.shouldShowRequestPermissionRationale(target, PERMISSION_PERFORMACTION)) {
target.doOnNeverAskAgain(); //拒绝了权限,并勾选了不再提醒
} else {
target.doOnPermissionDenied(); //用户拒绝了权限
}
}
break;
default:
break;
}
} private static final class PerformActionPermissionRequest implements PermissionRequest {
private final WeakReference<MainActivity> weakTarget; //弱引用,不影响GC private PerformActionPermissionRequest(MainActivity target) {
this.weakTarget = new WeakReference<MainActivity>(target);
} @Override
public void proceed() {
MainActivity target = weakTarget.get();
if (target == null) return;
//再次请求权限
ActivityCompat.requestPermissions(target, PERMISSION_PERFORMACTION, REQUEST_PERFORMACTION);
} @Override
public void cancel() {
MainActivity target = weakTarget.get();
if (target == null) return;
target.doOnPermissionDenied();//结束请求
}
}
}

官方文档:请求权限

官方文档:Request App Permissions

Every Android app runs in a limited-access sandbox. If an app needs to use resources or information outside of its own sandbox, the app has to request the appropriate 相应的 permission. You declare that your app needs a permission by listing the permission in the app manifest and then requesting that the user approve 批准 each permission at runtime (on Android 6.0 and higher).

This page describes how to use the Android Support Library to check for and request permissions. The Android framework provides similar methods as of Android 6.0 (API level 23), but using the support library makes it easier to provide compatibility with older versions of Android.

Add permissions to the manifest

On all versions of Android, to declare that your app needs a permission, put a <uses-permission> element in your app manifest, as a child of the top-level <manifest> element.

The system's behavior after you declare a permission depends on how sensitive 敏感程度 the permission is. Some permissions are considered "normal" so the system immediately grants them upon installation. Other permissions are considered "dangerous" so the user must explicitly 明确 grant your app access. For more information about the different kinds of permissions, see 保护级别.

Check for permissions

If your app needs a dangerous permission, you must check whether you have that permission every time you perform an operation that requires that permission. Beginning with Android 6.0 (API level 23), users can revoke 撤销 permissions from any app at any time, even if the app targets a lower API level. So even if the app used the camera yesterday, it can't assume 确认 it still has that permission today.

To check if you have a permission, call the ContextCompat.checkSelfPermission() method. For example, this snippet shows how to check if the activity has permission to write to the calendar:

if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
}

If the app has the permission, the method returns PERMISSION_GRANTED, and the app can proceed 继续 with the operation. If the app does not have the permission, the method returns PERMISSION_DENIED, and the app has to explicitly ask the user for permission.

Request permissions

When your app receives PERMISSION_DENIED from checkSelfPermission(), you need to prompt the user for that permission. Android provides several methods you can use to request a permission, such as requestPermissions(), as shown in the code snippet below. Calling these methods brings up 显示 a standard Android dialog, which you cannot customize.

How this is displayed to the user depends on the device Android version as well as the target version of your application, as described in the 权限概览.

Explain why the app needs permissions

In some circumstances, you want to help the user understand why your app needs a permission. For example, if a user launches a photography app, the user probably won't be surprised that the app asks for permission to use the camera, but the user might not understand why the app wants access to the user's location or contacts. Before your app requests a permission, you should consider providing an explanation to the user. Keep in mind that you don't want to overwhelm 不胜其烦 the user with explanations; if you provide too many explanations, the user might find the app frustrating 很麻烦 and remove it.

One approach 方法 you might use is to provide an explanation only if the user has already denied that permission request. Android provides a utility method, shouldShowRequestPermissionRationale(), that returns true if the user has previously 之前 denied the request, and returns false if a user has denied a permission and selected the Don't ask again option in the permission request dialog, or if a device policy 政策 prohibits 禁止 the permission.

If a user keeps trying to use functionality that requires a permission, but keeps denying the permission request, that probably means the user doesn't understand why the app needs the permission to provide that functionality. In a situation like that, it's probably a good idea to show an explanation 解释.

More advice about how to create a good user experience when asking for permission is provided in 应用权限最佳做法.

Request the permissions you need

If your app doesn't already have the permission it needs, the app must call one of the requestPermissions() methods to request the appropriate 相应 permissions. Your app passes the permissions it wants and an integer request code that you specify 指定 to identify 识别 this permission request. This method functions asynchronously 异步. It returns right away, and after the user responds to the prompt 响应提示, the system calls the app's callback method with the results, passing the same request code that the app passed to requestPermissions().

The following code checks if the app has permission to read the user's contacts. If it does not have permission it checks if it should show an explanation for needing the permission, and if no explanation is needed, it requests the permission:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
// Permission is not granted,Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
// Show an explanation 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; request the permission
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_CODE);
// The callback method gets the result of the request.
}
} else {
// Permission has already been granted
}

The prompt 提示 shown by the system describes the 权限组 your app needs access to, not the specific permission.

Note: When your app calls requestPermissions(), the system shows a standard dialog box to the user. Your app cannot configure or alter that dialog box. If you need to provide any information or explanation to the user, you should do that before you call requestPermissions(), as described in Explain why the app needs permissions.

Handle the permissions request response

When the user responds to your app's permission request, the system invokes 调用 your app's onRequestPermissionsResult() method, passing it the user response. Your app has to override that method to find out whether the permission was granted. The callback is passed the same request code you passed to requestPermissions(). For example, if an app requests READ_CONTACTS access it might have the following callback method:

@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;
}
// other 'case' lines to check for other permissions this app might request.
}
}

The dialog box shown by the system describes the permission group your app needs access to; it does not list the specific permission. For example, if you request the READ_CONTACTS permission, the system dialog box just says your app needs access to the device's contacts. The user only needs to grant permission once for each permission group.

If your app requests any other permissions in that group (that are listed in your app manifest), the system automatically grants them. When you request the permission, the system calls your onRequestPermissionsResult() callback method and passes PERMISSION_GRANTED, the same way it would if the user had explicitly 明确 granted your request through the system dialog box.

如果您的应用请求该组中的其他任何权限(已在您的应用清单中列出),系统会自动授予这些权限。当您请求权限时,系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_GRANTED,就像用户通过系统对话框明确同意了您的请求时的处理方式一样。

Note: Your app still needs to explicitly request every permission it needs, even if the user has already granted another permission in the same group. In addition 此外, the grouping of permissions into groups may change in future Android releases. Your code should not rely on the assumption 假设 that particular 特定 permissions are or are not in the same group.

For example, suppose you list both READ_CONTACTS and WRITE_CONTACTS in your app manifest. If you request READ_CONTACTS and the user grants the permission, and you then request WRITE_CONTACTS, the system immediately grants you that permission without interacting with 交互 the user.

If the user denies a permission request, your app should take appropriate action. For example, your app might show a dialog explaining why it could not perform the user's requested action that needs that permission.

When the system asks the user to grant a permission, the user has the option 选择 of telling the system not to ask for that permission again. In that case, any time an app uses requestPermissions() to ask for that permission again, the system immediately denies the request. The system calls your onRequestPermissionsResult() callback method and passes PERMISSION_DENIED, the same way it would if the user had explicitly rejected your request again. The method also returns false if a device policy prohibits the app from having that permission. This means that when you call requestPermissions(), you cannot assume that any direct interaction with the user has taken place 发生.

To provide the best user experience when asking for app permissions, also see 应用权限最佳做法.

Declare permissions by API level

To declare a permission only on devices that support runtime permissions, that is, devices running Android 6.0 (API level 23) or higher, include the uses-permission-sdk-23 tag instead of the uses-permission tag.

When using either of these tags, you can set the maxSdkVersion attribute to specify that, on devices running a higher version, a particular permission isn't needed.

所有危险权限(组)

权限组:Manifest.permission_group

权限:Manifest.permission

中文解释参考

使用以下 adb 命令可以查看所有需要授权的权限组:

adb shell pm list permissions -d -g

不同系统可能稍有差异,不过基本都一样,以下为一个案例:

Dangerous Permissions:

group:com.google.android.gms.permission.CAR_INFORMATION    汽车资料
permission:com.google.android.gms.permission.CAR_VENDOR_EXTENSION
permission:com.google.android.gms.permission.CAR_MILEAGE
permission:com.google.android.gms.permission.CAR_FUEL 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.ANSWER_PHONE_CALLS ---API26新增的
permission:android.permission.READ_PHONE_NUMBERS ---API26新增的
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.ACCEPT_HANDOVER ---API28新增的
permission:android.permission.USE_SIP
permission:com.android.voicemail.permission.ADD_VOICEMAIL group:android.permission-group.CALL_LOG【电话相关,API28中从PHONE权限组抽离出来的】
permission:android.permission.READ_CALL_LOG
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.PROCESS_OUTGOING_CALLS 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:com.google.android.gms.permission.CAR_SPEED
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 //省略了一小部分未分组的以及第三方应用(例如微博)定义的

2019-11-16

- Permission 运行时权限 总结 翻译 MD的更多相关文章

  1. Android M Permission 运行时权限 学习笔记

    Android M Permission 运行时权限 学习笔记 从Android 6.0开始, 用户需要在运行时请求权限, 本文对运行时权限的申请和处理进行介绍, 并讨论了使用运行时权限时新老版本的一 ...

  2. Android M新的运行时权限开发者需要知道的一切

    android M 的名字官方刚发布不久,最终正式版即将来临!android在不断发展,最近的更新 M 非常不同,一些主要的变化例如运行时权限将有颠覆性影响.惊讶的是android社区鲜有谈论这事儿, ...

  3. Android 运行时权限处理(from jianshu)

    https://www.jianshu.com/p/e1ab1a179fbb 翻译的国外一篇文章. android M 的名字官方刚发布不久,最终正式版即将来临! android在不断发展,最近的更新 ...

  4. Android数据存储之Android 6.0运行时权限下文件存储的思考

    前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以 ...

  5. Android权限管理之Android 6.0运行时权限及解决办法

    前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以 ...

  6. Android 6.0 运行时权限处理完全解析

    一.概述 随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化.对于6.0的几个主要的变化,查看查看官网的这篇文章http:// ...

  7. 谈谈Android 6.0运行时权限理解

    前言 谷歌在2015年8月份时候,发布了Android 6.0版本,代号叫做“棉花糖”(Marshmallow ),其中的很大的一部分变化,是在用户权限授权上,或许是感觉之前默认授权的不合理,现在6. ...

  8. Android 6.0的运行时权限

    原文  http://droidyue.com/blog/2016/01/17/understanding-marshmallow-runtime-permission/ 主题 安卓开发   Andr ...

  9. Android 运行时权限处理

    引言 Android 6.0 (API 23) 开始引入了运行时权限检查 (Permissions at Run Time),用户不需要在安装时同意授予应用权限,而是在应用运行时动态去申请所需要的权限 ...

随机推荐

  1. Nginx配置文件 nginx.conf 和default.conf 讲解

    nginx.conf /etc/nginx/nginx.conf ######Nginx配置文件nginx.conf中文详解##### #定义Nginx运行的用户和用户组 user www www; ...

  2. ubuntu18 安装坑点记录(华硕飞行堡垒)

    环境 电脑:华硕飞行堡垒FX53VD 显卡:集成显卡+NVIDIA显卡(linux版本很不友好) 镜像:ubuntu18.04.3 LTS 准备安装 * 制作启动盘(百度) * 设置U盘启动项:按F2 ...

  3. Nginx应用详解及配置

    一.Nginx简介 概述:Nginx是一款由俄罗斯开发的开源的高性能HTTP服务器和反向代理服务器,同时支持IMAP/POP3/SMTP代理服务,其性能优势着为显著,官网上称:单台nginx服务器可以 ...

  4. SpringBoot整合自定义FTP文件连接池

    说明:通过GenericObjectPool实现的FTP连接池,记录一下以供以后使用环境:JDK版本1.8框架 :springboot2.1文件服务器: Serv-U1.引入依赖 <!--ftp ...

  5. Discuz!开发之时间处理函数dgmdate()详解

    使用过Discuz!的朋友都会知道Discuz!的时间可以显示成多少秒前.多少分钟前.几个小时前.几天前等等,而不是单纯的显示标准时间,这样的时间显示方式就更显得人性化了!   那么Discuz!是如 ...

  6. mobx 学习笔记

    Mobx 笔记 Mobx 三板斧,observable.observer.action. observable: 通过 observable(state) 定义组件的状态,包装后的状态是一个可观察数据 ...

  7. Windbg Processes and Threads(进程和线程)窗口的使用

    在 WinDbg 中,进程和线程窗口中显示有关系统. 进程和线程正在调试的信息. 此窗口还可选择新的系统. 进程和线程处于活动状态. 如何打开进程和线程窗口 通过菜单View--->Proces ...

  8. [RN] React Native 图片保存到相册(支持 Android 和 ios)

    React Native 图片保存到相册(支持 Android 和 ios) 原理: IOS用 RN自带的 CameraRoll, Android 使用 不成功,需要 react-native-fs  ...

  9. [RN] Android 设备adb连接后unauthorized解决方法

    Android 设备adb连接后unauthorized解决方法 安卓设备usb或者adbwireless连接后输入adb device后都是未授权状态 相信很多同学都会遇到这种情况,除了一直重复开关 ...

  10. 【JZOJ6239】【20190629】智慧树

    题目 一颗\(n\)个节点的树,每个点有一个权值\(a_i\) 询问树上连通块权值之和对 \(m\) 取模为$ x $ 的方案数 答案对\(950009857\) 取模,满足\(m | 9500098 ...