TransactionTooLargeEception при попытке получить список установленных приложений
Как часть моего приложения, я получаю список приложений, установленных на устройстве, с помощью ApplicationPackageManager.getInstalledApplications, но для некоторых пользователей я получаю отчеты о сбоях, говорящие, что
TransactionTooLargeException в android.osBinderProxy.tranasact(собственный метод)
Кто-нибудь может подумать, почему я это получу?
Ответы
Ответ 1
Это исключение трудно воспроизвести при обычных обстоятельствах. Вы получите это исключение, если память IPC исчерпана при передаче данных. Это может произойти в обоих случаях, когда служба пытается разместить данные для клиента или клиент отправляет данные для обслуживания. Скорее всего, некоторые из ваших пользователей могли установить огромное количество приложений, что приводит к размеру данных, превышающему 1 МБ (который является размером буфера IPC).
Я боюсь, в этом случае вы не будете делать ничего лучше. Но если вы делаете что-то вроде applyBatch, вы можете разделить одну большую транзакцию на несколько меньших транзакций.
Также посмотрите на этот поток Что делать с TransactionTooLargeException
Ответ 2
Я обнаружил, что это было решено на Android 5.1 (доказательство здесь, поиск "Fix package manager TransactionTooLargeExceptions" ), как сообщалось в нескольких местах:
Тем не менее, я хотел решить это для pre-5.1, поэтому я придумал решение (и предложил Google разместить его в библиотеке поддержки здесь). Вот короткая кодовая версия того, что я предложил:
public static List<PackageInfo> getInstalledPackages(Context context,int flags)
{
final PackageManager pm=context.getPackageManager();
try
{
return pm.getInstalledPackages(flags);
}
catch(Exception ignored)
{
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
Process process;
List<PackageInfo> result=new ArrayList<>();
BufferedReader bufferedReader=null;
try
{
process=Runtime.getRuntime().exec("pm list packages");
bufferedReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line=bufferedReader.readLine())!=null)
{
final String packageName=line.substring(line.indexOf(':')+1);
final PackageInfo packageInfo=pm.getPackageInfo(packageName,flags);
result.add(packageInfo);
}
process.waitFor();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(bufferedReader!=null)
try
{
bufferedReader.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return result;
}
Что он делает, чтобы сначала использовать официальный путь, а затем, если не удалось, он извлекает имена пакетов с помощью команды ADB и получает информацию о каждом из приложений один за другим.
Это намного медленнее, чем официальное, но для меня это не грохнуло. Я тестировал его на эмуляторах Android (включая 2.3.x до 5.0.x, включая), а также на реальных устройствах.
Время, которое потребовалось моему устройству (Galaxy S3 с пользовательским слотом Android 5.1), составляет 1375-2012 мс (всего 197 приложений) по сравнению с 37-65 мс, используя официальный способ.
EDIT: люди заявляют здесь, что он не исправлен на Android 5.1. Надеюсь, что это исправлено на Android 6.