分享更有价值
被信任是一种快乐

Android怎么解决APP定位过于频繁问题

文章页正文上

本篇内容介绍了“Android怎么解决APP定位过于频繁问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 背景定位现在是很多 APP 最基本也不可或缺的能力之一,尤其是对打车、外卖之类的应用来说。但对定位的调用可不能没有节制,稍有不慎可能导致设备耗电过快,最终导致用户卸载应用。笔者所在项目是一个在后台运行的 APP,且需要时不时在后台获取一下当前位置,再加上项目里会引入很多合作第三方的库,这些库内部同样也会有调用定位的行为,因此经常会收到测试的反馈说我们的应用由于定位过于频繁导致耗电过快。排查这个问题的时候,笔者首先排除了我们业务逻辑的问题,因为项目中的各个功能模块在定位时调用的是统一封装后的定位模块接口,该模块中由对相应的接口做了一些调用频率的统计和监控并打印了相关的 log 语句,而问题 log 中跟定位相关的 log 语句打印频率跟次数都是在非常合理的范围内。这时我才意识到频繁定位的罪魁祸首并不在我们内部,而是第三方库搞的鬼。那么问题来了,引入的第三方库那么多,我怎么知道谁的定位调用频率不合理呢?虽然我在项目中的公共定位模块中打了 log,但问题是第三方库可调不到我们内部的接口。那么我们能不能到更底层的地方去埋点统计呢?2. AOPAOP,即面向切面编程,已经不是什么新鲜玩意了。就我个人的理解,AOP 就是把我们的代码抽象为层次结构,然后通过非侵入式的方法在某两个层之间插入免费云主机、域名一些通用的逻辑,常常被用于统计埋点、日志输出、权限拦截等等,详情可搜索相关的文章,这里不具体展开讲 AOP 了。要从应用的层级来统计某个方法的调用,很显然 AOP 非常适合。而 AOP 在 Android 的典型应用就是 AspectJ 了,所以我决定用 AspectJ 试试,不过哪里才是最合适的插入点呢?我决定去 SDK 源码里寻找答案。3. 策略探首先我们来看看定位接口一般是怎么调用的:当然不止这两个接口,还有好几个重载接口,但是通过查看 LocationManager 的源码,我们可以发现最后都会调到这个方法:看起来这里是一个比较合适的插入点,但是如果你通过 AspectJ 的注解在这个方法被调用的时候打印 log (AspectJ 的具体用法不是本文重点,这里不讲解), 编译运行下来后会发现根本没有打出你要的 log。通过了解 AspectJ 的工作机制,我们就可以知道为什么这个方法行不通了:… 在 class 文件生成后至 dex 文件生成前,遍历并匹配所有符合 AspectJ 文件中声明的切点,然后将事先声明好的代码在切点前后织入LocationManager 是 android.jar 里的类,并不参与编译(android.jar 位于 android 设备内)。这也宣告 AspectJ 的方案无法满足需求。4. 另辟蹊径软的不行只能来硬的了,我决定祭出反射+动态代理杀招,不过还前提还是要找到一个合适的插入点。通过阅读上面 LocationManager 的源码可以发现定位的操作最后是委托给了 mService 这个成员对象的的 requestLocationUpdates 方法执行的。这个 mService 是个不错的切入点,那么现在思路就很清晰了,首先实现一个 mService 的代理类,然后在我们感兴趣的方法(requestLocationUpdates)被调用时,执行自己的一些埋点逻辑 (例如打 log 或者上传到服务器等)。首先实现代理类:以上这个代理的作用就是取代 LocationManager 的 mService 成员,而实际的 ILocationManager 将被这个代理包装。这样我就能对实际 ILocationManager 的方法进行插桩,比如可以打 log,或将调用信息记录在本地磁盘等。值得一提的是, 由于我只关心 requestLocationUpdates, 所以对这个方法进行了过滤,当然你也可以根据需要制定自己的过滤规则。代理类实现好了之后,接下来我们就要开始真正的 hook 操作了,因此我们实现如下方法:简单几行代码就可以完成 hook 操作了,使用方法也很简单,只需要将 LocationManager 实例传进这个方法就可以了。现在回想一下我们是怎么获取 LocationManager 实例的:咱们一般当然是想 hook 应用全局的定位接口调用了,聪明的你也许想到了在 Application 初始化的时候去执行 hook 操作。也就是可是这样真的能保证全局的 LocationManager 都能被 hook 到吗?实测后你会发现还是有漏网之鱼的,例如如果你通过 Activity 的 context 获取到的 LocationManager 实例就不会被 hook 到,因为他跟 Application 中获取到的 LocationManager 完全不是同一个实例,想知道具体原因的话可参阅这里。所以如果要 hook 到所有的 LocationManager 实例的话,我们还得去看看 LocationManager 到底是怎么被创建的。我们再到 SystemServiceRegistry 一探究竟到这里,我们也就知道真正创建 LocationManager 实例的地方是在CachedServiceFetcher.createService,那问题就简单了,我在 LocationManager 被创建的地方调用 hookLocationManager,这下不就没有漏网之鱼了。但是要达到这个目的,我们得把LocationService 对应的 CachedServiceFetcher 也 hook 了。大体思路是将SYSTEM_SERVICE_FETCHERS 中 LocationService 对应的 CachedServiceFetcher 替换为我们实现的代理类 LMCachedServiceFetcherProxy,在代理方法中调用 hookLocationManager。代码如下:也许你发现了,上面我们明明说的创建 LocationManager 实例的地方是在CachedServiceFetcher.createService,可是这里我在 getService 调用时才去 hook LocationManager, 这是因为 createService 的调用时机太早,甚至比 Application 的初始化还早,所以我们只能从 getService 下手。经过上面的分析我们知道每次你调用context.getSystemService 的时候,CachedServiceFetcher.getService 都会调用,但是createService 并不会每次都调用,原因是 CachedServiceFetcher 内部实现了缓存机制,确保了每个 context 只能创建一个 LocationManager 实例。那这又衍生另一个问题,即同一个LocationManager 可能会被 hook 多次。这个问题也好解决,我们记录每个被 hook 过的LocationManager 实例就行了,HookHelper 的最终代码如下:“Android怎么解决APP定位过于频繁问题”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注云技术网站,小编将为大家输出更多高质量的实用文章!

相关推荐: 分析计算机网络HTTPS原理

本篇内容介绍了“分析计算机网络HTTPS原理”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、HTTP 的最大弊端——不安全HTTP 之所以被 HTTPS 取代…

文章页内容下
赞(0) 打赏
版权声明:本站采用知识共享、学习交流,不允许用于商业用途;文章由发布者自行承担一切责任,与本站无关。
文章页正文下
文章页评论上

云服务器、web空间可免费试用

宝塔面板主机、支持php,mysql等,SSL部署;安全高速企业专供99.999%稳定,另有高防主机、不限制内容等类型,具体可咨询QQ:360163164,Tel同微信:18905205712

主机选购导航云服务器试用

登录

找回密码

注册