更新日志:
-
1.7.2_3 支持AGP 8.0
-
1.7.2_2 支持AGP 7.0.2
-
1.7.1 优化代码
-
1.7.0 支持 Kotlin 和 java 混编
-
1.6.8 修复在 support 包下的错误
-
1.6.4 支持 AndroidX ,用 @GetPermissions4AndroidX 代替 @GetPermissionsAuto;使用 support 的请使用 com.github.2017398956:abcpermission-plugin:1.3.5 。
-
1.6.3 解决在 gradle4.4中 appcompat-v7 版本冲突问题:考虑到 appcompat-v7 现在是必安装的包,因此在项目中不再传递该依赖,以免造成版本冲突,使用时请自行添加 appcompat-v7 依赖。
接入方式优化(快速接入看这里:https://github.com/2017398956/abcpermission-plugin )
由于部分读者反映 AbcPermission 的接入方式太麻烦,利用周末时间我写了个 gradle 插件用于简化接入流程。这里提供了 2 套接入方案:一为最简便的;另一个支持版本自定义(如果你一直用最新版,这个可以忽略)。AbcPermission 的概念 这里 有详细的讲解。这里是一次重要的升级,也可以直接看 AbcPermission 的 readme 文件。
buildscript {
repositories {
...
maven { url 'https://jitpack.io' }
}
dependencies {
...
// 如果你使用的时 androidx 请接入 1.3.5 以后的版本
classpath 'com.github.2017398956:abcpermission-plugin:1.5.0'
}
}
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
apply plugin: 'abcpermission.plugin'
- 如果出现这个错误
- Default interface methods are only supported starting with Android N (--min-api 24): void org.aspectj.lang.ProceedingJoinPoint.stack$AroundClosure(org.aspectj.runtime.internal.AroundClosure)
- 或
- Error: Default interface methods are only supported starting with Android N (--min-api 24): void org.aspectj.lang.ProceedingJoinPoint.stack$AroundClosure(org.aspectj.runtime.internal.AroundClosure)
- 可以在 app moudle 的 build.gradle 中添加上下面的代码:
- android {
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
buildscript {
repositories {
...
maven { url 'https://jitpack.io' }
}
dependencies {
...
classpath 'com.github.2017398956:AspectPlugin:1.2'
}
}
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
apply plugin: 'AspectPlugin'
dependencies {
...
api("com.github.2017398956:AbcPermission:1.6.3") {
exclude module: 'permissionAnnotation'
exclude module: 'permissionCompiler'
}
provided("com.github.2017398956:AbcPermission:1.6.3") {
exclude module: 'permissionSupport'
exclude module: 'permissionCompiler'
}
annotationProcessor("com.github.2017398956:AbcPermission:1.6.3") {
exclude module: 'permissionSupport'
}
}
关于 AbcPermission 的概念 这里 有详细的讲解。但是在以前的版本中我们都不能对用户授权后进行回调,虽然用户只在初次授权的时候会多一次操作,但对用户体验影响不大。为了更加优化用户体检,从 V1.6 以后 AbcPermission 支持对 无返回值的方法 进行回调,并且其回调流程已统一整合在 AbcPerpermission.permissionListener 。请放心,这次升级完全兼容 V1.6 之前的版本。
-
V1.6 之前的版本获取权限时使用的是 @GetPermissions({...}) ,现在你可以放弃这种用法了,将其改为 @GetPermissionsAuto({...}) 这样就可以对 无返回值的方法 在进行申请权限时进行回调;对于 有返回值的方法 ,会走以前的逻辑,请放心升级。
-
回调分为以下三类:1.用户点击 同意授权 ,这种不需要你处理; 2.用户点击 拒绝授权 , 用户点击后会触发 AbcPerpermission.permissionListener.showRequestPermissionRationale(final Permission23Fragment permission23Fragment, final String[] permissions) 你可以在这里根据权限信息弹出相应的提示,当用户点击确定后 通过 permission23Fragment.requestPermissions(permissions); 再次申请权限,注意,这里不要使用 Fragment#requestPermissions(String[], int) 申请权限; 3.用户点击 拒绝授权且不再提示 ,*如果你已经接入了 V1.6 之前的版本,那么,不需要做任何处理,就是这么省心 ^_^,*如果是第一次接入你可以在 ABCPermission.permissionListener.cannotRequestAgain(...) 中进行弹窗提示和打开设置界面,详细内容请移步 这里 。
1.申请权限只需要一行代码
@GetPermissionsAuto({Manifest.permission.WRITE_EXTERNAL_STORAGE})
private void readFile() {
...
}
2.回调的处理,这里只以 用户点击 拒绝授权 为例
@RequiresApi(api = Build.VERSION_CODES.M)
public void showRequestPermissionRationale(final Permission23Fragment permission23Fragment, final String[] permissions) {
StringBuffer stringBuffer = new StringBuffer();
for (String permission : permissions) {
stringBuffer.append(permission);
stringBuffer.append("\n");
}
AlertDialog.Builder builder = new AlertDialog.Builder(permission23Fragment.getActivity()).setTitle("权限申请")
.setMessage(stringBuffer.toString())
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户看到需要权限的理由,同意后再次申请
permission23Fragment.requestPermissions(permissions);
}
})
.setNegativeButton("取消", null);
builder.create().show();
}
一个在 Android M+ 上很方便的权限申请库,只需要在需授权的方法上加上注解即可,不会侵入业务逻辑。当 app 没有该权限时不会执行该方法,并弹出申请权限框,如果用户选择了“不再提示”则会打开设置界面(这里你可以定制);如果有相应权限则会执行并且有异常时在 GetPermissionListener 中抛出,可自行 try catch 或根据异常信息做相应的操作。这里是这个库的思路 http://blog.csdn.net/niuzhucedenglu/article/details/78707302
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:+'
classpath 'org.aspectj:aspectjweaver:+'
}
}
dependencies {
...
api("com.github.2017398956:AbcPermission:1.4") {
exclude module: 'permissionAnnotation'
exclude module: 'permissionCompiler'
}
provided("com.github.2017398956:AbcPermission:1.4") {
exclude module: 'permissionSupport'
exclude module: 'permissionCompiler'
}
annotationProcessor("com.github.2017398956:AbcPermission:1.4") {
exclude module: 'permissionSupport'
}
}
// 如果 module 是 library 则 将 applicationVariants 替换为 libraryVariants
android.applicationVariants.all { variant ->
Task javaCompile = variant.getJavaCompiler()
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", android.bootClasspath.join(File.pathSeparator)]
MessageHandler handler = new MessageHandler(true)
new Main().run(args, handler)
def log = project.logger
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
如果报出 找不到 MessageHandler 等类的错误,先将其注释掉,sync 后再打开加入即可。
// 在申请权前配置
@Override
public void onCreate() {
super.onCreate();
AbcPermission.install(this);
由于权限的申请需要 activity 这里我们维护一个全局的 nowActivity,如果你的目标用户在 4.0 以上你可以在 application 中添加(也必须在申请权前)
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
ApplicationConstant.nowActivity = activity;
}
@Override
public void onActivityStarted(Activity activity) {
ApplicationConstant.nowActivity = activity;
}
@Override
public void onActivityResumed(Activity activity) {
ApplicationConstant.nowActivity = activity;
}
如果你的目标用户包括 4.0 之前的版本,那么会直接执行被注解的方法。但不用担心会发生 crash 。所有被注解的方法发生异常时都可以在 GetPermissionListener 中 try catch。
我已经提供了一个默认的 GetPermissionListener 仅供测试使用。
AbcPermission.permissionListener = new AbcPermission.GetPermissionListener(){
/**
* 当用户不给权限且选择了不再提示后,会执行这个方法,比如打开 设置 界面
*
* @param activity
* @param permissions 用户拒绝授予的权限
*/
@Override
public void cannotRequestAgain(Activity activity, String[] permissions) {
}
/**
* 为了程序不崩溃,被注解的方法在这里抛出异常,需要你自行处理
*
* @param throwable
*/
@Override
public void exeException(Throwable throwable) {
}
} ;
public void onClick(View view) {
readContacts();
}
@GetPermissions(Manifest.permission.READ_CONTACTS)
private void readContacts() {
Toast.makeText(this, "测试成功!!", Toast.LENGTH_SHORT).show();
}
关于兼用性由于没有相应手机可供测试,这里以 AndPermission 提到的几种情况作为适配依据,以下粗体字为我的适配方法。
-
部分中国厂商生产手机(例如小米某型号)的Rationale功能,在第一次拒绝后,第二次申请时不会返回true,并且会回调申请失败,也就是说在第一次决绝后默认勾选了不再提示。AbcPermission 有自己的判断逻辑,所以这一点已经和正常的逻辑统一了,只需要在permissionListener.cannotRequestAgain(...); 中处理即可。
-
部分中国厂商生产手机(例如小米、华为某型号)在申请权限时,用户点击确定授权后,还是回调我们申请失败,这个时候其实我们是拥有权限的。由于 AbcPermission 没有使用回调机制,所以这一点不会造成影响。
-
部分中国厂商生产手机在系统Setting中设置**[禁用/询问]**某权限,但是在申请此权限时却直接提示有权限,这可能是厂商故意这样设计的,当我们真正执行需要这个权限代码的时候系统会自动申请权限。既然需要权限的代码可以自动申请权限,而 AbcPermission 在得知有权限时会直接执行相应代码,所以对我们没影响
-
部分中国厂商生产手机(例如vivo、pppo某型号)在用户允许权限,并且回调了权限授权成功的方法,但是实际执行代码时并没有这个权限。由于没权限,当用户执行到这块代码的时候 app 会在 permissionListener.exeException(...) 抛出异常;此时我们在这里判断异常信息为缺权限时,提示用户打开设置界面即可,并且 permissionListener 是全局的,只需要维护一份代码即可。
-
部分开发者反馈,在某些手机的Setting中授权后,检查时还是没有权限,执行响应的代码时应用崩溃(错误提示是没有权限),这种手机真的兼容不到了,我也觉得没必要兼容了,建议直接放弃这种平台。在 Nexus6P Android 8.0.0 版本发现在使用 "android.permission.SYSTEM_ALERT_WINDOW" 和或"android.permission.WRITE_SETTINGS" 时出现了该问题,估计是新系统 bug (2017-12-08),执行到需要这些权限的方法,AbcPermission 会一直执行你在 AbcPermission.permissionListener.cannotRequestAgain(...) 设定的逻辑(如弹框让用户在设置界面授予权限),考虑到 8.0.0 在国内占有量不大,用户体验还可以接受
- 权限和业务分离
- 23 以下的版本适配到 23+ 以上非常方便: 当你的项目时间比较长,现在要从 Android 23 以下的版本适配到 Android 23 以上时,为了防止因为权限的问题而 crash 往往在 app 启动后立刻申请一系列的权限,如果用户不授予权限则不让用户使用(很多 app 都这么做的);这样处理虽然简便,但十分粗暴,严重影响用户体验。如果对使用到特殊权限的代码进行逐一修改来适配 Android 23+ 工作量大不说,还要小心处理业务逻辑,代码改动越大越容易出错,升级成本过大。如果使用 AbcPermission 则可以直接在这些方法上加一句权限申请的注解即可,不用动以前的逻辑;如果原业务逻辑不在你自己写的方法中,可以对其抽离成一个方法,也十分方便。