O que fazer em TransactionTooLargeException


239

Eu tenho um TransactionTooLargeException. Não reproduzível. Nos documentos, ele diz

A transação do Fichário falhou porque era muito grande.

Durante uma chamada de procedimento remoto, os argumentos e o valor de retorno da chamada são transferidos como objetos Parcel armazenados no buffer de transação do Fichário. Se os argumentos ou o valor de retorno forem muito grandes para caber no buffer de transação, a chamada falhará e TransactionTooLargeException será lançada.

...

Há dois resultados possíveis quando uma chamada de procedimento remoto lança TransactionTooLargeException. O cliente não pôde enviar sua solicitação ao serviço (provavelmente se os argumentos fossem muito grandes para caber no buffer da transação) ou o serviço não pôde enviar sua resposta de volta ao cliente (provavelmente se o valor de retorno fosse muito grande para caber no buffer de transação).

...

Então, em algum lugar, estou passando ou recebendo argumentos que excedem algum limite desconhecido. Onde?

O stacktrace não mostra nada de útil:

java.lang.RuntimeException: Adding window failed
at android.view.ViewRootImpl.setView(ViewRootImpl.java:548)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:406)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:320)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:152)
at android.view.Window$LocalWindowManager.addView(Window.java:557)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2897)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4977)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.view.IWindowSession$Stub$Proxy.add(IWindowSession.java:569)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
... 16 more
android.os.TransactionTooLargeException
at android.os.BinderProxy.transact(Native Method)
at android.view.IWindowSession$Stub$Proxy.add(IWindowSession.java:569)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:538)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:406)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:320)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:152)
at android.view.Window$LocalWindowManager.addView(Window.java:557)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2897)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4977)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)

Parece estar relacionado com pontos de vista? Como isso está relacionado à chamada de procedimento remoto?

Talvez seja importante: Versão Android: 4.0.3, Dispositivo: HTC One X


Não. Mas eu não entendi novamente. Tenha o rastreador de erros no aplicativo ao vivo e obtenha-o apenas desta vez em cerca de 3 semanas. Pelo menos, isso não parece acontecer com frequência. Talvez vale a pena abrir um problema no Android embora ...
Ixx

Não tenho uma resposta, mas isso faz com que meu Galaxy S2 seja reinicializado com segurança.
Timmmm 25/09/12

Acabei de ocorrer em um dos meus aplicativos hoje. Isso também aconteceu apenas uma vez e com um Galaxy S3. É interessante que isso só pareça apreender com os dispositivos mais poderosos.
Danwms 26/09/12

essa exceção foi adicionada na API 15, developer.android.com/reference/android/os/… E eu a reproduzi no MapView enquanto percorria o mapa. . até que o gc escreveu que eu não tenho nenhuma memória esquerda (Levei alguns minutos)
meh

O buffer da transação é limitado a 1 MB em todos os dispositivos, e esse buffer envelhece todas as transações. Portanto, quanto mais poderoso o dispositivo, mais transações ele pode realizar simultaneamente, todas consumindo o mesmo buffer de 1 MB. Dito isto, sua resposta não é uma resposta, mas um comentário.
3c71 23/11

Respostas:


158

Encontrei esse problema e descobri que, quando uma quantidade enorme de dados é trocada entre um serviço e um aplicativo (isso envolve a transferência de muitas miniaturas). Na verdade, o tamanho dos dados estava em torno de 500kb e o tamanho do buffer da transação IPC é definido como 1024KB. Não sei por que excedeu o buffer de transação.

Isso também pode ocorrer quando você passa muitos dados por meio de extras intencionais

Quando você receber essa exceção em seu aplicativo, analise seu código.

  1. Você está trocando muitos dados entre seus serviços e aplicativos?
  2. Usando intenções para compartilhar dados enormes (por exemplo, o usuário seleciona um grande número de arquivos do compartilhamento de imprensa de compartilhamento de galeria, os URIs dos arquivos selecionados serão transferidos usando intenções)
  3. recebendo arquivos de bitmap do serviço
  4. aguardando que o android responda com enormes dados (por exemplo, getInstalledApplications () quando o usuário instalou muitos aplicativos)
  5. usando applyBatch () com muitas operações pendentes

Como lidar quando você recebe essa exceção

Se possível, divida a operação grande em pequenos pedaços, por exemplo, em vez de chamar applyBatch () com 1000 operações, chame-a com 100 cada.

Não troque dados enormes (> 1 MB) entre serviços e aplicativos

