一般 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 崩溃。

对于上面这种情况,考虑如果只获取versionNameversionCode两个信息,不需要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;    }  }

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 之View state changes(视图状态改变)
  3. Android(安卓)代码用来返回上一个activity 调用onKeyDown()时发
  4. Android(安卓)PopupWindow Dialog 关于 is your activity runnin
  5. Android(安卓)MediaScanner源代码解析
  6. gradle自动修改android版本号的方法,取java静态变量重命名apk文件
  7. Android(安卓)BaseAdapter与ListView的使用
  8. Android滑动手势侦测方法
  9. Android4.0 LCD和键盘 背光亮度设置

随机推荐

  1. hust校赛d题 PHP is the best language i
  2. 存储用户所需语言的最佳方式
  3. PHP中使用cURL实现Get和Post请求的方法
  4. 一些PHP相关的数据比较
  5. Windows7搭建Apache本地服务器+PHP环境
  6. 如何为$ wpdb-> insert创建一个包含colum
  7. 为什么cron工作不是从浏览器工作,但从终端
  8. php连接mysql的三种方法
  9. 如何递归将特色图像应用于Wordpress中的
  10. 将Ajax div内容存储在PHP变量中