程序猿如何从产品的角度去提升应用的体验之Android权限优化篇

简介:
本文讲的是 程序猿如何从产品的角度去提升应用的体验之Android权限优化篇, 大家平时在开发的过程中是否会遇到这种情况:很多产品体验上的细节,特别是涉及到技术相关的细节,产品与设计可能并不会给出详细的解决方案,甚至可能并不太关注这方面的体验细节。例如,应用的缓存清理机制该怎么实现?权限申请的时机应该放在哪?用户没有给予应用必要的权限该怎么处理......这种时候,作为一个开发人员,特别是对自家产品的使用体验有追求的开发人员,其实完全可以充当一回产品,从产品的角度出发去思考,该怎样在技术实现的细节上,让自家的APP体验变得更好。千万不要小瞧这些细节,一个产品的极致体验,就是由无数的细节堆砌而成的。

1. 应用通知权限的优化

众所周知,推送对于一个APP来说是很重要的功能。推送在好的产品设计中可以有效地提高产品活跃度,增加用户的忠诚度以及留存率。但是,用户有可能会在无意中把应用的通知权限给禁止了,导致收不到推送(用户主动禁止应用的通知权限除外)。例如,华为手机会在通知中心直接提示用户是否关掉某个应用的通知权限。如果用户,特别是小白用户一不小心把通知权限给禁止了,导致应用收不到推送,反而可能还会把这种情况当做bug来向客服反馈(任何时候都千万不要高估用户对于智能手机使用的了解,尤其是你的APP的目标用户还包括中老年人的时候)。

华为手机的推送中心

如果大家有细心观察的话会发现,当应用的通知权限被禁止的时候,体验好的应用会在适当的时机以及场景下出现提示,告知用户通知在应用中起到的作用,尝试去消除用户的不信任和谨慎心理,并引导用户去打开通知权限。

那么,我们怎么知道自己的应用程序通知权限被禁止了呢?如果被禁止了又该怎么办呢?下面就来说一下解决方案。

1.1 检测应用的通知权限状态

检测应用的通知权限其实比较简单。通过查询 官方文档 可以发现,在support库的API 24.0.0 版本,已经有现成的方法可以直接查询应用的通知权限状态:


 
 
  1. NotificationManagerCompat.from(this).areNotificationEnable(); 

但是,这就意味着应用的 compileSdkVersion 也需要与support库的版本保持一致。如果应用目前所使用的compileSdkVersion 低于 24.0.0 或者由于某些历史原因而不能将compileSdkVersion 升到 24.0.0以上,那么就没有办法检测到应用的通知权限了吗?