Eu não sei como fazer isso, mas, Não consulte o Android, que pode retornar dados enormes :-)


1
Suponha que eu esteja verificando se meu .apk está instalado ou não? no momento da instalação ... Estou recebendo a mesma exceção ao verificar meu pacote com.test.installedornot. Meu tamanho de .apk é superior a 9 MB; nesse caso, como vou gerenciar essa exceção?
DJhon 14/05

15
Estou recebendo essa exceção exatamente durante uma chamada para getInstalledApplications. O que pode ser feito para resolver isso?
Stan

1
@ Stan Esta API é comum e usada amplamente em torno do aplicativo Android. Essa exceção realmente me preocupa de alguma forma quando eu uso essa API.
peacepassion

7
Posso confirmar suas conclusões sobre o limite estar em torno de 500 KB, mas é específico para o dispositivo. Em alguns dispositivos, você pode transferir quase todo o 1 MB. Eu também tive essa exceção, então fiz algumas investigações e escrevi um post que pode ser interessante para pessoas com esse problema. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

11
Se você está achando difícil rastrear exatamente qual o estado que está causando a falha, poderá achar o TooLargeTool útil.
Max Spencer

48

Se você precisar investigar qual parcela está causando a falha, considere tentar o TooLargeTool .

(Encontrei isso como um comentário do @Max Spencer sob a resposta aceita e foi útil no meu caso.)


9
Solução mais subestimada. Esta ferramenta ajuda a diminuir as actividades incriminadas
Kedar Paranjape

Esta ferramenta que ajudou a resolver o meu problema. Fácil de instalar e usar.
Carlos

esta ferramenta é para kotlin: / alguma alternativa para java?
maxwellnewage

2
@ maxwellnewage: parece que a versão mais recente (0.2.1, 0.2.0 também) atualmente não está funcionando em aplicativos somente para Java. Eu tive que usar a versão 0.1.6 e funcionou bem então
heisenberg

Usando essa ferramenta, detecto que estava usando um tamanho enorme de pacotes nos meus fragmentos. O que fiz foi extrair o argumento do pacote e bundle.clear()
limpá-lo

41

Essa não é uma resposta definitiva, mas pode esclarecer as causas de um TransactionTooLargeExceptione ajudar a identificar o problema.

Embora a maioria das respostas se refira a grandes quantidades de dados transferidos, vejo essa exceção sendo lançada acidentalmente após rolagem e zoom pesados ​​e abrindo repetidamente um menu giratório do ActionBar. O acidente ocorre ao tocar na barra de ação. (este é um aplicativo de mapeamento personalizado)

Os únicos dados transmitidos parecem ser toques do "Input Dispatcher" para o aplicativo. Eu acho que isso não pode chegar a 1 MB no "Transaction Buffer".

Meu aplicativo está sendo executado em um dispositivo quad core de 1,6 GHz e usa 3 threads para levantamento de peso, mantendo um núcleo livre para o thread da interface do usuário. Além disso, o aplicativo usa o android: largeHeap, tem 10 mb de heap não utilizado restante e tem 100 mb de espaço restante para aumentar o heap. Então, eu não diria que é uma questão de recursos.

A falha é sempre imediatamente precedida por estas linhas:

W/InputDispatcher( 2271): channel ~ Consumer closed input channel or an error occurred.  events=0x9
E/InputDispatcher( 2271): channel ~ Channel is unrecoverably broken and will be disposed!
E/JavaBinder(28182): !!! FAILED BINDER TRANSACTION !!!

Que não são necessariamente impressos nessa ordem, mas (tanto quanto eu verifiquei) acontecem no mesmo milissegundo.

E o próprio rastreamento de pilha, para maior clareza, é o mesmo da pergunta:

E/AndroidRuntime(28182): java.lang.RuntimeException: Adding window failed
..
E/AndroidRuntime(28182): Caused by: android.os.TransactionTooLargeException

Investigando o código fonte do android, encontramos as seguintes linhas:

frameworks / base / core / jni / android_util_Binder.cpp:

case FAILED_TRANSACTION:
    ALOGE("!!! FAILED BINDER TRANSACTION !!!");
    // TransactionTooLargeException is a checked exception, only throw from certain methods.
    // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
    //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
    //        for other reasons also, such as if the transaction is malformed or
    //        refers to an FD that has been closed.  We should change the driver
    //        to enable us to distinguish these cases in the future.
    jniThrowException(env, canThrowRemoteException
            ? "android/os/TransactionTooLargeException"
                    : "java/lang/RuntimeException", NULL);

