博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Android Pro] 分析 Package manager has died
阅读量:6968 次
发布时间:2019-06-27

本文共 7023 字,大约阅读时间需要 23 分钟。

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//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
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了。

 

转载于:https://www.cnblogs.com/0616--ataozhijia/p/6478231.html

你可能感兴趣的文章
为程序添加系统上下文菜单
查看>>
css色彩对应值
查看>>
XWiki 4.3 正式版发布
查看>>
java中finally和return的执行顺序
查看>>
.net微信公众号开发——群发消息
查看>>
纪念逝去的岁月——C++实现一个队列(使用类模板)
查看>>
GO语言练习:多返回值函数
查看>>
让x86的android模拟器能模拟arm架构系统
查看>>
初学Struts2-自定义拦截器及其配置
查看>>
关于js中的几个小问题。
查看>>
hdoj-2058-the sum problem
查看>>
MySql基础整理
查看>>
Spring Bean Scope 有状态的Bean 无状态的Bean
查看>>
php 批量修改文件格式或重命名
查看>>
Android数据加密之Aes加密
查看>>
InputStream,String相互转化
查看>>
Atitit.gui api自动化调用技术原理与实践
查看>>
详解zabbix安装部署(Server端篇)
查看>>
阿里云负载不支持 WebSocket 协议与 WSS 和 Nginx 配置问题
查看>>
获取Android屏幕尺寸、控件尺寸、状态栏/通知栏高度、导航栏高度
查看>>