其实,办法还是有的。通过查看系统源码,可以发现,NotificationManagerCompat.from(this).areNotificationEnable() 这个方法在不同版本的SDK上会有不同的实现。


 
 
  1. /** 
  2.  
  3. Returns whether notifications from the calling package are not blocked. 
  4.  
  5. */ 
  6.  
  7. public boolean areNotificationsEnabled() {  
  8.     return IMPL.areNotificationsEnabled(mContext, mNotificationManager); 
  9.  
  10.  

IMPL是一个实现了Impl接口的实现类对象,而且在静态代码块中通过判断手机系统所使用的版本号来初始化不同的实现类:


 
 
  1. static {    if (BuildCompat.isAtLeastN()) { 
  2.         IMPL = new ImplApi24(); 
  3.     } else if (Build.VERSION.SDK_INT >= 19) { 
  4.         IMPL = new ImplKitKat(); 
  5.     }  else if (Build.VERSION.SDK_INT >= 14) { 
  6.         IMPL = new ImplIceCreamSandwich(); 
  7.     } else { 
  8.         IMPL = new ImplBase(); 
  9.     } 
  10.     SIDE_CHANNEL_BIND_FLAGS = IMPL.getSideChannelBindFlags(); 
  11.  

当手机系统Android版本小于4.4.0的时候, areNotificationEnable()方法会默认返回true。所以,此方法只有在4.4.0以上的手机系统上才能返回准确的结果。


 
 
  1. /** 
  2.  * Returns whether notifications from the calling package are blocked. 
  3.  */ 
  4. public boolean areNotificationsEnabled() { 
  5.     INotificationManager service = getService(); 
  6.     try {        return service.areNotificationsEnabled(mContext.getPackageName()); 
  7.     } catch (RemoteException e) { 
  8.         throw e.rethrowFromSystemServer(); 
  9.     } 
  10.  

这种反射的方式实际上就是通过AppOpsManager和AppOpsService去获取位于/data/system/目录下的文件Appops.xml里的数据。所以,这个系统源码里的方法可以单独抽取出来作为一个通用的检测通知权限状态的方法。有关AppOpsManager,这里先不做展开,等下在1.4章节单独说一下。

1.2 引导用户跳转到通知权限设置界面

既然已经能检测到应用的通知权限状态,当应用的通知权限被禁止的时候,应该出现提示告知用户通知在应用中起到的作用,并引导用户去打开通知权限。以下为跳转到通知权限设置的通用方法:


 
 
  1. public void toNotificationSetting() {    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
  2.         Intent intent = new Intent(); 
  3.         intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 
  4.         intent.putExtra("app_package", this.getPackageName()); 
  5.         intent.putExtra("app_uid", this.getApplicationInfo().uid); 
  6.         startActivity(intent); 
  7.     } else if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { 
  8.         Intent intent = new Intent(); 
  9.         intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 
  10.         intent.addCategory(Intent.CATEGORY_DEFAULT); 
  11.         intent.setData(Uri.parse("package:" + this.getPackageName())); 
  12.         startActivity(intent); 
  13.     } 
  14.  

在5.0以上,可以直接跳转到某个应用的通知权限快捷设置界面。但是在5.0以下,暂时还没有找到可以直接跳转到通知权限设置界面的方法,所以目前的做法是跳转某个应用的设置界面,在设置界面列表中应该会有通知权限管理的入口。如果你有更好的做法,欢迎在评论中指出来。这里再另外抛出一个问题,供大家思考:关于通知权限提示的方案,应该在什么时机或场景下出现好?出现提示的频率为多少好呢?是只提示一次呢,还是只要用户没打开通知权限就一直提示呢?欢迎大家在留言中说出自己的看法。

1.3 当通知权限被关闭后,Toast可能无法正常工作的问题

虽然跟通知权限的优化没什么关系,不过在这里还是要提一下。这个是在调研通知权限优化问题的时候偶然发现的坑:在大部分的机型上,当应用的通知权限被关闭后,系统的 Toast会直接无法正常工作。

以下是我测试过的数据:

可以看出,这个坑影响的机型范围还是挺大的。为什么会这样呢?

查阅源码后可以发现 Toast 里也用到了NotificationManagerService。在Toast执行show()方法后,执行到enqueueToast()的时候如下:


 
 
  1. if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {    if (!isSystemToast) { 
  2.         Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");        return
  3.     } 
  4.  

原来这里也用到了检测通知权限的方法noteNotificationOp()。如果通知权限被禁止了,那么Toast也就无法正常工作。

对于Android手机来说,Toast在应用中随处可见,如果因为通知权限导致Toast不工作那么影响还是挺大的。所以,在这里建议大家寻找一下Toast的替代方案,不要在项目中直接使用系统自带的Toast。同样的,如果大家有什么好的解决方案,也欢迎在留言中指出来。

1.4 AppOpsManager的工作原理

既然上面提到了AppOpsManager,那么这里来简单地介绍一下它的工作原理。AppOpsManager的工作框架图如下:

Setting UI通过AppOpsManager与AppOpsService 交互,给用户提供入口管理各个app的操作。

AppOpsService具体处理用户的各项设置,用户的设置项存储在 /data/system/appops.xml文件中。

AppOpsService也会被注入到各个相关的系统服务中,进行权限操作的检验。

各个权限操作对应的系统服务(比如定位相关的Location Service,Audio相关的Audio Service等)中注入AppOpsService的判断。如果用户做了相应的设置,那么这些系统服务就要做出相应的处理。比如,LocationManagerSerivce的定位相关接口在实现时,会有判断调用该接口的app是否被用户设置成禁止该操作,如果有该设置,就不会继续进行定位。

2. 应用权限的提示优化

由于篇幅的原因,这里就拿相机权限来举例。假设需求如下:

在需要使用相机的场景下,先提前检测相机权限是否打开,如果没有打开,则尝试申请相机权限,如果用户还是拒绝,则出现权限提示,引导用户去开启相机权限。下面是微信的处理方式:

(1)6.0系统以上,先尝试申请相机权限,用户点击禁止后弹出引导界面

(2)6.0系统以下,用户点击保持禁止后弹出引导界面

在6.0以上,我们一般可以通过系统自带的方法来检测某个权限是否被允许:


 
 
  1. ActivityCompat.checkSelfPermission(context, permission) 

但是,在6.0以下,如果想检测相机权限,却没有一个很好的方法。最后,经过查阅各种资料,发现好像只能通过一种简单粗暴的方式去检测相机权限:


 
 
  1. /** 
  2.  * 在6.0系统以下,通过尝试打开相机的方式判断有无拍照权限 
  3.  * 
  4.  * @return 
  5.  */ 
  6. private boolean checkCameraPermissionUnderM() { 
  7.     boolean isCanUse = true
  8.     Camera mCamera = null
  9.     try { 
  10.         mCamera = Camera.open(); 
  11.         Camera.Parameters mParameters = mCamera.getParameters(); 
  12.         mCamera.setParameters(mParameters); 
  13.     } catch (Exception e) { 
  14.         isCanUse = false
  15.     }    if (mCamera != null) { 
  16.         try { 
  17.             mCamera.release(); 
  18.         } catch (Exception e) { 
  19.             e.printStackTrace();            return isCanUse; 
  20.         } 
  21.     }    return isCanUse; 
  22.  

当使用这个方法的时候,在6.0以下的手机,一般调用 Camera.open()方法,如果相机权限没打开,会先弹出相机权限申请弹框。如果点击允许,那么该方法就会返回true,点击禁止则返回false。这里需要指出的是,如果你使用相机的方式是通过Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)来跳转到系统的相机界面的话,那么即使应用的相机权限被禁止了,也还是可以正常使用相机来拍照的。这种情况下要不要做权限检查,就看个人的看法了。

不知道大家有没注意到,微信在6.0以上和6.0以下弹出的提示对话框有点不同。在6.0以上提供“去设置”的选项,点击会跳转到设置里的应用列表界面,在6.0以下仅仅是提示。这也是一个产品的细节。个人看法,因为在6.0以下,有些国产系统的权限管理根本就不在设置里面,而是需要到官方提供的手机管家类型应用里面才可以进行权限的管理。那么多的国产系统,需要适配有点困难,如果没有很好的解决方案,那么还不如不要擅自帮用户做决定。可以看得出微信在跳转到权限设置界面的适配上也经过了一番考量,最后选择了这种折中的方案。

说到这里,既然权限提示优化的思路已经有了,大家也可以在自己的项目中封装一个权限管理类,“检查权限-被禁止-尝试申请权限-被拒绝-弹出提示框-引导用户去打开权限”通过一个方法一气呵成。

3. 总结

本文涉及到的知识点可能并不深奥,更多的是想向大家展示一下,假如开发人员从产品的角度去提升应用的体验,可以从什么角度去切入。如果能给大家带来一些启发就好了。看完文章后,不凡思考一下,自己的应用在权限提示上的体验是否做到最好了呢?如果还有可以改善的地方,那么赶紧根据自家应用的实际情况,改善一下权限提示的体验吧。相信我,做了这件事,你的用户会感激你的。


本文作者:佚名

来源:51CTO

原文标题:程序猿如何从产品的角度去提升应用的体验之Android权限优化篇
相关文章
|
10天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
10天前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
14天前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。
|
16天前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
26 2
|
20天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
46 5
|
20天前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
29天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
42 4
|
29天前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
47 2
|
11天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
1月前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
21 0