reference to : http://blog.csdn.net/xxooyc/article/details/50162523
这是今天遇到的一个issue,由于Binder造成的。虽然比较简单,还是保持记录下吧。
先来开看一下Crash log:
1
2
3
4
5
6
7
8
9
10
11
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
|
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:
1
|
44
#define BINDER_VM_SIZE ((
1
*
1024
*
1024
) - (
4096
*
2
))
|
这便是一个进程中binder的大小,大约1M。
给Binder分配内存的代码:
1
2
3
4
5
6
7
8
9
|
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来证明一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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:
1
2
3
4
5
6
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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:
1
2
3
4
5
|
synchronized
(MainActivity.
class
){
PackageInfo pi = getPackageManager()
.getPackageInfo(info.packageName,
PackageManager.GET_ACTIVITIES);
}
|
再次运行就不会Crash了。