Para mim, parece que estou atingindo esse recurso não documentado, em que a transação falha por outros motivos que não sejam uma transação muito grande. Eles deveriam ter nomeado TransactionTooLargeOrAnotherReasonException.

No momento, não resolvi o problema, mas, se encontrar algo útil, atualizarei esta resposta.

update: descobriu-se que meu código vazou alguns descritores de arquivo, cujo número é maximizado no linux (normalmente 1024), e isso parece ter desencadeado a exceção. Afinal, era uma questão de recursos. Eu verifiquei isso abrindo /dev/zero1024 vezes, o que resultou em todos os tipos de exceções estranhas nas ações relacionadas à interface do usuário, incluindo a exceção acima, e até mesmo alguns SIGSEGVs. Aparentemente, a falha ao abrir um arquivo / soquete não é algo que é tratado / relatado de maneira muito limpa em todo o Android.


36

o TransactionTooLargeException está nos atormentando há cerca de 4 meses e finalmente resolvemos o problema!

O que estava acontecendo era que estamos usando FragmentStatePagerAdapterumViewPager . O usuário paginaria e criaria mais de 100 fragmentos (é um aplicativo de leitura).

Embora tenhamos gerenciado os fragmentos corretamente destroyItem(), na implementação do Android FragmentStatePagerAdapterhá um bug, onde ele mantinha uma referência à seguinte lista:

private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();

E quando o Android FragmentStatePagerAdaptertentar salvar o estado, ele chamará a função

@Override
public Parcelable saveState() {
    Bundle state = null;
    if (mSavedState.size() > 0) {
        state = new Bundle();
        Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
        mSavedState.toArray(fss);
        state.putParcelableArray("states", fss);
    }
    for (int i=0; i<mFragments.size(); i++) {
        Fragment f = mFragments.get(i);
        if (f != null && f.isAdded()) {
            if (state == null) {
                state = new Bundle();
            }
            String key = "f" + i;
            mFragmentManager.putFragment(state, key, f);
        }
    }
    return state;
}

Como você pode ver, mesmo se você gerenciar adequadamente os fragmentos da FragmentStatePagerAdaptersubclasse, a classe base ainda armazenará um Fragment.SavedStatepara cada fragmento criado. O TransactionTooLargeExceptionocorreria quando essa matriz foi despejado para umparcelableArray eo SO não gostaria que 100+ itens.

Portanto, a correção para nós era substituir o saveState()método e não armazenar nada "states".

@Override
public Parcelable saveState() {
    Bundle bundle = (Bundle) super.saveState();
    bundle.putParcelableArray("states", null); // Never maintain any states from the base class, just null it out
    return bundle;
}

melhor resposta para mim. Muito obrigado
pavel

De todas as possibilidades para isso, para mim isso parecia apenas uma opção. O que é armazenado no estado que permitiria atingir esse tamanho? Se o estado é tão necessário que exista código para salvá-lo, como isso não causa outros problemas? Graças
Kenny

A mesma pergunta que @Kenny
Mr. Rabbit

1
@Override public Parcelable saveState() { Bundle bundle = (Bundle) super.saveState(); if (bundle != null) { Parcelable[] states = bundle.getParcelableArray("states"); // Subset only last 3 states if (states != null) states = Arrays.copyOfRange(states, states.length > 3 ? states.length - 3 : 0, states.length - 1); bundle.putParcelableArray("states", states); } else bundle = new Bundle(); return bundle; }
Ramy Sabry

Acabei de deixar apenas os últimos 3 estados, verifique o código acima.
Ramy Sabry

20

Para aqueles que se decepcionaram amargamente em busca de respostas sobre por que o TransactionTooLargeException aparece, tente verificar quanta informação você salva no estado da instância.

No compile / targetSdkVersion <= 23, temos apenas um aviso interno sobre o tamanho grande do estado salvo, mas nada falha:

E/ActivityThread: App sent too much data in instance state, so it was ignored
    android.os.TransactionTooLargeException: data parcel size 713856 bytes
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(Binder.java:615)
    at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3604)
    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3729)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6044)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Mas em compile / targetSdkVersion> = 24, temos uma verdadeira falha de RuntimeException neste caso:

