这是今天遇到的一个issue,由于Binder造成的。虽然比较简单,还是保持记录下吧。
先来开看一下Crash log:
1. E/HpnsService(24810): HPNS Version is 5.0java.lang.RuntimeException: Package manager has died
2. E/HpnsService(24810): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:111)
3. E/HpnsService(24810): at com.xx.xxx.util.AppUtil.checkInstalledPackageVersionCode(AppUtil.java:568)
4. E/HpnsService(24810): at com.xx.xxx.util.AppUtil.checkAppStatus(AppUtil.java:653)
5. E/HpnsService(24810): at com.xx.xxx.view.AppListView$LoadingAppThread.run(AppListView.java:723)
6. E/HpnsService(24810): Caused by: android.os.TransactionTooLargeException
7. E/HpnsService(24810): at android.os.BinderProxy.transactNative(Native Method)
8. E/HpnsService(24810): at andr
oid.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:
1. 102 @Override
2. 103 public PackageInfo getPackageInfo(String packageName, int flags)
3. 104 throws NameNotFoundException {
4. 105 try {
5. 106 PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId());
6. 107 if (pi != null) {
7. 108 return pi;
8. 109 }
9. 110 } catch (RemoteException e) {
10. 111 throw new RuntimeException("Package manager has died", e);
11. 112 }
12. 113
13. 114 throw new NameNotFoundException(packageName);
14. 115 }
这是一个Binder调用,造成这个的原因是因为发生了RemoteException。
那为什么友会发生RemoteException?
Caused by: android.os.TransactionTooLargeException造成的。
为什么会造成TransactionTooLargeException?
frameworks/base/core/jni/android_util_Binder.cpp:
1. 682 case FAILED_TRANSACTION:
2. 683 ALOGE("!!! FAILED BINDER TRANSACTION !!!");
3. 684 // TransactionTooLargeException is a checked exception, only throw from certain methods.
4. 685 // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
5. 686 // but it is not the only one. The Binder driver can return BR_FAILED_REPLY
6. 687 // for other reasons also, such as if the transaction is malformed or
7. 688 // refers to an FD that has been closed. We should change the driver
8. 689 // to enable us to distinguish these cases in the future.
9. 690 jniThrowException(env, canThrowRemoteException
10. 691 ? "android/os/TransactionTooLargeException"
11. 692 : "java/lang/RuntimeException", NULL);
12. 693 break;
可以看出如果Binder的使用超出了一个进程的限制就会抛出TransactionTooLargeException这个异常。
如果是其他原因造成Binder crash的话就会抛出RuntimeException。
那一个进程的Binder内存限制是多少?
frameworks/native/libs/binder/ProcessState.cpp:
1. 44 #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
这便是一个进程中binder的大小,大约1M。
给Binder分配内存的代码:
1. 349#if !defined(HAVE_WIN32_IPC)
2. 350 // mmap the binder, providing a chunk of virtual address space to receive transactions.
3. 351 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
4. 352 if (mVMStart == MAP_FAILED) {
5. 353 // *sigh*
6. 354 ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
7. 355 close(mDriverFD);
8. 356 mDriverFD = -1;
9. 357 }
通过上面的清理,知道了如果一个进程中使用的Binder内容超过了1M,就会crash.
而如果这时候恰巧在用getPackageManager()做事情,就会提示Package manager has died。
可以事实真的是这样的吗?
写了个demo来证明一下:
1. public class MainActivity extends Activity {
2.
3. @Override
4. protected void onCreate(Bundle savedInstanceState) {
5. super.onCreate(savedInstanceState);
6. setContentView(R.layout.activity_main);
7. test();
8. }
9.
10. private void test() {
11. for (int i = 0; i < 2; i++) {
12. new Thread() {
13. @Override
14. public void run() {
15. int count = 0;
16. List<PackageInfo> list = getPackageManager()
17. 10000);
18. for (PackageInfo info : list) {
19. if(count >=1000){
20. break;
21. }
22. try {
23. PackageInfo pi = getPackageManager()
24. .getPackageInfo(info.packageName,
25. PackageManager.GET_ACTIVITIES);
26. "yanchen", "yanchen threadid:"+Thread.currentThread().getId()
27. ",i:" + count++);
28. catch (NameNotFoundException e) {
29. }
30. }
31. }
32. }.start();
33. }
34. }
35. }
这个Demo就是同时创建两个线程来进行Binder调用.
运行打印的log:
1. E/yanchen (21180): yanchen threadid:4097,i:271
2. E/yanchen (21180): yanchen threadid:4097,i:272
3. E/yanchen (21180): yanchen threadid:4097,i:273
4. E/yanchen (21180): yanchen threadid:4097,i:274
5. E/yanchen (21180): yanchen threadid:4097,i:275
6. E/yanchen (21180): yanchen threadid:4097,i:276
此时也如预期发生了Crash:
1. E/JavaBinder(31244): !!! FAILED BINDER TRANSACTION !!!
2. E/AndroidRuntime(31244): FATAL EXCEPTION: Thread-4798
3. E/AndroidRuntime(31244): Process: com.example.testdl, PID: 31244
4. E/AndroidRuntime(31244): java.lang.RuntimeException: Package manager has died
5. E/AndroidRuntime(31244): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:155)
6. E/AndroidRuntime(31244): at com.example.testdl.MainActivity$1.run(MainActivity.java:40)
7. E/AndroidRuntime(31244): Caused by: android.os.TransactionTooLargeException
8. E/AndroidRuntime(31244): at android.os.BinderProxy.transactNative(Native Method)
9. E/AndroidRuntime(31244): at android.os.BinderProxy.transact(Binder.java:496)
10. E/AndroidRuntime(31244): at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:2208)
11. E/AndroidRuntime(31244): at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:150)
12. E/AndroidRuntime(31244): ... 1 more
13. D/EnterpriseDeviceManagerService( 3021): isMana
解决方式:
其实只要避免多个线程同时来调用Binder就可以了,毕竟一个线程用了会释放,所以理论上是很难发生的。
修改后的Demo:
1. synchronized(MainActivity.class){
2. pi = getPackageManager()
3. .getPackageInfo(info.packageName,
4. PackageManager.GET_ACTIVITIES);
5. }
再次运行就不会Crash了。