PackageManager可以乱用吗?

版权声明:

本公众号发布的所有文章,均属于原创,版权归本公众号所有。

未经允许,禁止转载。

一、什么是PackageManager?

PackageManager(下文简称PM)是Android系统为开发者提供的系统服务管理类中的一个,主要用于帮助开发者管理应用程序安装包,通过它可以有效的获取设备上安装的Apk或者未安装的Apk文件的一些数据。

PM对象也非常的方便获取,只需要调用Context.getPackageManager()即可获取到,关于PM的简单使用,不在本片文章的范畴内,所以不再进行具体的讲解了,不明白的可以自行查阅资料。

二、PM有什么坑吗?

PM内提供了非常丰富的API,使用起来非常的方便。那么它实际使用起来有什么坑吗?这就要从一个Bug说起了。

直接上崩溃栈。

可以很明确的看到,最终在调用ApplicationPackageManager.getPackageInfo()的时候,抛出了一个 「Package manager has died」的RuntimeException的异常,这会导致崩溃。

实际上,去阅读getPackageInfo的源码,确实可以看到它是有可能抛出这个异常的。

再向下追看源码,可以看到这里其实做了一个Binder调用,但是Binder的使用超出了限制,最终触发了TranscationTooLargeException这个异常,然后在ApplicationPackageManager中catch了一层,转而发出一个  「Package manager has died」的错误。

下面是触发Binder异常的地方:

看注释可以看出,如果Binder的使用超出了一个进程的限制,就才会抛出TransactionTooLargeException这个异常。

那么问题又转到了一个进程的Binder内存限制有多少的问题?

继续查找源码,在ProcessState.cpp文件中可以找到相关内容。

通过上面代码的理解,如果mVMStart大概会分配BINDER_VM_SIZE这么大的内存空间,大概是1MB的样子。如果使用大于这个范围,就会触发Crash,而如果恰巧正在使用getPackageManager(),就会抛「Pakcage manage has died」。

三、写个Demo验证一下

首先既然是多个线程同时获取PackageInfo造成的,那么就写个方法模拟这样的情况。

运行一下,发现也确实是这样的问题,立刻就崩溃了。

证明我们问题找的方向没错,这个确实是因为多线程调用PM的api而产生Binder溢出造成的错误。

四、解决问题

找到问题不代表解决问题了。既然和多线程有关系,那么就加锁吧。对PM的一些api进行简单的封装,加一个锁,保持线程安全。但是既然已经使用多线程了,肯定有效率上的考虑,还可以考虑为PackageInfo加一个缓存。

加缓存问题大不大?以实际例子来说,看功能需求,如果只是看看当前设备上安装了哪些Apk,包名是什么、名称是什么、版本号是什么,我觉得加缓存问题不大,唯一可变动的可能就是版本号如果缓存期间升级了,所以可以考虑对版本号的获取不加严格缓存。

用一个单例的类去承载这样的业务。

修改一下刚才的Demo,运行之后发现稳定无报错,并且从Log上看出,大多数其实已经被cache抗住了,也没什么压力了。

到这里就算把这个问题给解决了,PmWrapper.class其实实现的并不完全,如果有需要添加的方法,按格式自行添加即可。

五、题外话

所有的问题最终都会在源码里找到答案。有时候不方便查看源码,可以考虑用在线查找的方式看源码,这里推荐一个网站:http://androidxref.com/

androidxref可以快捷的查看不同版本下,Android下不同版本,不同层的源代码。

例如本文中:

  • android_util_Binder.cpp:http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/jni/android_util_Binder.cpp

  • ProcessState.cpp:http://androidxref.com/5.0.0_r2/xref/frameworks/native/libs/binder/ProcessState.cpp



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值