java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 713860 bytes
    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3737)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6044)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
 Caused by: android.os.TransactionTooLargeException: data parcel size 713860 bytes
   at android.os.BinderProxy.transactNative(Native Method)
   at android.os.BinderProxy.transact(Binder.java:615)
   at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3604)
   at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3729)
   at android.os.Handler.handleCallback(Handler.java:751) 
   at android.os.Handler.dispatchMessage(Handler.java:95) 
   at android.os.Looper.loop(Looper.java:154) 
   at android.app.ActivityThread.main(ActivityThread.java:6044) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755) 

O que fazer?

Salve os dados no banco de dados local e mantenha apenas os IDs no estado da instância que você pode usar para recuperar esses dados.


é possível mantê-lo como parâmetro global e usá-lo mais tarde?
Jitendra ramoliya

@Jitendraramoliya Sim, você pode. É exatamente isso que eu quero dizer.
Yazon2006

13

Essa exceção geralmente é lançada quando o aplicativo está sendo enviado para o segundo plano.

Por isso, decidi usar o método Fragment de dados para contornar completamente o onSavedInstanceStaeciclo de vida. Minha solução também lida com estados complexos de instância e libera memória o mais rápido possível.

Primeiro, criei um Farcement simples para armazenar os dados:

package info.peakapps.peaksdk.logic;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;

/**
 * A neat trick to avoid TransactionTooLargeException while saving our instance state
 */

public class SavedInstanceFragment extends Fragment {

    private static final String TAG = "SavedInstanceFragment";
    private Bundle mInstanceBundle = null;

    public SavedInstanceFragment() { // This will only be called once be cause of setRetainInstance()
        super();
        setRetainInstance( true );
    }

    public SavedInstanceFragment pushData( Bundle instanceState )
    {
        if ( this.mInstanceBundle == null ) {
            this.mInstanceBundle = instanceState;
        }
        else
        {
            this.mInstanceBundle.putAll( instanceState );
        }
        return this;
    }

    public Bundle popData()
    {
        Bundle out = this.mInstanceBundle;
        this.mInstanceBundle = null;
        return out;
    }

    public static final SavedInstanceFragment getInstance(FragmentManager fragmentManager )
    {
        SavedInstanceFragment out = (SavedInstanceFragment) fragmentManager.findFragmentByTag( TAG );

        if ( out == null )
        {
            out = new SavedInstanceFragment();
            fragmentManager.beginTransaction().add( out, TAG ).commit();
        }
        return out;
    }
}

Então, na minha Atividade principal, contorno completamente o ciclo de instâncias salvas e adio a respoinsibilidade ao meu Fragmento de dados. Não há necessidade de usar isso nos próprios fragmentos, pois seu estado é adicionado ao estado da atividade automaticamente):

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    SavedInstanceFragment.getInstance( getFragmentManager() ).pushData( (Bundle) outState.clone() );
    outState.clear(); // We don't want a TransactionTooLargeException, so we handle things via the SavedInstanceFragment
}

O que resta é simplesmente exibir a instância salva:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(SavedInstanceFragment.getInstance(getFragmentManager()).popData());
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState( SavedInstanceFragment.getInstance( getFragmentManager() ).popData() );
}

Detalhes completos: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
O que acontece se a atividade é destruída quando o aplicativo está em segundo plano e você tenta a atividade em primeiro plano?
Master Disaster

@MasterDisaster nesse caso, nenhum estado é salvo porque o processo que contém o fragmento da instância morreu.
Vaiden

Direitos, portanto, este caso funciona apenas com alterações de configuração.
Master Disaster

Funciona sempre que o sistema operacional é acionado onSavedState(), o que acontece em muitos casos. A mudança na configuração é uma. Trocar de aplicativo e ir para segundo plano são outra. E tem mais.
quer

1
Eu acho que essa solução deve ser expandida para salvar dados de diferentes fontes. Provavelmente um HashMap com tags como chaves e bundles como valores .. #
CoolMind 20/12/18

11

Não há uma causa específica desse problema. Para mim, na minha classe Fragment, eu estava fazendo isso:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View rootView = inflater.inflate(R.layout.snacks_layout, container); //<-- notice the absence of the false argument
    return rootView;
}

em vez disso:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);

9

É importante entender que o buffer da transação é limitado a 1 MB, independentemente dos recursos ou aplicativos do dispositivo. Esse buffer é usado com todas as chamadas de API que você faz e é compartilhado entre todas as transações que um aplicativo está executando no momento.

Eu acredito que ele também contém algum objeto específico, como parcelas e coisas do tipo (Parcel.obtain()), por isso é importante sempre combinar todos obtain()com a recycle().

