告别重复编码:3步封装通用Android权限请求工具类
你是否还在为Android应用中的权限请求逻辑重复编写大量代码?每次新增权限都要复制粘贴相似的检查、请求和回调处理代码?本文将带你基于EasyPermissions库,通过3个简单步骤打造一个可复用的权限请求工具类,彻底解决权限逻辑代码冗余问题。
为什么需要封装权限请求工具类
Android 6.0(API 23)引入动态权限(Runtime Permissions)机制后,开发者需要手动处理权限请求流程。未经封装的权限请求代码通常存在以下问题:
- 代码冗余:每个权限请求都需要重复编写检查、请求、回调处理代码
- 逻辑分散:权限相关代码散布在各个Activity或Fragment中,难以维护
- 不一致性:不同开发者实现的权限处理逻辑风格不一
- 异常情况:容易忽略"不再询问"等特殊场景处理
EasyPermissions库(README.md)作为Android权限处理的优秀封装,已经简化了基础权限逻辑,但在大型项目中仍需进一步封装以实现全应用统一管理。
准备工作:了解EasyPermissions核心API
在开始封装前,我们先了解EasyPermissions提供的核心能力:
| 核心方法 | 作用 | 所在文件 |
|---|---|---|
hasPermissions() | 检查是否已获得权限 | easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java |
requestPermissions() | 请求权限 | [easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java#L137-L154) |
onRequestPermissionsResult() | 处理权限请求结果 | [easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java#L171-L208) |
somePermissionPermanentlyDenied() | 检查是否被永久拒绝 | [easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java#L225-L229) |
AppSettingsDialog | 引导用户到应用设置界面 | easypermissions/src/main/java/pub/devrel/easypermissions/AppSettingsDialog.java |
EasyPermissions的示例代码展示了基础用法,如app/src/main/java/pub/devrel/easypermissions/sample/MainActivity.java中的相机权限请求:
@AfterPermissionGranted(RC_CAMERA_PERM)
public void cameraTask() {
if (hasCameraPermission()) {
// 有权限,执行操作
Toast.makeText(this, "TODO: Camera things", Toast.LENGTH_LONG).show();
} else {
// 无权限,请求权限
EasyPermissions.requestPermissions(
this,
getString(R.string.rationale_camera),
RC_CAMERA_PERM,
Manifest.permission.CAMERA);
}
}
第1步:创建权限工具类基础架构
首先创建一个PermissionHelper工具类,作为权限请求的统一入口。这个类将封装EasyPermissions的核心功能,并提供简洁的API。
public class PermissionHelper {
private static final String TAG = "PermissionHelper";
private WeakReference<Activity> mActivity;
private WeakReference<Fragment> mFragment;
private OnPermissionCallback mCallback;
// 权限回调接口
public interface OnPermissionCallback {
void onPermissionGranted(int requestCode, List<String> perms);
void onPermissionDenied(int requestCode, List<String> perms);
}
// 构造方法,支持Activity和Fragment
public static PermissionHelper with(Activity activity) {
return new PermissionHelper(activity);
}
public static PermissionHelper with(Fragment fragment) {
return new PermissionHelper(fragment);
}
private PermissionHelper(Activity activity) {
this.mActivity = new WeakReference<>(activity);
}
private PermissionHelper(Fragment fragment) {
this.mFragment = new WeakReference<>(fragment);
}
// 设置回调监听
public PermissionHelper setCallback(OnPermissionCallback callback) {
this.mCallback = callback;
return this;
}
// 检查是否有权限
public boolean hasPermissions(String... perms) {
Context context = getContext();
if (context == null) return false;
return EasyPermissions.hasPermissions(context, perms);
}
// 获取上下文
private Context getContext() {
if (mActivity != null && mActivity.get() != null) {
return mActivity.get();
}
if (mFragment != null && mFragment.get() != null) {
return mFragment.get().getActivity();
}
return null;
}
// 获取宿主(Activity或Fragment)
private Object getHost() {
if (mActivity != null && mActivity.get() != null) {
return mActivity.get();
}
if (mFragment != null && mFragment.get() != null) {
return mFragment.get();
}
return null;
}
}
这个基础架构实现了:
- 支持Activity和Fragment两种使用场景
- 使用弱引用(WeakReference)避免内存泄漏
- 定义权限回调接口
- 封装上下文获取逻辑
第2步:实现权限请求核心逻辑
接下来为工具类添加权限请求的核心实现,包括请求权限方法和结果处理方法:
// 继续在PermissionHelper类中添加
// 请求权限
public void requestPermissions(int requestCode, String rationale, String... perms) {
Object host = getHost();
if (host == null) return;
// 如果已经有权限,直接回调成功
if (hasPermissions(perms)) {
if (mCallback != null) {
mCallback.onPermissionGranted(requestCode, Arrays.asList(perms));
}
return;
}
// 构建权限请求
PermissionRequest request = new PermissionRequest.Builder(getHost(), requestCode, perms)
.setRationale(rationale)
.setPositiveButtonText(android.R.string.ok)
.setNegativeButtonText(android.R.string.cancel)
.build();
EasyPermissions.requestPermissions(request);
}
// 处理权限请求结果
public static void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults, Object host) {
// 将结果转发给EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, host);
}
同时,我们需要在工具类中实现EasyPermissions的回调接口,统一处理权限结果:
// 让PermissionHelper实现EasyPermissions的回调接口
public class PermissionHelper implements EasyPermissions.PermissionCallbacks {
// ... 之前的代码 ...
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
if (mCallback != null) {
mCallback.onPermissionGranted(requestCode, perms);
}
}
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
if (mCallback != null) {
mCallback.onPermissionDenied(requestCode, perms);
}
// 检查是否有永久拒绝的权限
if (somePermissionPermanentlyDenied(perms)) {
showAppSettingsDialog(requestCode);
}
}
// 检查是否有永久拒绝的权限
private boolean somePermissionPermanentlyDenied(List<String> perms) {
Object host = getHost();
if (host instanceof Activity) {
return EasyPermissions.somePermissionPermanentlyDenied((Activity) host, perms);
} else if (host instanceof Fragment) {
return EasyPermissions.somePermissionPermanentlyDenied((Fragment) host, perms);
}
return false;
}
// 显示应用设置对话框
private void showAppSettingsDialog(int requestCode) {
Context context = getContext();
if (context == null) return;
new AppSettingsDialog.Builder(context)
.setTitle("权限需要")
.setRationale("此功能需要以下权限,请在设置中开启:")
.setPositiveButton("去设置")
.setNegativeButton("取消")
.setRequestCode(requestCode)
.build()
.show();
}
}
第3步:使用与扩展工具类
完成工具类封装后,我们来看看如何在Activity或Fragment中使用它。
在Activity中使用
public class MainActivity extends AppCompatActivity {
private static final int RC_CAMERA_PERM = 123;
private PermissionHelper mPermissionHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化权限工具
mPermissionHelper = PermissionHelper.with(this)
.setCallback(new PermissionHelper.OnPermissionCallback() {
@Override
public void onPermissionGranted(int requestCode, List<String> perms) {
if (requestCode == RC_CAMERA_PERM) {
// 相机权限已授予,执行相机操作
openCamera();
}
}
@Override
public void onPermissionDenied(int requestCode, List<String> perms) {
Toast.makeText(MainActivity.this, "权限被拒绝", Toast.LENGTH_SHORT).show();
}
});
// 点击按钮请求相机权限
findViewById(R.id.btn_camera).setOnClickListener(v -> {
mPermissionHelper.requestPermissions(
RC_CAMERA_PERM,
"需要相机权限来拍摄照片",
Manifest.permission.CAMERA);
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 将权限结果转发给工具类处理
PermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
private void openCamera() {
// 相机操作实现
}
}
在Fragment中使用
public class MyFragment extends Fragment {
private static final int RC_STORAGE_PERM = 124;
private PermissionHelper mPermissionHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 初始化权限工具
mPermissionHelper = PermissionHelper.with(this)
.setCallback(new PermissionHelper.OnPermissionCallback() {
@Override
public void onPermissionGranted(int requestCode, List<String> perms) {
if (requestCode == RC_STORAGE_PERM) {
// 存储权限已授予,执行文件操作
saveFile();
}
}
@Override
public void onPermissionDenied(int requestCode, List<String> perms) {
Toast.makeText(getActivity(), "存储权限被拒绝", Toast.LENGTH_SHORT).show();
}
});
}
// 请求存储权限的方法
private void requestStoragePermission() {
mPermissionHelper.requestPermissions(
RC_STORAGE_PERM,
"需要存储权限来保存文件",
Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 将权限结果转发给工具类处理
PermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
private void saveFile() {
// 文件保存操作实现
}
}
高级扩展:添加权限组管理
对于需要多个权限的场景,可以进一步扩展工具类,添加权限组管理功能:
// 在PermissionHelper中添加
private static final Map<String, Integer> PERMISSION_GROUPS = new HashMap<>();
// 静态代码块初始化权限组
static {
PERMISSION_GROUPS.put("CAMERA_GROUP", 100);
PERMISSION_GROUPS.put("STORAGE_GROUP", 101);
PERMISSION_GROUPS.put("LOCATION_GROUP", 102);
}
// 获取权限组对应的请求码
public static int getGroupRequestCode(String groupName) {
return PERMISSION_GROUPS.getOrDefault(groupName, -1);
}
// 获取权限组包含的权限
public static String[] getGroupPermissions(String groupName) {
switch (groupName) {
case "CAMERA_GROUP":
return new String[]{Manifest.permission.CAMERA};
case "STORAGE_GROUP":
return new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
case "LOCATION_GROUP":
return new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
default:
return new String[0];
}
}
// 请求权限组
public void requestPermissionGroup(String groupName, String rationale) {
int requestCode = getGroupRequestCode(groupName);
if (requestCode == -1) return;
String[] perms = getGroupPermissions(groupName);
requestPermissions(requestCode, rationale, perms);
}
使用权限组:
// 请求存储权限组
mPermissionHelper.requestPermissionGroup(
"STORAGE_GROUP",
"需要存储权限来读取和保存文件");
完整工具类代码结构
最终封装的权限请求工具类完整路径建议放在:app/src/main/java/pub/devrel/easypermissions/util/PermissionHelper.java,其核心结构如下:
PermissionHelper
├── 构造方法
│ ├── with(Activity)
│ └── with(Fragment)
├── 配置方法
│ └── setCallback(OnPermissionCallback)
├── 核心方法
│ ├── hasPermissions(String...)
│ ├── requestPermissions(int, String, String...)
│ ├── requestPermissionGroup(String, String)
│ └── onRequestPermissionsResult(...)
└── 回调接口
├── onPermissionGranted(int, List<String>)
└── onPermissionDenied(int, List<String>)
总结与最佳实践
通过本文介绍的3个步骤,我们基于EasyPermissions库成功封装了一个通用的权限请求工具类。这个工具类具有以下优点:
- 使用简单:一行代码即可发起权限请求
- 支持多种场景:兼容Activity和Fragment
- 自动处理异常情况:包括"不再询问"场景
- 避免内存泄漏:使用弱引用管理上下文
- 易于扩展:支持权限组和自定义对话框
在实际项目中使用时,建议遵循以下最佳实践:
- 将所有权限请求集中通过工具类处理
- 为不同权限组定义常量,如
LOCATION_PERMISSIONS - 统一管理权限请求码,避免冲突
- 权限 rationale 信息使用资源文件定义,便于国际化
- 在
onPermissionDenied中向用户解释为何需要该权限
通过这种封装方式,不仅可以大幅减少重复代码,还能保证全应用权限处理逻辑的一致性和可维护性,是Android开发中权限管理的推荐实践。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



