標籤:nbsp 5.0 work 比較 大小 jni 多個 tar change
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 @Override103 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 }113114 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了。
[Android Pro] 分析 Package manager has died