Esse erro pode ocorrer facilmente em chamadas de API que retornam muitos dados, mesmo que os dados retornados sejam menores que 1 MB (se outras transações ainda estiverem em execução).

Por exemplo, a PackageManager.getInstalledApplication()chamada retorna uma lista de todos os aplicativos instalados. A adição de sinalizadores específicos permite recuperar muitos dados extras. Se o fizer, é provável que falhe, por isso é recomendável não recuperar nenhum dado extra e recuperá-lo por aplicativo.

No entanto, a chamada ainda pode falhar, por isso é importante cercá-la com um catche poder tentar novamente, se necessário.

Até onde eu sei, não há solução alternativa para esse problema, exceto tentar novamente e garantir o mínimo de informações possível.


Obrigado pela informação, esse buffer é compartilhado entre todas as transações dentro do aplicativo!
Artem Mostyaev

1
Se esse foi o caso, por que eu só estou usando telefones Android 4.4 e em nenhum outro lugar? Eu acho que é mais um bug no 4.4 que eu sou incapaz de descobrir o porquê.
JPM

9

Eu também recebi essa exceção em um Samsung S3. Eu suspeito que 2 causas principais,

  1. você tem bitmaps que carregam e consomem muita memória, use downsizing
  2. Você tem alguns drawables ausentes nas pastas drawable-_dpi, o android os procura no drawable e os redimensiona, fazendo com que o seu setContentView salte repentinamente e use muita memória.

Use o DDMS e observe sua pilha enquanto reproduz seu aplicativo, que fornecerá algumas indicações sobre qual setcontentview está criando o problema.

Copiei todos os drawables em todas as pastas para se livrar do problema 2.

Problema resolvido.


As exceções de memória / bitmap geralmente parecem diferentes. Já vi muitos deles testando com o Android 2.x - 4.xe as exceções sempre parecem diferentes. Mas, quem sabe, talvez isso também esteja relacionado, mas específico às versões 4.x.
Ixx

10
É apenas uma exceção terrível, no que diz respeito à informação, pois não dá nenhuma pista sobre de onde vem o problema.
Ixx

Eu acho que os poucos dias acabaram, quais são suas descobertas?
Denny

8

Adicione isso à sua atividade

@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
    super.onSaveInstanceState(oldInstanceState);
    oldInstanceState.clear();
}

Funciona para mim espero que também ajude você


7
Eu acho que é a dica mais prejudicial. Por que devemos limpar os dados nos quais queremos restaurar onCreate()?
CoolMind

2
As implicações deste código é que você acaba não salvar seu estado exemplo ...
Justin

4

Então, para nós, estávamos tentando enviar um objeto muito grande por meio de nossa interface AIDL para um serviço remoto. O tamanho da transação não pode exceder 1 MB. A solicitação é dividida em partes separadas de 512 KB e enviada uma por vez pela interface. Uma solução brutal que eu conheço, mas ei - seu Android :(


4

Você limpou seu InstanceState antigo do método onSaveInstanceState e ele funcionará bem. Estou usando o FragmentStatePagerAdapter para o meu viewpager, por isso mantenho abaixo o método Override na atividade pai para limpar InstanceState.

@Override
protected void onSaveInstanceState(Bundle InstanceState) {
             super.onSaveInstanceState(InstanceState);
             InstanceState.clear();
}

Encontrei esta solução a partir daqui android.os.TransactionTooLargeException no Nougat


Muito obrigado.
Andrain

Obrigado sua resposta me ajude
Jatin Patel

3

Recentemente, também encontrei um caso interessante ao trabalhar com o provedor de contatos do Android .

Eu precisava carregar fotos dos contatos do banco de dados de contatos internos e, de acordo com a arquitetura do sistema, todos esses dados são entregues por meio de consultas ao provedor de contatos.

Como funciona como um aplicativo separado - todos os tipos de transferência de dados são realizados usando o mecanismo Binder e, portanto, o buffer do Binder entra em jogo aqui.

Meu principal erro foi não fechar os Cursordados com blob obtidos do provedor de contatos, de modo que a memória alocada para o provedor aumentasse e isso inflasse o buffer do fichário até que eu recebesse toneladas de !!!FAILED BINDER TRANSACTION!!!mensagens na saída do LogCat.

Portanto, a idéia principal é que, quando você trabalha com provedores de conteúdo externos e recebe Cursors deles, sempre o fecha quando terminar de trabalhar com eles.


3

Eu enfrentei o mesmo problema quando tentei enviar bitmap via Intent e, ao mesmo tempo, desdobrei o aplicativo.

Como é descrito neste artigo, insira a descrição do link aqui , acontece quando uma Atividade está em processo de parada, o que significa que a Atividade estava tentando enviar seus Pacotes de estado salvos para o sistema operacional do sistema para manter a segurança para restauração posteriormente (após uma alteração na configuração ou processar a morte), mas um ou mais pacotes enviados foram muito grandes.

Eu o resolvi via hack substituindo onSaveInstanceState na minha atividade:

@Override
protected void onSaveInstanceState(Bundle outState) {
    // super.onSaveInstanceState(outState);
}

e comentar chamar super. É um truque sujo, mas está funcionando perfeitamente. O bitmap foi enviado com sucesso sem falhas. Espero que isso ajude alguém.


2

No meu caso, recebo TransactionTooLargeException como uma falha secundária depois que a biblioteca nativa travou com o SIGSEGV. A falha da biblioteca nativa não é relatada, por isso só recebo TransactionTooLargeException.


2

Eu peguei isso no meu syncadapter ao tentar inserir em massa um grande ContentValues ​​[]. Decidi corrigi-lo da seguinte maneira:

try {
    count = provider.bulkInsert(uri, contentValueses);
} catch (TransactionTooLarge e) {
    int half = contentValueses.length/2;
    count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, 0, half));
    count += provider.bulkInsert(uri, Arrays.copyOfRange(contentValueses, half, contentValueses.length));
}

