Android 获取 PackageInfo 引发 Crash 填坑

一般 Android 通过PackageInfo这个类来获取应用安装包信息,比如应用内包含的所有Activity名称、应用版本号之类的。PackageInfo通过PackageManager来获取,代码如下:

PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);

比如我们要获取应用版本号时:

public static int getVersionCode(Context context) {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
}

Tip: 获取应用自身版本号,推荐使用BuildConfig.VERSION_CODE 方式,这里只是为了方便举例说明问题。

一般情况下,上面的方法是可以正常拿到数据的,但是在某些情况下这也可能会引发 java.lang.RuntimeException: Package manager has died 异常。

 java.lang.RuntimeException: Package manager has died
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:82)

为了分析引发 Package manager has died 这个问题的具体原因,我们先来看看 getPackageInfo 这个方法:

frameworks/base/core/java/android/app/ApplicationPackageManager.java:

@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
try {
PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
if (pi != null) {
return pi;
}
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
} throw new NameNotFoundException(packageName);
}

从上面可以看出,getPackageInfo 具体实现是一个 Binder 调用,造成这个的原因是因为发生了 RemoteException 。

Binder 调用为什么会造成 Exception,下面再来看看 Binder 代码frameworks/base/core/jni/android_util_Binder.cpp:

 case FAILED_TRANSACTION:
ALOGE("!!! FAILED BINDER TRANSACTION !!!");
// TransactionTooLargeException is a checked exception, only throw from certain methods.
// FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
// to enable us to distinguish these cases in the future.
jniThrowException(env, canThrowRemoteException
? "android/os/TransactionTooLargeException"
: "java/lang/RuntimeException", NULL);
break;

可以看出造成 Binder crash 抛出 RuntimeException 是因为获取应用 PackageInfo 中数据量太大了,超出了 Binder 可传递的最大容量,进而导致 PackageManager 崩溃。

对于上面这种情况,考虑如果只获取versionName和versionCode两个信息,不需要Activity等信息,设法让PackageInfo的信息量小点,避免超出了 Binder 可传递的最大容量。

我们可以利用 getPackageInfo(String packageName, @PackageInfoFlags int flags) 它的第二个参数 flag ,使得该方法返回的对象容量减小,比如使用 PackageManager.GET_CONFIGURATIONS

此外,如果对与Binder的同时调用超出了限制就会抛出 TransactionTooLargeException这个异常,虽然这种场景比较少见,但是我们还是有比较避免多个线程同时来调用Binder就可以了。

优化后代码如下:

public static int getVersionCode(Context context) {
synchronized(Hold.class){
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS);
return info.versionCode;
}
}

Package manager has died异常PackageInfo 引发 Crash的更多相关文章

  1. [Android Pro] 分析 Package manager has died

    reference to : http://blog.csdn.net/xxooyc/article/details/50162523 这是今天遇到的一个issue,由于Binder造成的.虽然比较简 ...

  2. Visual Studio 2015 新建MVC项目 Package Manager Console不能使用 (HRESULT: 0x80131500)

    Visual studio 2015 突然新建不了MVC项目,报出错误: HRESULT: 0x80131500 在折腾了很长时间,最后在Github上看到这样一个贴 地址:https://githu ...

  3. 你需要知道的包管理器(Package Manager)

    最近我花了一点时间关注了在不同系统之中所用到的包管理器(Package Manager) .最开始的时候,我是在使用Linux操作系统时,对这种工具以及它背后的想法深深迷恋住了:这真是自由的软件世界. ...

  4. 解决VS2015启动时Package manager console崩溃的问题 - Windows PowerShell updated your execution policy successfully, but the setting is overridden by a policy defined at a more specific scope

    安装VS2015,启动以后,Package manager console崩溃,错误信息如下: Windows PowerShell updated your execution policy suc ...

  5. Error: Could not access the Package Manager. Is the system running?

    最近在搭建cordova,android 开发环境,安装android studio之后创建一个demo之后,运行想看一下效果,在运行过程中创建一个虚拟机(arm)的,等了有1分钟左右,再次运行程序, ...

  6. Visual Studio 2010 更新NuGet Package Manager出错解决办法

    在Visual Studio 2010的扩展管理器中发现NuGet Package Manger有最新版本更新提示,选择更新安装提示以下错误信息: 2013/4/25 1:11:48 - Micros ...

  7. Getting and installing the PEAR package manager

    Windows After you have downloaded and installed PHP, you have to manually execute the batch file loc ...

  8. RPM是RedHat Package Manager(RedHat软件包管理工具)

    RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...

  9. installation - How to install Synaptic Package Manager? - Ask Ubuntu

    installation - How to install Synaptic Package Manager? - Ask Ubuntu How to install Synaptic Package ...

随机推荐

  1. arcgis server10.2发布地图服务报错

    发布地图服务时,读取了本机电脑中的切片方案.发布服务,报打包成功,但发布失败错误. 解决办法:给arcgis账户,赋予读写权限即可.重复发布服务,成功发布.

  2. js常见的设计模式一

    1.观察者模式 定义一些一对多的关系,将一系列的观察者对目标函数感兴趣,将自己添加进目标函数,当目标函数状态发生改变时,发送通知,以此通知附加在目标上的观察者 /* 目标 */ class subje ...

  3. java中构造器(Constructor)

    大部分内容转自:http://tech.it168.com/j/2006-05-18/200605181021879.shtml        构造器是一个创建对象时被自动调用的特殊方法,为的是初始化 ...

  4. Vue实现二级菜单的显示与隐藏

    <html> <head> <title>Vue实现二级菜单的显示与隐藏</title> <script src="vue.js&quo ...

  5. ASSERT()断言

    头文件<assert.h>   作用:用于判断是否有非法的数据,有则程序报告错误,终止运行.(注意是非法情况,而不是错误情况)   ASSERT()和assert()的区别: ASSERT ...

  6. 关于tomcat NoClassDefDoundErr异常的记录

    在做DRP项目的时候,copy了drp1.3,粘贴重命名成drp1.4,把drp1.4加入到tomcat中,发现drp1.4中新加的jsp可以正常运行,而从1.3那copy来的不能运行,抛出NoCla ...

  7. fpga新建nios

    [FPGA]基于Qsys的第一个Nios II系统设计 (2013-12-12 21:50:08) 转载▼ 标签: fpga niosii qsys helloword quartusii 分类: 嵌 ...

  8. Mongo--03 mongo副本集、备份与恢复

    目录 一.mongo副本集配置 二.查看副本集状态 三.副本集权重调整 四.创建节点 五.仲裁节点 六.mongo备份与恢复 七.准备测试数据 一.mongo副本集配置 1.创建节点目录和数据目录 # ...

  9. 下载放在resource下面的excel文件

    1.将excel文件放项目resources目录下 2.打包的时候排除指定后缀文件,否则打包时会出现文件损坏的情况 <configuration> <encoding>UTF- ...

  10. Django【第28篇】:优化查询的方式

    优化查询的方式 一.假设有三张表 Room id 1 2 .. 1000 User: id 1 .. 10000 Booking: user_id room_id time_id date 1 1 8 ...