reference to : http://blog.csdn.net/xxooyc/article/details/50162523

这是今天遇到的一个issue,由于Binder造成的。虽然比较简单,还是保持记录下吧。

先来开看一下Crash log:

    E/HpnsService(24810): HPNS Version is 5.0java.lang.RuntimeException: Package manager has died
E/HpnsService(24810): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:111)
E/HpnsService(24810): at com.xx.xxx.util.AppUtil.checkInstalledPackageVersionCode(AppUtil.java:568)
E/HpnsService(24810): at com.xx.xxx.util.AppUtil.checkAppStatus(AppUtil.java:653)
E/HpnsService(24810): at com.xx.xxx.view.AppListView$LoadingAppThread.run(AppListView.java:723)
E/HpnsService(24810): Caused by: android.os.TransactionTooLargeException
E/HpnsService(24810): at android.os.BinderProxy.transactNative(Native Method)
E/HpnsService(24810): at android.os.BinderProxy.transact(Binder.java:496)
E/HpnsService(24810): at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1786)
E/HpnsService(24810): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:106)
E/HpnsService(24810): ... 3 more

为什么会发生Package manager has died?

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

102    @Override
103 public PackageInfo getPackageInfo(String packageName, int flags)
104 throws NameNotFoundException {
105 try {
106 PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
107 if (pi != null) {
108 return pi;
109 }
110 } catch (RemoteException e) {
111 throw new RuntimeException("Package manager has died", e);
112 }
113
114 throw new NameNotFoundException(packageName);
115 }

这是一个Binder调用,造成这个的原因是因为发生了RemoteException。

那为什么友会发生RemoteException?

其实也就是下面的这句Caused by: android.os.TransactionTooLargeException造成的。

为什么会造成TransactionTooLargeException?

frameworks/base/core/jni/android_util_Binder.cpp:

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

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

如果是其他原因造成Binder crash的话就会抛出RuntimeException。

那一个进程的Binder内存限制是多少?

frameworks/native/libs/binder/ProcessState.cpp:

44 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))

这便是一个进程中binder的大小,大约1M。

给Binder分配内存的代码:

    349#if !defined(HAVE_WIN32_IPC)
350 // mmap the binder, providing a chunk of virtual address space to receive transactions.
351 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
352 if (mVMStart == MAP_FAILED) {
353 // *sigh*
354 ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
355 close(mDriverFD);
356 mDriverFD = -1;
357 }

通过上面的清理,知道了如果一个进程中使用的Binder内容超过了1M,就会crash.

而如果这时候恰巧在用getPackageManager()做事情,就会提示Package manager has died。

可以事实真的是这样的吗?

写了个demo来证明一下:

    public class MainActivity extends Activity {  

        @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
} private void test() {
for (int i = 0; i < 2; i++) {
new Thread() {
@Override
public void run() {
int count = 0;
List<PackageInfo> list = getPackageManager()
.getInstalledPackages(10000);
for (PackageInfo info : list) {
if(count >=1000){
break;
}
try {
PackageInfo pi = getPackageManager()
.getPackageInfo(info.packageName,
PackageManager.GET_ACTIVITIES);
Log.e("yanchen", "yanchen threadid:"+Thread.currentThread().getId()
+ ",i:" + count++);
} catch (NameNotFoundException e) {
}
}
}
}.start();
}
}
}

这个Demo就是同时创建两个线程来进行Binder调用.

运行打印的log:

    E/yanchen (21180): yanchen threadid:4097,i:271
E/yanchen (21180): yanchen threadid:4097,i:272
E/yanchen (21180): yanchen threadid:4097,i:273
E/yanchen (21180): yanchen threadid:4097,i:274
E/yanchen (21180): yanchen threadid:4097,i:275
E/yanchen (21180): yanchen threadid:4097,i:276

此时也如预期发生了Crash:

    E/JavaBinder(31244): !!! FAILED BINDER TRANSACTION !!!
E/AndroidRuntime(31244): FATAL EXCEPTION: Thread-4798
E/AndroidRuntime(31244): Process: com.example.testdl, PID: 31244
E/AndroidRuntime(31244): java.lang.RuntimeException: Package manager has died
E/AndroidRuntime(31244): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:155)
E/AndroidRuntime(31244): at com.example.testdl.MainActivity$1.run(MainActivity.java:40)
E/AndroidRuntime(31244): Caused by: android.os.TransactionTooLargeException
E/AndroidRuntime(31244): at android.os.BinderProxy.transactNative(Native Method)
E/AndroidRuntime(31244): at android.os.BinderProxy.transact(Binder.java:496)
E/AndroidRuntime(31244): at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2208)
E/AndroidRuntime(31244): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:150)
E/AndroidRuntime(31244): ... 1 more
D/EnterpriseDeviceManagerService( 3021): isMana

解决方式:

其实只要避免多个线程同时来调用Binder就可以了,毕竟一个线程用了会释放,所以理论上是很难发生的。

修改后的Demo:

    synchronized(MainActivity.class){
PackageInfo pi = getPackageManager()
.getPackageInfo(info.packageName,
PackageManager.GET_ACTIVITIES);
}

再次运行就不会Crash了。

最新文章

  1. SQL Server 维护计划实现数据库备份(策略实战)
  2. excel导入数据库
  3. oracle触发器如何使用2
  4. shell学习笔记(1):利用IFS打印用户和默认shell
  5. 【转】从INF文件认识驱动
  6. Javascript的IE和Firefox兼容性汇编
  7. Codeforces 245H Queries for Number of Palindromes
  8. IT人不要一直做技术
  9. Vim入门基础
  10. 短视频服务大PK,阿里云、腾讯云、又拍云、七牛云、金山云5强横向对比
  11. arcgis更改栅格数据范围
  12. Memcached入门学习
  13. 第十二章 NIO
  14. 实验二 合作:王宏财 http://www.cnblogs.com/wanghongcai/
  15. Python中【__all__】的用法
  16. 开源VS商用,IBM区块链从Hyperledger到商用平台之道 | 对话IBM高级架构师【 笔记】(转)
  17. python 安装wheel .whl文件
  18. 如何在PeopleSoft中找到并更改默认样式表名称
  19. mysql CONCAT用法
  20. .NET Core 类库中读取appsettings.json

热门文章

  1. Codeigniter处理用户登录验证后URL跳转
  2. webapi调用post时自动匹配参数
  3. [实战]MVC5+EF6+MySql企业网盘实战(13)——编辑文件夹
  4. eclipse 创建 maven 项目时如何修改 web 的版本和 jdk 的版本
  5. css3实现立方体,并且自转效果
  6. 洛谷——P1330 封锁阳光大学
  7. 图形管线之旅 Part2
  8. POJ 3228 [并查集]
  9. 【BZOJ 3998】 3998: [TJOI2015]弦论 (SAM )
  10. JZYZOJ1330 土地购买 dp 斜率优化