Android应用程序包管理器因TransactionTooLargeException异常而终止。

11

我的应用程序读取所有已安装的APK文件列表,然后循环遍历该列表以读取APK信息,但是它会抛出TransactionTooLargeException异常。

根据我在这里http://developer.android.com/reference/android/os/TransactionTooLargeException.html所读到的,谷歌建议将大事务拆分为较小的事务。但是,在循环APK列表时似乎是发生在中途。如果我捕获并继续处理异常,则其余部分都可以正常工作。在调用getPackageInfo时是否有减少内存使用的方法?该调用返回后是否仍然保留某些内容。

以下是发生异常时的跟踪:

at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:89)
at com.myapp.appreader.getAppDetails(Appreader.java:207)
at com.myapp.appreader.collectData(Appreader.java:99)
at com.myapp.appreader.AppDataCollectionTask.run(AppDataCollectionTask.java:26)
at com.myapp.appreader.service.AppDataTaskExecutor$AppDataAsyncTask.executeTask(AppDataTaskExecutor.java:439) 
at com.myapp.appreader.service.AppDataTaskExecutor$AppDataAsyncTask.doInBackground(AppDataTaskExecutor.java:327)
at com.myapp.appreader.service.AppDataTaskExecutor$AppDataAsyncTask.doInBackground(AppDataTaskExecutor.java:1)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856)\nCaused by: android.os.TransactionTooLargeExceptionat android.os.BinderProxy.transact(Native Method)
at android.content.pm.IPackageManager$Stub$Proxy.getPackageInfo(IPackageManager.java:1538)
at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:84)

尝试每次调用使用更小的标志列表。也就是说,不要在一个调用中请求所有可能的标志,而是尝试进行两到三个调用,每个调用都带有一部分标志。 - CommonsWare
现在我的调用中有7个标志,这太大了吗?我将尝试将其拆分为2-3个调用,看看情况如何。目前错误总是发生在循环中的第74个APK上。但是,在它之后还有更多,而且所有这些(以及那个之前的)都正常工作。 - Ray
“现在我的调用中有7个标志,这太大了吗?”-- 这取决于包的复杂程度。包含的组件和其他内容越多,结果集就越大。也许第74个应用程序只是一个非常复杂的应用程序。 - CommonsWare
这很有趣。我想这是一个复杂的应用程序(Google 地图)。然而,我尝试只调用 Google 地图的 getPackageInfo,它可以正常工作,没有任何问题。因此,显然循环也是异常的一个因素。一些资源释放得不够快。将循环分成几个较小的循环值得一试吗? - Ray
显示剩余2条评论
2个回答

26

Binder 事务存在1MB的限制,这意味着大多数IPC调用必须控制在较小的大小。

如果从PackageManager(例如 getPackageInfo())检索数据时遇到TransactionTooLargeException或类似的Binder失败,请尝试将请求拆分为多个调用,并减少每个调用中的标志数(例如,GET_META_DATA)。希望这样可以将任何单个事务的大小减小到1MB以下。

此外,如果您正在使用返回多个结果的PackageManager调用(例如,getInstalledPackages()),请尝试在该调用上不要设置任何标志,然后逐个检索每个包的值,以避免一次获取许多条目的数据。

当然,只使用需要的标志,尤其是如果您的调用可能包含某些标志。例如,GET_META_DATA就是一个经典的例子:许多应用程序使用它(例如 Play 服务),但是如果您不需要知道元数据信息,请不要请求它。


1
为什么Android的原始代码不能处理这样的请求?难道不应该是它了解限制,将查询分割并正确处理吗?我怎么知道有多少个应用程序,以便我可以决定何时将查询分成多个查询?您能否提供一个示例代码,具有与此代码相同的功能:packageManager.getInstalledPackages(0) - android developer
@androiddeveloper:“没有标记意味着0,不是吗?”-- 应该是这样。“至少有一位用户仍然给我报告了这个异常”--::耸肩::尽可能优雅地失败吧。 “它真的说他们在Android 5.1上修复了吗?”-- 这是我对问题评论#11的解释。 - CommonsWare
所以我对此无能为力...我想知道需要多少应用程序才能导致这种情况。用户肯定没有安装1M个应用程序(如果每个应用程序信息占用1B的话,那就是最大值)...也许我可以自己查询?检查APK路径的文件夹? - android developer
你能猜出可能会引发此问题的应用程序数量吗? - android developer
@androiddeveloper:随口一说?成千上万。也许高达数百个。现在,请提出一个新的SO问题,而不是继续在这里链接评论。 - CommonsWare
显示剩余10条评论

0

我找到了解决这个问题的方法,并在这里发布了相关内容。

简而言之,它通过使用 ADB 找到已安装应用程序的包名称,然后逐个获取每个应用程序的信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接