2
E se o outro falhar? Você precisa dividir mais, usando um loop. Existe talvez uma maneira de obter o tamanho da transação e obter o tamanho máximo da transação?
desenvolvedor android

2

Para mim, também foi o FragmentStatePagerAdapter, no entanto, a substituição saveState()não funcionou. Aqui está como eu o consertei:

Ao chamar o FragmentStatePagerAdapterconstrutor, mantenha uma lista separada de fragmentos dentro da classe e adicione um método para remover os fragmentos:

class PagerAdapter extends FragmentStatePagerAdapter {
    ArrayList<Fragment> items;

    PagerAdapter(ArrayList<Fragment> frags) {
        super(getFragmentManager()); //or getChildFragmentManager() or getSupportFragmentManager()
        this.items = new ArrayList<>();
        this.items.addAll(frags);
    }

    public void removeFragments() {
        Iterator<Fragment> iter = items.iterator();

        while (iter.hasNext()) {
            Fragment item = iter.next();
                getFragmentManager().beginTransaction().remove(item).commit();
                iter.remove();
            }
            notifyDataSetChanged();
        }
    }
    //...getItem() and etc methods...
}

Em seguida, no Activity, salve a ViewPagerposição e chame adapter.removeFragments()no onSaveInstanceState()método substituído :

private int pagerPosition;

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    //save other view state here
    pagerPosition = mViewPager.getCurrentItem();
    adapter.removeFragments();
}

Por fim, no onResume()método substituído , instancie novamente o adaptador, se não estiver null. (Se estiver null, então ele Activityestá sendo aberto pela primeira vez ou após o aplicativo ter sido eliminado pelo Android, no qual onCreatefará a criação do adaptador.)

@Override
public void onResume() {
    super.onResume();
    if (adapter != null) {
        adapter = new PagerAdapter(frags);
        mViewPager.setAdapter(adapter);
        mViewPager.setCurrentItem(currentTabPosition);
    }
}

1

Certifique-se de não inserir dados de objeto Intent de tamanho grande. No meu caso, estava adicionando o tamanho da String 500k e iniciando outra atividade. Sempre falhava com essa exceção. Evitei compartilhar dados entre as atividades usando variáveis ​​estáticas das atividades - você não precisa enviá-las para o Intent e depois retirá-las.

O que eu tinha:

String html = new String();//some string of 500K data.
Intent intent = new Intent(MainActivity.this, PageWebView.class);
//this is workaround - I just set static variable and then access it from another    activity.
MainActivity.htmlBody = timelineDb.getHTMLBodyForTweet(tweet);
//This line was present and it actually failed with the same exception you had.
//intent.putExtra("com.gladimdim.offtie.webview", html);

Esta é uma péssima ideia. Usar variáveis ​​estáticas dessa maneira é um cheiro de código. Além disso, tente executar esse código com o sinalizador "Não manter atividades" e pressione home depois de navegar para o PageWebView. Depois de abrir sua atividade novamente, você provavelmente sofrerá uma falha, pois o MainActivity estará 100% morto com todas as suas variáveis.
Kyrylo Zapylaiev

