移动开发认证鉴权【免费下载链接】easypermissionsSimplify Android M system permissions项目地址https://gitcode.com/gh_mirrors/ea/easypermissions点击查看免费下载本篇技术指南以开源仓库 easypermissions 的 README 为核心骨架系统讲解如何在 Android MAPI 23及更高版本上通过pub.devrel:easypermissions库大幅简化运行时权限的检查、请求、Rationale 弹窗与永久拒绝后引导去设置页等繁琐逻辑。读完本文你将掌握从依赖引入、结果转发、AfterPermissionGranted注解、PermissionCallbacks/RationaleCallbacks回调到AppSettingsDialog的完整实战方案并理解其底层PermissionHelper调用链与低版本兼容机制。一、为什么需要 EasyPermissionsAndroid M 运行时权限的痛点从 Android 6.0API 23代号 Marshmallow开始系统引入了运行时权限模型高危权限如相机、定位、通讯录、短信等不再只在安装时一次性授权而是需要在应用运行过程中随时动态请求并处理用户的允许 / 拒绝 / 不再询问三种结果。开发者必须自己维护一整套样板代码检查权限、判断是否需要展示 rationale解释为何需要该权限、发起请求、在onRequestPermissionsResult中分发结果、处理永久拒绝后的设置页引导……EasyPermissions 正是为解决这套重复逻辑而生的封装库wrapper library定位与源码注释一致Utility to request and check System permissions for apps targeting Android M (API 23)见 EasyPermissions.java。它的核心价值可以概括为用hasPermissions(...)一行完成任意数量权限的批量检查用requestPermissions(...)自动判断是否需要先展示 rationale 弹窗需要时自动弹出用AfterPermissionGranted注解 唯一 requestCode实现权限全部授予后自动继续执行原任务用somePermissionPermanentlyDenied(...)AppSettingsDialog一键把用户引导到系统设置页。补充说明README 指出如果应用使用 Kotlin 编写可关注基于本库封装的 easypermissions-ktx 扩展库提供 Kotlin 扩展函数本仓库核心仍为 Java 实现。二、安装与依赖配置在app/build.gradle的dependencies块中加入以下依赖即可这是 README 给出的官方配置dependencies { // For developers using AndroidX in their applications implementation pub.devrel:easypermissions:3.0.0 // For developers using the Android Support Library implementation pub.devrel:easypermissions:2.0.1 }需要注意两点按工程使用的 UI 体系选择版本如果应用已迁移到 AndroidX使用3.0.0如果仍在使用旧版 Android Support Library则使用2.0.1。两者 API 形态一致但底层分别依赖androidx.appcompat与android.support组件。运行前提本文描述的运行时权限 API 面向 targetSdk 为 Android MAPI 23及以上的应用库内部会对低于 23 的系统走免请求兼容路径详见下文低版本兼容一节。从仓库结构看项目由两个 Gradle 模块组成app/是可运行的示例工程easypermissions/是库模块本体核心源码位于 easypermissions/src/main/java/pub/devrel/easypermissions/。三、快速开始在 Activity / Fragment 中转发权限结果EasyPermissions 不接管系统回调而是要求你把自己的onRequestPermissionsResult结果转发给库。README 给出的最小接入方式如下public class MainActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // Forward results to EasyPermissions EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } }这段转发的含义是把系统返回的requestCode、permissions、grantResults三元组连同宿主对象this一起交给EasyPermissions.onRequestPermissionsResult(...)。从 EasyPermissions.java 的实现可以看到库会在此处完成三件事将permissions与grantResults一一对应拆分为granted已授予与denied被拒绝两个列表若宿主实现了PermissionCallbacks则触发onPermissionsGranted/onPermissionsDenied回调若本次请求全部授予成功granted非空且denied为空则通过反射执行所有带AfterPermissionGranted(requestCode)注解、且 requestCode 匹配的方法。receivers是可变参数Object... receivers意味着你可以同时传入 Activity 和它内部的 Fragment 等多个对象让它们各自收到对应回调。四、请求权限的完整姿势检查 → 请求 → 注解自动续跑README 给出的典型请求场景是某功能同时需要CAMERA和ACCESS_FINE_LOCATION两个权限。示例方法通常与AfterPermissionGranted注解配合形成一个自洽的检查—请求—续跑闭环AfterPermissionGranted(RC_CAMERA_AND_LOCATION) private void methodRequiresTwoPermission() { String[] perms {Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION}; if (EasyPermissions.hasPermissions(this, perms)) { // Already have permission, do the thing // ... } else { // Do not have permissions, request them now EasyPermissions.requestPermissions(this, getString(R.string.camera_and_location_rationale), RC_CAMERA_AND_LOCATION, perms); } }使用时有几个要点README 已明确说明此处逐条展开EasyPermissions#hasPermissions(...)检查应用当前是否已具备全部所需权限可变参数String... perms可一次传入任意多个权限。实现上通过ContextCompat.checkSelfPermission逐个比对PackageManager.PERMISSION_GRANTED见 EasyPermissions.java。对 SDK 23 的设备则直接返回true源码注释甚至标注了 DANGER ZONE!!! Changing this will break the library即该分支是整个库兼容性的根基。EasyPermissions#requestPermissions(...)真正发起系统权限请求若系统判断应当展示 rationale则先弹出解释文案。requestCode需要在本请求内唯一也是可变参数接收权限列表。AfterPermissionGranted注解可选但非常方便当某次请求的所有权限全部被授予时所有标注了对应 requestCode 的注解方法都会被自动执行因此务必保证 requestCode 唯一。被注解的方法必须满足两个硬性约束——返回值必须是void且不能有输入参数如果有需要在回调后保留的中间状态README 建议改用onSaveInstanceState保存以便在注解方法执行时恢复这既是为了简化权限到位后继续干活的常见流程也是反射method.invoke(object)的机制要求。当然你也可以放弃注解把继续执行的逻辑写在onPermissionsGranted回调里效果等价。在注解的实现层面AfterPermissionGranted是Retention(RUNTIME)、Target(METHOD)的运行时注解见 AfterPermissionGranted.java其唯一的int value()就是匹配用的 requestCode。库在 runAnnotatedMethods 中通过反射遍历宿主类及其父类链上所有带该注解的方法requestCode 不匹配则跳过方法带参数getParameterTypes().length 0会直接抛出RuntimeException提示非 void 方法 / 带参数方法无法执行匹配成功则setAccessible(true)后invoke。因此注解方法声明为private也完全可以示例工程的MainFragment.smsTask()就是 private 方法见 MainFragment.java。五、用 PermissionRequest 精细控制 rationale 弹窗如果不满足于默认弹窗README 提供了PermissionRequest的 Builder 方式可以对 rationale 弹窗进行更精细的定制EasyPermissions.requestPermissions( new PermissionRequest.Builder(this, RC_CAMERA_AND_LOCATION, perms) .setRationale(R.string.camera_and_location_rationale) .setPositiveButtonText(R.string.rationale_ask_ok) .setNegativeButtonText(R.string.rationale_ask_cancel) .setTheme(R.style.my_fancy_style) .build());PermissionRequest是一个不可变immutable的请求模型对象构造函数中会对权限数组做clone()防御性拷贝并实现equals/hashCode基于权限数组与 requestCode相关细节见 PermissionRequest.java。其 Builder 提供的方法与默认值如下依据 build() 方法 及源码注释整理Builder 方法作用未设置时的默认值setRationale(String / int resId)设置首次被拒后展示的解释文案库资源R.string.rationale_askThis app may not work correctly without the requested permissions.见 strings.xmlsetPositiveButtonText(String / int resId)设置弹窗确认按钮文字android.R.string.ok确定/OKsetNegativeButtonText(String / int resId)设置弹窗取消按钮文字android.R.string.cancel取消setTheme(int styleRes)设置弹窗主题样式使用默认主题Builder 初始mTheme -1仅当 0 时才应用自定义主题构造参数requestCode请求标识必须小于 256无必填关于requestCode的0~255约束requestPermissions与onRequestPermissionsResult两个方法的 requestCode 参数都标注了IntRange(from 0, to 255)见 EasyPermissions.java这是 Android 系统Activity.requestPermissions对 requestCode 的硬性上限超出会抛异常使用时需注意。此外还有一个值得注意的行为EasyPermissions.requestPermissions(PermissionRequest)在真正发起请求前会先做一次hasPermissions预检查如果权限已全部具备则不再弹窗、不再请求而是直接走notifyAlreadyHasPermissions模拟PERMISSION_GRANTED结果回调见 requestPermissions 方法 与 notifyAlreadyHasPermissions。这意味着已授权 → 直接触发回调/注解方法的路径同样被自动化处理。六、实现 PermissionCallbacks精确响应授权结果如果你需要精细控制授权结果而不是只依赖注解自动续跑可以让 Activity / Fragment 实现EasyPermissions.PermissionCallbacks接口。README 给出的完整示例public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // Forward results to EasyPermissions EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } Override public void onPermissionsGranted(int requestCode, ListString list) { // Some permissions have been granted // ... } Override public void onPermissionsDenied(int requestCode, ListString list) { // Some permissions have been denied // ... } }接口定义在 EasyPermissions.javaPermissionCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback包含onPermissionsGranted(int requestCode, ListString perms)与onPermissionsDenied(int requestCode, ListString perms)两个方法。注意回调中的ListString是本次请求中被授予 / 被拒绝的权限子集而不是全部请求权限——这意味着一次请求可能同时触发两个回调部分授权、部分拒绝分发逻辑见onRequestPermissionsResult中对granted/denied两个列表的处理。这一行为在测试用例 EasyPermissionsTest.java 中有明确验证当结果数组为{DENIED, GRANTED}时onPermissionsDenied收到READ_SMSonPermissionsGranted收到ACCESS_FINE_LOCATION。七、处理永久拒绝用 AppSettingsDialog 引导用户去系统设置某些场景下应用缺少特定权限就无法正常工作。如果用户勾选了不再询问Never Ask Again系统将不再弹权限框应用也无法再通过普通方式请求该权限只能引导用户去系统设置页手动开启。README 给出的处理方式是EasyPermissions.somePermissionPermanentlyDenied(...)AppSettingsDialogOverride public void onPermissionsDenied(int requestCode, ListString perms) { Log.d(TAG, onPermissionsDenied: requestCode : perms.size()); // (Optional) Check whether the user denied any permissions and checked NEVER ASK AGAIN. // This will display a dialog directing them to enable the permission in app settings. if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { new AppSettingsDialog.Builder(this).build().show(); } } Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) { // Do something after user returned from app settings screen, like showing a Toast. Toast.makeText(this, R.string.returned_from_app_settings_to_activity, Toast.LENGTH_SHORT) .show(); } }README 特别强调了一个重要限制受 Android 框架权限 API 提供的信息所限somePermissionPermanentlyDenied只有在权限已被拒绝、且应用已收到onPermissionsDenied回调之后调用才有效否则库无法区分永久拒绝和尚未拒绝两种情况。所以在onPermissionsDenied回调内部使用它是最正确的姿势。从实现角度看somePermissionPermanentlyDenied底层委托给PermissionHelper#somePermissionPermanentlyDenied其判定逻辑是遍历被拒权限任一权限满足!shouldShowRequestPermissionRationale(perm)即为永久拒绝见 PermissionHelper.java——因为 Android 的设计是一旦用户勾选不再询问系统对该权限的 rationale 询问将永远返回false。测试用例 EasyPermissionsTest.java 分别验证了不再展示 rationale → 判定为永久拒绝与仍展示 rationale → 判定非永久拒绝两个分支。AppSettingsDialog.Builder(this)支持Activity或Fragment两种宿主可链式定制标题、文案、按钮、主题、请求码等详见下节参数表。调用.build().show()后库内部会启动AppSettingsDialogHolderActivity来承载弹窗用户点击去设置后通过Settings.ACTION_APPLICATION_DETAILS_SETTINGS携带本应用包名跳转系统应用详情页见 AppSettingsDialogHolderActivity.java。从设置页返回后结果会回传到原宿主的onActivityResult请求码默认是AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE 16061定义见 AppSettingsDialog.java。你可以在返回回调里用EasyPermissions.hasPermissions(...)复查权限是否已被用户手动打开示例工程 MainActivity.java 就是通过检查 Camera / 定位通讯录 / 短信权限并弹出 Toast 反馈的。AppSettingsDialog.Builder的可配置项与默认值如下依据 Builder 源码整理Builder 方法作用默认值setThemeResId(int styleRes)弹窗主题应用默认主题setTitle(String / int resId)弹窗标题R.string.title_settings_dialogPermissions RequiredsetRationale(String / int resId)弹窗解释文案R.string.rationale_ask_again提示应用可能无法正常工作、请到设置页开启权限见 strings.xmlsetPositiveButton(String / int resId)确认按钮文字android.R.string.oksetNegativeButton(String / int resId)取消按钮文字android.R.string.cancelsetRequestCode(int)跳设置页请求码DEFAULT_SETTINGS_REQ_CODE16061setOpenInNewTask(boolean)是否以FLAG_ACTIVITY_NEW_TASK在新任务中打开设置页false八、与 rationale 弹窗交互RationaleCallbacks如果你希望在解释为什么需要权限的弹窗被用户点击后做出响应可以实现EasyPermissions.RationaleCallbacks接口Override public void onRationaleAccepted(int requestCode) { // Rationale accepted to request some permissions // ... } Override public void onRationaleDenied(int requestCode) { // Rationale denied to request some permissions // ... }关键提醒README 原文要点rationale 回调并不代表权限发生了变化——用户接受了 rationale 只说明系统接下来会弹出权限请求框或直接请求最终授权与否仍要通过PermissionCallbacks判断。从源码看这两个回调的触发点在 RationaleDialogClickListener.onClick用户点弹窗确认时回调onRationaleAccepted并立刻以directRequestPermissions发起真实权限请求点取消时回调onRationaleDenied并向PermissionCallbacks模拟通知onPermissionsDenied。另外无论弹窗在 Fragment 还是 Activity 上展示click listener 都会优先取父 Fragment 作为宿主否则回退到 Activity。九、源码级原理一次权限请求背后的完整调用链将以上内容串联起来一次完整的权限请求在库内部的调用链如下对应关系均可从源码确认入口EasyPermissions.requestPermissions(...)Activity / Fragment /PermissionRequest三种重载统一收敛到requestPermissions(PermissionRequest)。先做hasPermissions预检查已具备 → 直接模拟授予回调并结束否则进入下一步。策略分发PermissionHelper.newInstance(host)根据宿主机类型与系统版本选择合适的 Helper见 PermissionHelper.newInstanceBuild.VERSION.SDK_INT M→LowApiPermissionsHelper宿主是AppCompatActivity→AppCompatActivityPermissionsHelper宿主是普通Activity→ActivityPermissionHelper宿主是androidx.fragment.app.Fragment→SupportFragmentPermissionHelper。rationale 判断PermissionHelper#requestPermissions中若任一权限满足shouldShowRequestPermissionRationale则走showRequestPermissionRationale弹出RationaleDialogFragment或RationaleDialogFragmentCompat分别对应 framework Dialog 与 support v4/v7 的 AppCompat 环境二者共用RationaleDialogConfig序列化参数并统一构造不可取消的AlertDialog见 RationaleDialogConfig.java否则直接directRequestPermissions发起系统请求见 PermissionHelper.java。BaseSupportPermissionsHelper还会检查同名 rationale Fragment 是否已展示避免重复弹窗见 BaseSupportPermissionsHelper.java。结果回流系统回调onRequestPermissionsResult→ 应用转发给EasyPermissions.onRequestPermissionsResult→ 拆分 granted/denied → 触发PermissionCallbacks回调 → 全部成功时反射执行AfterPermissionGranted方法。永久拒绝兜底onPermissionsDenied中调用somePermissionPermanentlyDenied→AppSettingsDialog→AppSettingsDialogHolderActivity→ 系统设置页 →onActivityResult收尾。十、低版本兼容API 23对于 Android M 之前的系统API 23运行时权限模型并不存在安装时授权即可。库通过LowApiPermissionsHelper做了显式兼容见 LowApiPermissionsHelper.javahasPermissions(...)在 SDK M 时恒定返回true配合源码中的警告注释让业务代码无需分叉低版本上调用requestPermissions/ 展示 rationale 会抛出IllegalStateException(Should never be requesting permissions on API 23!)从机制上杜绝无意义的请求。配套的 EasyPermissionsLowApiTest.javaRobolectricConfig(sdk 19)验证了低版本下hasPermissions恒为真且从 Activity / Support FragmentActivity / AppCompatActivity / Fragment 发起的请求都会直接得到onPermissionsGranted回调。十一、测试用例佐证行为即契约库的测试代码本身就是行为契约的最佳说明值得阅读以加深理解EasyPermissionsTest.javaRobolectricSDK 23覆盖了权限检查、回调分发、注解方法执行、rationale 弹窗展示与按钮文案、somePermissionDenied/somePermissionPermanentlyDenied/permissionPermanentlyDenied的全部正向与反向分支AppSettingsDialogTest.java 验证了AppSettingsDialog从 Activity 与 Support Fragment 构建时的跳转 intent、默认请求码DEFAULT_SETTINGS_REQ_CODE以及正负按钮点击监听EasyPermissionsLowApiTest.java 如上所述锁定了低版本兼容行为。十二、示例工程速览仓库的app/模块提供了一个完整的示例应用是 README 各 API 的可运行版本MainActivity.javaAppCompatActivity同时实现PermissionCallbacks与RationaleCallbacks演示单权限相机、双权限定位 通讯录的请求以及永久拒绝后的设置页引导与返回复查MainFragment.java演示在Fragment中请求短信权限并使用 private 的AfterPermissionGranted方法strings.xml包含各权限的 rationale 文案与从设置页返回的提示模板。附开源许可EasyPermissions 遵循 Apache License 2.0版权所有 Google2017完整许可文本见 LICENSE。该协议允许自由使用、修改与分发但需保留版权声明与免责条款。使用提示本文所有代码与行为描述均以当前仓库easypermissions库模块源码、app示例模块与easypermissions/src/test测试用例为准接入时请以 README 中标注的依赖版本AndroidX 环境3.0.0、Support Library 环境2.0.1为准并确保targetSdk不低于 23。赞分享移动开发认证鉴权【免费下载链接】easypermissionsSimplify Android M system permissions项目地址https://gitcode.com/gh_mirrors/ea/easypermissions点击查看免费下载相关推荐EasyPermissions简化你的Android权限管理EasyPermissions简化你的Android权限管理 项目基础介绍与编程语言 EasyPermissions 是一个由Google支持的开源项目专为移动开发认证鉴权Android M 运行时权限被拒绝处理指南shouldShowRequestPermissionRationale 的语义与实战Android M 运行时权限被拒绝处理指南shouldShowRequestPermissionRationale 的语义与实战 本文以 在Android文档教程知识库163MusicLyrics如何3秒搞定音乐歌词获取难题163MusicLyrics如何3秒搞定音乐歌词获取难题 你是否曾为寻找一首歌的完整歌词而翻遍全网或者需要批量处理数百首歌曲的字幕却无从下手音乐歌词获取桌面应用音视频上一篇Awesome Fiber数据库集成PostgreSQL、MongoDB连接的最佳实践下一篇如何用Homebrew Cask在3分钟内安装Google Chrome和其他macOS应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?