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.