1

Quando estou lidando com o WebViewno meu aplicativo, isso acontece. Eu acho que está relacionado aos addViewrecursos da interface do usuário. No meu aplicativo, adiciono algum código WebViewActivitycomo este abaixo e ele funciona ok:

@Override
protected void onDestroy() {
    if (mWebView != null) {
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);  
        mWebView.removeAllViews();  
        mWebView.destroy();
    }
    super.onDestroy();
}

1

Encontrei a causa raiz disso (obtivemos "falha na adição de janela" e vazamento no descritor de arquivos, como diz o mvds).

Há um erro no BitmapFactory.decodeFileDescriptor()Android 4.4. Só ocorre quando inPurgeablee inInputShareablede BitmapOptionsestão definidos como true. Isso causa muitos problemas em muitos lugares e interage com arquivos.

Observe que o método também é chamado de MediaStore.Images.Thumbnails.getThumbnail().

O Universal Image Loader é afetado por esse problema. Picasso e Glide parecem não ser afetados. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

Essa linha de código no método writeToParcel (Parcel dest, int flags) me ajudou a me livrar do TransactionTooLargeException.

dest=Parcel.obtain(); 

Após esse código apenas, estou gravando todos os dados no objeto de parcela, ou seja, dest.writeInt () etc.


1

Tente usar EventBusou ContentProvidergostar da solução.

Se você estiver no mesmo processo (normalmente todas as suas atividades estariam), tente usar EventBus, pois a troca de dados em processo NÃO precisa de um buffer, para que você não precise se preocupar com o tamanho excessivo dos dados. (Você pode simplesmente usar a chamada de método para transmitir dados, e EventBus oculta as coisas feias) Aqui está o detalhe:

// one side
startActivity(intentNotTooLarge);
EventBus.getDefault().post(new FooEvent(theHugeData));

// the other side
@Subscribe public void handleData(FooEvent event) { /* get and handle data */ }

Se os dois lados do Intent não estiverem no mesmo processo, tente um pouco ContentProvider.


Consulte TransactionTooLargeException

A transação do Fichário falhou porque era muito grande.

Durante uma chamada de procedimento remoto, os argumentos e o valor de retorno da chamada são transferidos como objetos Parcel armazenados no buffer de transação do Fichário. Se os argumentos ou o valor de retorno forem muito grandes para caber no buffer de transação, a chamada falhará e TransactionTooLargeException será lançada.


1

Recebi uma TransactionTooLargeException de um erro do Stackoverflow em um teste do Android Espresso. Encontrei o rastreamento da pilha de erros stackoverflow nos logs quando removi o filtro Logcat do meu aplicativo.

Suponho que o Espresso tenha causado a TransactionTooLargeException ao tentar manipular um rastreamento de pilha de exceção realmente grande.


1

Pode-se usar:

android:largeHeap="true"

no Manifesto do Android, na tag do aplicativo.

Isso resolveu o problema no meu caso!


No meu caso (por causa de chamar onSaveInstantStateatividades / fragmentos e salvar grandes listas), não ajudou.
CoolMind 30/01/19

1
Isso é considerado uma má prática, porque você não está lidando com o motivo pelo qual muitos dados são salvos em primeiro lugar. Em dispositivos mais recentes, ele trava o aplicativo e o limite é muito menor (256 KB). Investigue por que você está armazenando lotes primeiro e reduza-o.
Tim Kist

1

Também estava enfrentando esse problema nos dados de bitmap que passavam de uma atividade para outra, mas resolvi fazer meus dados como dados estáticos e isso está funcionando perfeitamente para mim

Em atividade primeiro:

public static Bitmap bitmap_image;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_first);
   bitmap_image=mybitmap;
}

e na segunda atividade:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
   Bitmap mybitmap=first.bitmap_image;
}

1

Isso estava acontecendo no meu aplicativo porque eu estava passando uma lista de resultados de pesquisa nos argumentos de um fragmento, atribuindo essa lista a uma propriedade do fragmento - que na verdade é uma referência ao mesmo local na memória apontado pelos argumentos do fragmento - e adicionando novos itens para a lista, que também alteraram o tamanho dos argumentos do fragmento. Quando a atividade é suspensa, a classe de fragmento base tenta salvar os argumentos do fragmento em onSaveInstanceState, que trava se os argumentos forem maiores que 1 MB. Por exemplo:

