Android 权限管理
大部分介绍都是开发文档上面的,下面是链接
https://developer.android.com/training/permissions/requesting?hl=zh-cn
我也是一个Android新手,今天写程序的时候感觉关于Activity中的权限管理和事情是一件很麻烦且重复的事情,然后自己就抽离出一个权限管理的工具,
我采用的是继承的方式解决的,虽然这样会有一些不妥的地方,但是感觉整体上自己使用还是满足的,当然管理权限管理这方面网上有很多的框架,但是我
还有学过,有时间在研究吧,我就直接短时间的抽离出来,方便自己在现阶段的程序代码编写。没有解决版本兼容性问题。
我们还是看开发文档上的说明吧,
检查权限
如果应用需要一项危险权限,那么每次执行需要该权限的操作时,您都必须检查自己是否具有该权限。
从 Android 6.0(API 级别 23)开始,用户可随时从任何应用撤消权限,即使应用以较低的 API 级别为目标平台也是如此。因此,即使应用昨天使用了相机,也不能认为它今天仍具有该权限。
要检查您是否具有某项权限,请调用 ContextCompat.checkSelfPermission() 方法。例如,以下代码段展示了如何检查 Activity 是否具有向日历写入数据的权限:
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
!= PackageManager.PERMISSION_GRANTED) {
// 当前权限 android.permission.WRITE_CALENDAR没有许可。
}
如果应用具有此权限,该方法将返回 PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具备此权限,该方法将返回 PERMISSION_DENIED,且应用必须明确要求用户授予权限。
请求权限
当您的应用从 checkSelfPermission() 收到 PERMISSION_DENIED 时,您需要提示用户授予该权限。Android 为您提供了几种可用来请求权限的方法(如 requestPermissions()),如下面的代码段所示。调用这些方法时,会显示一个无法自定义的标准 Android 对话框。
如何向用户显示此内容取决于设备的 Android 版本以及应用的目标版本,如权限概览中所述。
解释为什么应用需要权限
在某些情况下,您需要帮助用户理解为什么您的应用需要某项权限。例如,如果用户启动一款摄影应用,用户或许不会对该应用请求使用相机的权限感到惊讶,但用户可能不理解为什么该应用想要访问用户的位置或联系人。在您的应用请求权限之前,不妨考虑向用户提供解释。请注意,您肯定不希望您的解释让用户不胜其烦;如果您提供太多解释,用户可能会觉得这款应用很麻烦,因而将其移除。
您可以使用的一种方法是只在用户之前拒绝过该权限请求的情况下才提供解释。Android 提供了一种实用程序方法,即 shouldShowRequestPermissionRationale()。如果用户之前拒绝了该请求,该方法将返回 true;如果用户之前拒绝了某项权限并且选中了权限请求对话框中的不再询问选项,或者如果设备政策禁止该权限,该方法将返回 false。
如果用户不断尝试使用需要某项权限的功能,但一直拒绝权限请求,这或许意味着,用户不理解为什么应用需要该权限才能提供这项功能。在这种情况下,显示解释或许是不错的做法。
应用权限最佳做法中提供了更多建议,指导您如何在请求权限时创造良好的用户体验。
在必要时请求成为默认处理程序
有些应用依赖于访问与通话记录和短信有关的敏感用户信息。如果您想请求特定于通话记录和短信的权限,并将应用发布到 Play 商店,则必须在请求这些运行时权限之前提示用户将应用设置为“默认处理程序”以获得核心系统功能。
如需详细了解默认处理程序(包括有关如何向用户显示默认处理程序提示的指南),请参阅有关仅在默认处理程序中使用的权限的指南。
请求您需要的权限
如果您的应用还不具备它需要的权限,那么它必须调用一个 requestPermissions() 方法来请求相应权限。您的应用将传递它想要的权限,以及您指定用于识别此权限请求的整数请求代码。此方法异步运行。它会立即返回结果,并且在用户响应提示后,系统会利用相应结果来调用应用的回调方法,在调用过程中传递的请求代码与应用传递给 requestPermissions() 的请求代码相同。
以下代码将检查应用是否具有读取用户联系人的权限。如果没有该权限,它会检查是否应显示需要该权限的解释,如果不需要解释,它会请求该权限:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
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(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.
}
} else {
// Permission has already been granted
}
系统显示的提示说明了您的应用需要访问的权限组,而不是具体权限。
注意:当您的应用调用 requestPermissions() 时,系统会向用户显示一个标准对话框。您的应用无法配置或更改该对话框。如果您需要向用户提供任何信息或解释,您应该在调用 requestPermissions() 之前这样做,如解释为什么应用需要权限中所述。
处理权限请求响应
当用户响应您应用的权限请求时,系统会调用您应用的 onRequestPermissionsResult() 方法,在调用过程中向其传递用户响应。您的应用必须替换该方法,以查明是否已向其授予相应权限。在回调过程中传递的请求代码与传递给 requestPermissions() 的请求代码相同。例如,如果应用请求 READ_CONTACTS 访问权限,则它可能采用以下回调方法:
@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.
}
}
系统显示的对话框说明了您的应用需要访问的权限组,它不会列出具体权限。例如,如果请求 READ_CONTACTS 权限,系统对话框只会说明您的应用需要访问设备的联系人。用户只需要针对每个权限组授予一次权限。如果您的应用请求该组中的其他任何权限(已在您的应用清单中列出),系统会自动授予这些权限。当您请求权限时,系统会调用您的 onRequestPermissionsResult() 回调方法并传递 PERMISSION_GRANTED,就像用户通过系统对话框明确同意了您的请求时的处理方式一样。
注意:您的应用仍需要明确请求它需要的每项权限,即使用户已授予同一组中的其他权限。此外,权限分组在将来的 Android 版本中可能会发生变化。您的代码不应依赖于特定权限属于或不属于同一组的假设。
权限的分类
大家可以参考这个文章
https://www.jianshu.com/p/ccea3c2f9cfa
下面是代码部分
package ***;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
/**
* @author shaoyayu
* 权限管理工具,
* 负责权限的申请和管理
*/
public abstract class AuthorityManagementActivity extends AppCompatActivity {
private static final String TAG = "AuthorityManagementActivity";
/**
* 所有权限
*/
protected List<String> authority = new ArrayList<>();
/**
* 已经授权的权限
*/
protected List<String> grantedPermissions = new ArrayList<>();
/**
* 没有授权的权限
*/
protected List<String> noGrantedPermissions = new ArrayList<>();
/**
* 申请的权限被用户用户不提醒的拒绝,需要重新申请
*/
protected List<String> deniedPermission = new ArrayList<>();
protected static final int CODE_AUTHORITY = 0x00010;
/**
* 初始化布局
* @param savedInstanceState
*/
@Override
protected final void onCreate(@Nullable Bundle savedInstanceState) {
//提供初始化的布局
super.onCreate(savedInstanceState);
initActivity(savedInstanceState);
}
/**
* 子类在这个地方初始化activity
* @param savedInstanceState
*/
protected abstract void initActivity(Bundle savedInstanceState);
/**
* 初始权限,发现没有申请的权限直接申请
* @param authority
*/
protected void setAuthority(String authority[]){
for (int i = 0; i < authority.length; i++) {
this.authority.add(authority[i]);
}
/**
* 初始换权限
*/
initAuthority();
};
/**
* 分类权限
*/
private void initAuthority(){
for (int i = 0; i < authority.size(); i++) {
if (ActivityCompat.checkSelfPermission(this, this.authority.get(i)) == PackageManager.PERMISSION_GRANTED) {
//提出授权
this.grantedPermissions.add(authority.get(i));
}else {
//用户尚未授权
this.noGrantedPermissions.add(authority.get(i));
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
authority.get(i))) {
/**
* 权限已经在上一次申请的时候被永久的驳回
* 解释一下权限需求的重要性,打开应用详情去设置权限给予
* 我采用的是对话框的形式显示和解释给用户
*/
deniedPermission.add(authority.get(i));
} else {
//没有被驳回的权限,在这里我会采用统一的方式去申请
}
}
}
requestForAccess(noGrantedPermissions);
}
/**
* 初始化权限,但不申请
* @param authority
*/
@SuppressLint("LongLogTag")
protected void initAuthority(String[] authority){
for (int i = 0; i < authority.length; i++) {
this.authority.add(authority[i]);
Log.i(TAG,"初始化的权限有:"+authority[i]);
}
for (int i = 0; i < this.authority.size(); i++) {
if (ActivityCompat.checkSelfPermission(this, this.authority.get(i)) == PackageManager.PERMISSION_GRANTED) {
//提出授权
this.grantedPermissions.add(this.authority.get(i));
Log.i(TAG,"已经授权的权限:"+this.authority.get(i));
}else {
//用户尚未授权
this.noGrantedPermissions.add(this.authority.get(i));
Log.i(TAG,"尚未授权的权限有:"+this.authority.get(i));
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
this.authority.get(i))) {
/**
* 权限已经在上一次申请的时候被永久的驳回
* 解释一下权限需求的重要性,打开应用详情去设置权限给予
* 我采用的是对话框的形式显示和解释给用户
*/
deniedPermission.add(this.authority.get(i));
Log.i(TAG,"永久驳回的权限有:"+this.authority.get(i));
} else {
//没有被驳回的权限,在这里我会采用统一的方式去申请
}
}
}
}
/**
* 取出已经授权得权限
* @return
*/
protected List<String> getGrantedPermissions(){
return this.grantedPermissions;
}
protected List<String> getNoGrantedPermissions(){
return this.noGrantedPermissions;
}
public List<String> getDeniedPermission() {
return deniedPermission;
}
/**
* 申请权限
*/
protected void requestForAccess(List<String> atu){
String[] aut = new String[atu.size()];
for (int i = 0; i < atu.size(); i++) {
aut[i] = atu.get(i);
}
if (aut.length!=0){
//没有权限,向用户请求权限
ActivityCompat.requestPermissions(this, aut,
AuthorityManagementActivity.CODE_AUTHORITY);
}
}
/**
* 权限回调
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
//从新定义申请的权限和未申请的权限
this.grantedPermissions.clear();
this.noGrantedPermissions.clear();
this.deniedPermission.clear();
if (requestCode==AuthorityManagementActivity.CODE_AUTHORITY){
for (int i = 0; i < permissions.length; i++) {
//从新定义授权的情况
if (grantResults[i]==PackageManager.PERMISSION_GRANTED){
//同意授权
grantedPermissions.add(permissions[i]);
}else {
noGrantedPermissions.add(permissions[i]);
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
permissions[i])) {
/**
* 权限已经在上一次申请的时候被永久的驳回
* 解释一下权限需求的重要性,打开应用详情去设置权限给予
* 我采用的是对话框的形式显示和解释给用户
*/
deniedPermission.add(permissions[i]);
} else {
//没有被驳回的权限,在这里我会采用统一的方式去申请
}
}
}
}
authorizationProcessing();
}
/**
* 子类需要在这个地方写,授权后的回调
*/
abstract void authorizationProcessing();
/**
* 打开应用详情设置授权,
* 然用户在这里授权
* 记住当前的额报名是需要可以取值为当前应用的包名
* 否则会出错
*/
protected void openAppSettings(){
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
}
startActivity(localIntent);
}
}
大家有什么好的建议或者意见的话,留言我会去学习的,谢谢。。。。