private ArrayList<SearchResult> mSearchResults;

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (getArguments() != null && getArguments().getSerializable("SearchResults") != null) {
        mSearchResults = (ArrayList) getArguments().getSerializable("SearchResults");
    }
}

private void onSearchResultsObtained(ArrayList<SearchResult> pSearchResults) {

    // Because mSearchResults points to the same location in memory as the fragment's arguments
    // this will also increase the size of the arguments!
    mSearchResults.addAll(pSearchResults);
}

A solução mais fácil nesse caso foi atribuir uma cópia da lista à propriedade do fragmento em vez de atribuir uma referência:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (getArguments() != null && getArguments().getSerializable("SearchResults") != null) {

        // Copy value of array instead of reference
        mSearchResults = new ArrayList((ArrayList) getArguments().getSerializable("SearchResults"));
    }
}

Uma solução ainda melhor seria não passar tantos dados nos argumentos.

Eu provavelmente nunca teria encontrado isso sem a ajuda desta resposta e do TooLargeTool .


1

Eu também vivi TransactionTooLargeException. Em primeiro lugar, trabalhei para entender onde isso ocorre. Eu sei a razão pela qual isso ocorre. Todos nós sabemos por causa do grande conteúdo. Meu problema era assim e eu resolvi. Talvez essa solução possa ser útil para qualquer pessoa. Eu tenho um aplicativo que obtém conteúdo da API. Estou obtendo resultado da API na primeira tela e enviando para a segunda tela. Posso enviar esse conteúdo para a segunda tela com êxito. Após a segunda tela, se eu quiser ir para a terceira tela, essa exceção ocorre. Cada uma das minhas telas é criada a partir do fragmento. Percebi isso quando saio da segunda tela. Ele salva o conteúdo do pacote. se esse conteúdo for muito grande, essa exceção acontece. Minha solução é depois que eu recebo o conteúdo do pacote e limpo.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }

Enquanto isso estiver correto (que vergonha para mim, eu entendi o mesmo depois de um ano), como o fragmento fará se recriar (rotação da tela)?
CoolMind 20/02

0

Uma solução seria o aplicativo gravar o ArrayList (ou qualquer outro objeto que esteja causando o problema) no sistema de arquivos, passar uma referência a esse arquivo (por exemplo, nome do arquivo / caminho) por meio do Intent para o IntentService e deixar o IntentService recuperar o conteúdo do arquivo e convertê-lo novamente em um ArrayList.

Quando o IntentService concluir o arquivo, ele deve ser excluído ou passado as instruções ao aplicativo por meio de um Broadcast Local para excluir o arquivo que ele criou (retornando a mesma referência de arquivo que foi fornecida a ele).

Para mais informações, consulte minha resposta para esse problema relacionado .


0

Como Intenções, Provedores de Conteúdo, Messenger, todos os serviços do sistema, como Telefone, Vibrador etc., utilizam o provedor de infraestrutura IPC da Binder. Além disso, os retornos de chamada do ciclo de vida da atividade também usam essa infraestrutura.

1 MB é o limite geral de todas as transações do fichário executadas no sistema em um momento específico.

Caso ocorram muitas transações quando a intenção é enviada, ela poderá falhar, mesmo que os dados extras não sejam grandes. http://codetheory.in/an-overview-of-android-binder-framework/


0

Em tantos lugares onde a TransactionTooLargeException pode acontecer - aqui está mais uma novidade no Android 8 - uma falha quando alguém simplesmente começa a digitar um EditText se o conteúdo for muito grande.

Está relacionado ao AutoFillManager (novo na API 26) e ao seguinte código em StartSessionLocked():

    mSessionId = mService.startSession(mContext.getActivityToken(),
            mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
            mCallback != null, flags, mContext.getOpPackageName());

Se bem entendi, isso chama o serviço de preenchimento automático - passando o AutofillManagerClient no fichário. E quando o EditText tem muito conteúdo, parece causar o TTLE.

Algumas coisas podem atenuá-lo (ou fiz como eu estava testando de qualquer maneira): Adicione android:importantForAutofill="noExcludeDescendants"a declaração de layout xml do EditText. Ou no código:

EditText et = myView.findViewById(R.id.scriptEditTextView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    et.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}

Uma segunda solução terrível e terrível também pode ser substituir os métodos performClick()e onWindowFocusChanged()para detectar o erro em uma subclasse do TextEdit. Mas eu não acho isso realmente sábio ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.