Que faire sur TransactionTooLargeException

239

J'en ai un TransactionTooLargeException. Non reproduisible. Dans les documents, il est dit

La transaction Binder a échoué car elle était trop importante.

Lors d'un appel de procédure distante, les arguments et la valeur de retour de l'appel sont transférés en tant qu'objets Parcel stockés dans le tampon de transaction Binder. Si les arguments ou la valeur de retour sont trop grands pour tenir dans le tampon de transaction, l'appel échouera et TransactionTooLargeException sera levée.

...

Il existe deux résultats possibles lorsqu'un appel de procédure distante lève TransactionTooLargeException. Soit le client n'a pas pu envoyer sa demande au service (très probablement si les arguments étaient trop volumineux pour tenir dans le tampon de transaction), soit le service n'a pas pu renvoyer sa réponse au client (très probablement si la valeur de retour était trop grand pour tenir dans le tampon de transaction).

...

Quelque part, je passe ou reçois des arguments qui dépassent une limite inconnue. Où?

Le stacktrace ne montre rien d'utile:

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)

Cela semble lié aux vues? Quel est le lien avec l'appel de procédure à distance?

Peut-être important: version Android: 4.0.3, appareil: HTC One X

Ixx
la source
Non, mais je ne l'ai pas encore compris. Avoir un tracker d'erreur dans l'application en direct et ne l'obtenir qu'une seule fois en environ 3 semaines. Au moins, cela ne semble pas se produire fréquemment. Peut-être vaut-il la peine d'ouvrir un problème sur Android bien que ...
Ixx
Je n'ai pas de réponse, mais cela provoque une réinitialisation matérielle de mon Galaxy S2.
Timmmm
Je viens d'avoir ce phénomène dans l'une de mes applications aujourd'hui. Cela n'est également arrivé qu'une seule fois avec un Galaxy S3. Il est intéressant de noter que cela ne semble appréhender qu'avec les appareils les plus puissants.
danwms
cette exception a été ajoutée dans l'API 15, developer.android.com/reference/android/os/… Et je l'ai reproduite dans MapView en faisant défiler la carte. jusqu'à ce que le GC écrit qu'il ne me reste plus de mémoire. (Cela m'a pris quelques minutes)
meh
Le tampon de transaction est limité à 1 Mo sur tous les appareils, et ce tampon ancien chaque transaction. Ainsi, plus l'appareil est puissant, plus il peut effectuer de transactions simultanément, toutes consommant le même tampon de 1 Mo. Cela dit, votre réponse n'est pas une réponse mais un commentaire.
3c71

Réponses:

158

J'ai rencontré ce problème et j'ai constaté que lorsqu'il y avait une énorme quantité de données échangées entre un service et une application (cela implique le transfert de nombreuses miniatures). En fait, la taille des données était d'environ 500 Ko et la taille du tampon de transaction IPC est définie sur 1024 Ko. Je ne sais pas pourquoi il a dépassé le tampon de transaction.

Cela peut également se produire lorsque vous passez beaucoup de données via des extras d'intention

Lorsque vous obtenez cette exception dans votre application, veuillez analyser votre code.

  1. Échangez-vous beaucoup de données entre vos services et votre application?
  2. Utilisation d'intentions pour partager d'énormes données (par exemple, l'utilisateur sélectionne un grand nombre de fichiers dans le partage de presse de partage de galerie, les URI des fichiers sélectionnés seront transférés à l'aide d'intentions)
  3. recevoir des fichiers bitmap du service
  4. en attendant qu'Android réponde avec d'énormes données (par exemple, getInstalledApplications () lorsque l'utilisateur a installé beaucoup d'applications)
  5. en utilisant applyBatch () avec beaucoup d'opérations en attente

Comment gérer lorsque vous obtenez cette exception

Si possible, divisez la grande opération en petits morceaux, par exemple, au lieu d'appeler applyBatch () avec 1000 opérations, appelez-la avec 100 chacune.

N'échangez pas de données volumineuses (> 1 Mo) entre les services et l'application

Je ne sais pas comment faire cela, mais ne demandez pas à Android, qui peut renvoyer d'énormes données :-)

Durairaj Packirisamy
la source
1
Supposons que je vérifie que mon .apk est installé ou non? au moment de l'installation ... J'obtiens la même exception lors de la vérification de mon package com.test.installedornot.My.
DJhon
15
Je reçois cette exception exactement lors d'un appel à getInstalledApplications. Que peut-on faire pour résoudre ce problème?
Stan
1
@Stan Cette API est courante et largement utilisée dans les applications Android. Cette exception m'inquiète vraiment d'une manière ou d'une autre lorsque j'utilise cette API.
peacepassion
7
Je peux confirmer vos conclusions sur la limite se situant aux alentours de 500 Ko, mais c'est spécifique à l'appareil, sur certains appareils, vous pouvez transférer presque tout le 1 Mo. J'ai également eu cette exception, j'ai donc fait quelques recherches et j'ai écrit un article qui pourrait être intéressant à lire pour les personnes ayant ce problème. nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic
11
Si vous avez du mal à trouver exactement quel état est à l'origine de votre crash, vous trouverez peut-être TooLargeTool utile.
Max Spencer
48

Si vous devez rechercher la parcelle à l'origine de votre incident , vous devriez envisager d'essayer TooLargeTool .

(J'ai trouvé cela comme un commentaire de @Max Spencer sous la réponse acceptée et cela a été utile dans mon cas.)

sulai
la source
9
Solution la plus sous-estimée. Cet outil vous aide à réduire les activités incriminées
Kedar Paranjape
Cet outil qui a aidé à résoudre mon problème. Facile à installer et à utiliser.
Carlos
cet outil est pour kotlin: / toute alternative pour java?
maxwellnewage
2
@maxwellnewage: il semble que la dernière version (0.2.1, 0.2.0 aussi) ne fonctionne pas actuellement dans les applications Java uniquement. J'ai dû utiliser la version 0.1.6 et cela a bien fonctionné alors
heisenberg
En utilisant cet outil, je détecte que j'utilisais une taille énorme de faisceaux dans mes fragments. Ce que j'ai fait est d'extraire l'argument du paquet et de le nettoyer en utilisantbundle.clear()
EJ Chathuranga
41

Ce n'est pas une réponse définitive, mais cela peut éclairer les causes d'un TransactionTooLargeExceptionproblème et aider à identifier le problème.

Bien que la plupart des réponses se réfèrent à de grandes quantités de données transférées, je vois cette exception être levée accidentellement après un défilement et un zoom intensifs et l'ouverture répétée d'un menu spinner ActionBar. Le crash se produit en appuyant sur la barre d'action. (il s'agit d'une application de cartographie personnalisée)

Les seules données transmises semblent être des contacts du «répartiteur d'entrée» à l'application. Je pense que cela ne peut raisonnablement pas se situer à près de 1 Mo dans le "tampon de transaction".

Mon application fonctionne sur un appareil quadricœur à 1,6 GHz et utilise 3 threads pour le levage lourd, en gardant un core libre pour le thread d'interface utilisateur. En outre, l'application utilise android: largeHeap, dispose de 10 Mo de tas inutilisés et de 100 Mo d'espace disponible pour agrandir le tas. Je ne dirais donc pas que c'est un problème de ressources.

Le crash est toujours immédiatement précédé de ces lignes:

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 !!!

Qui ne sont pas nécessairement imprimés dans cet ordre, mais (pour autant que je vérifie) se produisent sur la même milliseconde.

Et la trace de pile elle-même, pour plus de clarté, est la même que dans la question:

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

En fouillant dans le code source d'Android on trouve ces lignes:

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);

Pour moi, il semble que je frappe peut-être cette fonctionnalité non documentée, où la transaction échoue pour d'autres raisons qu'une transaction étant TooLarge. Ils auraient dû le nommer TransactionTooLargeOrAnotherReasonException.

Pour l'instant, je n'ai pas résolu le problème, mais si je trouve quelque chose d'utile, je mettrai à jour cette réponse.

mise à jour: il s'est avéré que mon code a divulgué certains descripteurs de fichiers, dont le nombre est maximisé sous linux (généralement 1024), et cela semble avoir déclenché l'exception. C'était donc un problème de ressources après tout. J'ai vérifié cela en ouvrant /dev/zero1024 fois, ce qui a entraîné toutes sortes d'exceptions étranges dans les actions liées à l'interface utilisateur, y compris l'exception ci-dessus, et même certains SIGSEGV. Apparemment, l'échec de l'ouverture d'un fichier / socket n'est pas quelque chose qui est géré / signalé très proprement dans Android.

mvds
la source
36

Le TransactionTooLargeExceptionnous a été qui sévit pendant environ 4 mois maintenant, et nous avons enfin résolu le problème!

Ce qui se passait, c'est que nous utilisons un FragmentStatePagerAdapterdans un ViewPager. L'utilisateur feuilleterait et créerait plus de 100 fragments (c'est une application de lecture).

Bien que nous gérions correctement les fragments dans destroyItem(), dans la mise en œuvre d'Androids, FragmentStatePagerAdapteril y a un bogue, où il a gardé une référence à la liste suivante:

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

Et lorsque Android FragmentStatePagerAdaptertentera de sauvegarder l'état, il appellera la fonction

@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;
}

Comme vous pouvez le voir, même si vous gérez correctement les fragments de la FragmentStatePagerAdaptersous - classe, la classe de base stockera toujours un Fragment.SavedStatepour chaque fragment jamais créé. Le TransactionTooLargeExceptionse produirait lorsque ce tableau était transféré vers un parcelableArrayet que le système d'exploitation ne l'aimerait pas plus de 100 éléments.

Par conséquent, le correctif pour nous était de remplacer la saveState()méthode et de ne rien stocker pour "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;
}
IK828
la source
meilleure réponse pour moi. Merci beaucoup
pavel
De toutes les possibilités pour cela, cela me semblait être la seule option. Qu'est-ce qui est stocké dans l'état qui lui permettrait d'atteindre cette taille? Si l'état est si nécessaire qu'il y a du code pour l'enregistrer, comment cela ne cause-t-il pas d'autres problèmes? Merci
Kenny
Même question que @Kenny
M. 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
Je ne laisse que les 3 derniers états, vérifiez le code ci-dessus.
Ramy Sabry
20

Pour ceux qui ont été amèrement déçus à la recherche d'une réponse expliquant pourquoi TransactionTooLargeException apparaît, essayez de vérifier la quantité d'informations que vous enregistrez dans l'état de l'instance.

Sur compile / targetSdkVersion <= 23, nous n'avons qu'un avertissement interne sur la grande taille de l'état enregistré, mais rien ne plante:

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)

Mais sur compile / targetSdkVersion> = 24, nous avons un véritable crash RuntimeException dans ce cas:

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) 

Que faire?

Enregistrez les données dans la base de données locale et conservez uniquement les identifiants dans l'état d'instance que vous pouvez utiliser pour récupérer ces données.

Yazon2006
la source
est-il possible de le conserver comme paramètre global et de l'utiliser ultérieurement?
Jitendra ramoliya
@Jitendraramoliya Oui, vous le pouvez. C'est exactement ce que je veux dire.
Yazon2006
13

Cette exception est généralement levée lorsque l'application est envoyée en arrière-plan.

J'ai donc décidé d'utiliser la méthode Fragment de données pour contourner complètement le onSavedInstanceStaecycle de vie. Ma solution gère également des états d'instance complexes et libère de la mémoire dès que possible.

J'ai d'abord créé un simple segment pour stocker les données:

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;
    }
}

Ensuite, lors de mon activité principale, je contourne complètement le cycle d'instance enregistré et reporte la responsabilité à mon fragment de données. Pas besoin de l'utiliser sur les Fragments eux-mêmes, car leur état s'ajoute automatiquement à l'état de l'Activité):

@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
}

Il ne reste plus qu'à faire éclater l'instance enregistrée:

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

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

Détails complets: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/

Vaiden
la source
1
Que se passe-t-il si l'activité est détruite lorsque l'application est en arrière-plan et que vous essayez de mettre en avant l'activité?
Master Disaster du
@MasterDisaster dans ce cas, aucun état n'est enregistré car le processus qui contient le fragment d'instance est mort.
Vaiden
Droits, donc ce cas ne fonctionne qu'avec un changement de configuration.
Master Disaster
Cela fonctionne chaque fois que le système d'exploitation se déclenche onSavedState(), ce qui se produit dans de nombreux cas. Le changement de configuration en est un. Changer d'application et passer en arrière-plan en sont une autre. Et il y en a plus.
Vaiden
1
Je pense que cette solution devrait être étendue pour enregistrer les données de différentes sources. Probablement un HashMap avec des tags comme clés et des bundles comme valeurs ..
CoolMind
11

Il n'y a pas de cause spécifique à ce problème, pour moi, dans ma classe Fragment, je faisais ceci:

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;
}

au lieu de cela:

View rootView = inflater.inflate(R.layout.softs_layout, container, false);
Ojonugwa Jude Ochalifu
la source
9

Il est important de comprendre que le tampon de transaction est limité à 1 Mo, quelles que soient les capacités de l'appareil ou l'application. Ce tampon est utilisé avec tous les appels d'API que vous effectuez et est partagé entre toutes les transactions qu'une application exécute actuellement.

Je crois qu'il contient également un objet spécifique comme des colis et autres (Parcel.obtain()), il est donc important de toujours faire correspondre chacun obtain()avec un recycle().

Cette erreur peut facilement se produire lors d'appels d'API renvoyant un grand nombre de données, même si les données retournées sont inférieures à 1 Mo (si d'autres transactions sont toujours en cours d'exécution).

Par exemple, l' PackageManager.getInstalledApplication()appel renvoie une liste de toutes les applications installées. L'ajout de drapeaux spécifiques permet de récupérer un grand nombre de données supplémentaires. Cela pourrait échouer, il est donc recommandé de ne pas récupérer de données supplémentaires et de les récupérer par application.

Cependant, l'appel peut toujours échouer, il est donc important de l'entourer d'un catchet de pouvoir réessayer si nécessaire.

Pour autant que je sache, il n'y a pas de solution à ce problème, sauf en réessayant et en veillant à récupérer le moins d'informations possible.

3c71
la source
Merci pour les informations, ce tampon est partagé entre toutes les transactions au sein de l'application!
Artem Mostyaev
1
Si tel était le cas, alors pourquoi est-ce que je ne reçois que des téléphones Android 4.4 et nulle part ailleurs. Je pense que c'est plus un bug dans 4.4 que je ne peux pas comprendre pourquoi.
JPM
9

Moi aussi, j'ai eu cette exception sur un Samsung S3. Je soupçonne 2 causes profondes,

  1. vous avez des bitmaps qui se chargent et prennent trop de mémoire, utilisez la réduction des effectifs
  2. Il vous manque des tirables dans les dossiers drawable-_dpi, android les recherche dans drawable et les redimensionne, ce qui fait que votre setContentView saute soudainement et utilise beaucoup de mémoire.

Utilisez DDMS et regardez votre tas pendant que vous jouez votre application, cela vous donnera une indication sur quel setcontentview crée le problème.

J'ai copié tous les drawables dans tous les dossiers pour se débarrasser du problème 2.

Le problème est résolu.

Siddharth
la source
Les exceptions de mémoire / bitmap sont généralement différentes. J'en ai déjà vu beaucoup tester avec Android 2.x - 4.x et les exceptions sont toujours différentes. Mais, qui sait, c'est peut-être aussi lié mais spécifique aux versions 4.x.
Ixx
10
C'est juste une terrible exception, en ce qui concerne l'information, car elle ne donne aucune idée de l'origine du problème.
Ixx
Je pense que les quelques jours sont terminés, quelles sont vos conclusions?
Denny
8

Ajoutez ceci à votre activité

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

Cela fonctionne pour moi j'espère aussi que cela vous aidera

Makvin
la source
7
Je pense que c'est l'indice le plus nocif. Pourquoi devrions-nous effacer les données que nous voulons restaurer onCreate()?
CoolMind
2
Les implications de ce code sont que vous ne sauvegardez PAS l'état de votre instance ...
Justin
4

Donc, pour nous, nous essayions d'envoyer un objet trop volumineux via notre interface AIDL à un service distant. La taille de la transaction ne peut pas dépasser 1 Mo. La demande est décomposée en blocs séparés de 512 Ko et envoyée une par une via l'interface. Une solution brutale que je connais mais bon - son Android :(

Kevin Parker
la source
4

Vous avez effacé votre ancien InstanceState de la méthode onSaveInstanceState , et cela fonctionnera bien. J'utilise FragmentStatePagerAdapter pour mon viewpager donc je reste en dessous de la méthode Override dans mon activité parent pour effacer InstanceState.

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

J'ai trouvé cette solution ici android.os.TransactionTooLargeException sur Nougat

varotariya vajsi
la source
Merci beaucoup.
Andrain
Merci votre réponse aidez-moi
Jatin Patel
3

Récemment, j'ai également rencontré un cas intéressant en travaillant avec le fournisseur de contacts d'Android .

Je devais charger des photos de contacts à partir de la base de données de contacts internes et selon l'architecture du système, toutes ces données sont fournies par des requêtes au fournisseur de contacts.

Comme cela fonctionne comme une application distincte - toutes sortes de transferts de données sont effectués en utilisant le mécanisme Binder et donc le tampon Binder entre en jeu ici.

Ma principale erreur a été de ne pas fermer les Cursordonnées blob obtenues auprès du fournisseur de contacts, de sorte que la mémoire allouée au fournisseur a augmenté et cela a gonflé le tampon du classeur jusqu'à ce que !!!FAILED BINDER TRANSACTION!!!j'obtienne des tonnes de messages dans ma sortie LogCat.

L'idée principale est donc que lorsque vous travaillez avec des fournisseurs de contenu externes et que vous les obtenez Cursor, fermez-les toujours lorsque vous avez fini de travailler avec eux.

Alex Bonel
la source
3

J'ai été confronté au même problème lorsque j'ai essayé d'envoyer un bitmap via Intent et en même temps, quand il s'est produit, j'ai plié l'application.

Comment il est décrit dans cet article entrez la description du lien ici, cela se produit lorsqu'une activité est en train de s'arrêter, cela signifie que l'activité essayait d'envoyer ses bundles d'état enregistrés au système d'exploitation pour les conserver en toute sécurité pour la restauration plus tard (après un changement de configuration) ou traiter la mort) mais qu'un ou plusieurs des paquets envoyés étaient trop volumineux.

Je l'ai résolu via hack en remplaçant onSaveInstanceState dans mon activité:

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

et commenter appeler super. C'est un hack sale mais cela fonctionne parfaitement. Bitmap a été envoyé avec succès sans se bloquer. J'espère que cela aidera quelqu'un.

Anonyme
la source
2

Dans mon cas, j'obtiens TransactionTooLargeException comme un incident secondaire après que la bibliothèque native s'est écrasée avec SIGSEGV. Le crash de la bibliothèque native n'est pas signalé, donc je ne reçois que TransactionTooLargeException.

chris
la source
2

J'ai obtenu ceci dans mon syncadapter en essayant de vracInsert un grand ContentValues ​​[]. J'ai décidé de le réparer comme suit:

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));
}
Pepijn
la source
2
Et si l'autre échoue? Vous devez faire plus de division en utilisant une boucle. Existe-t-il peut-être un moyen d'obtenir la taille de transaction et d'obtenir la taille de transaction maximale?
développeur Android
2

Pour moi, c'était aussi le FragmentStatePagerAdapter , mais le dépassement saveState()n'a pas fonctionné. Voici comment je l'ai corrigé:

Lorsque vous appelez le FragmentStatePagerAdapter constructeur, conservez une liste distincte de fragments dans la classe et ajoutez une méthode pour supprimer les fragments:

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...
}

Ensuite Activity, enregistrez la ViewPagerposition et appelez adapter.removeFragments()dans leonSaveInstanceState() méthode :

private int pagerPosition;

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

Enfin, dans la onResume()méthode substituée , réinstanciez l'adaptateur s'il ne l'est pas null. (Si c'est le cas null, alors le Activityest ouvert pour la première fois ou après que l'application a été supprimée par Android, dans laquelle onCreatela création de l'adaptateur sera effectuée.)

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

Assurez-vous que vous ne placez pas dans Intent des données d'objet de grande taille. Dans mon cas, j'ajoutais une taille de chaîne de 500 k, puis je commençais une autre activité. Il a toujours échoué avec cette exception. J'ai évité de partager des données entre les activités en utilisant des variables statiques d'activités - vous n'avez pas à les envoyer à Intent, puis à en tirer.

Ce que j'avais:

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);
Gladimdim
la source
C'est une très mauvaise idée. L'utilisation de variables statiques de cette manière est une odeur de code. Essayez également d'exécuter ce code avec l'indicateur "Ne pas conserver les activités" et appuyez sur Accueil après avoir accédé à PageWebView. Après avoir ouvert à nouveau votre activité, vous obtiendrez probablement un plantage car MainActivity sera mort à 100% avec toutes ses variables.
Kyrylo Zapylaiev
1

Lorsque je traite avec WebViewdans mon application, cela se produit. Je pense que c'est lié aux addViewressources de l'interface utilisateur. Dans mon application, j'ajoute du code WebViewActivitycomme celui-ci ci-dessous, puis il fonctionne correctement:

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

J'ai trouvé la cause profonde de cela (nous avons eu à la fois "l'ajout de la fenêtre a échoué" et la fuite du descripteur de fichier comme le dit mvds).

Il y a un bogue dans BitmapFactory.decodeFileDescriptor()Android 4.4. Il se produit uniquement lorsque inPurgeableet inInputShareablede BitmapOptionssont définis sur true. Cela provoque de nombreux problèmes dans de nombreux endroits interagissent avec les fichiers.

Notez que la méthode est également appelée depuis MediaStore.Images.Thumbnails.getThumbnail().

Universal Image Loader est concerné par ce problème. Picasso et Glide ne semblent pas affectés. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020

ypresto
la source
1

Cette seule ligne de code dans la méthode writeToParcel (Parcel dest, int flags) m'a aidé à me débarrasser de TransactionTooLargeException.

dest=Parcel.obtain(); 

Après ce code, j'écris toutes les données sur l'objet parcelle, c'est-à-dire dest.writeInt (), etc.

dilip
la source
1

Essayez d'utiliser EventBusou d' ContentProvideraimer la solution.

Si vous êtes dans le même processus (normalement, toutes vos activités le seraient), essayez d'utiliser EventBus, car l'échange de données en cours n'a PAS besoin d'un tampon, donc vous n'avez pas à vous soucier de la taille de vos données. (Vous pouvez simplement utiliser l'appel de méthode pour transmettre des données, et EventBus cache les choses laides) Voici le détail:

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

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

Si les deux côtés de l'intention ne sont pas dans le même processus, essayez un peu ContentProvider.


Voir TransactionTooLargeException

La transaction Binder a échoué car elle était trop importante.

Lors d'un appel de procédure distante, les arguments et la valeur de retour de l'appel sont transférés en tant qu'objets Parcel stockés dans le tampon de transaction Binder. Si les arguments ou la valeur de retour sont trop grands pour tenir dans le tampon de transaction, l'appel échouera et TransactionTooLargeException sera levée.

boileryao
la source
1

J'ai reçu une TransactionTooLargeException à partir d'une erreur Stackoverflow dans un test Android Espresso. J'ai trouvé la trace de la pile d'erreur stackoverflow dans les journaux lorsque j'ai retiré le filtre Logcat pour mon application.

Je suppose que Espresso a provoqué la TransactionTooLargeException lors de la tentative de gestion d'une trace de pile d'exceptions vraiment importante.

Dagmar
la source
1

On peut utiliser:

android:largeHeap="true"

dans le manifeste Android sous la balise d'application.

Cela a résolu le problème dans mon cas!

MazRoid
la source
Dans mon cas (à cause de l'appel onSaveInstantStateà des activités / fragments et à l'enregistrement de grandes listes), cela n'a pas aidé.
CoolMind
1
Ceci est considéré comme une mauvaise pratique car vous ne traitez pas la raison pour laquelle de nombreuses données sont enregistrées en premier lieu. Sur les appareils plus récents, l'application plante et la limite est beaucoup plus petite (256 Ko). Cherchez d'abord pourquoi vous stockez des lots et réduisez-les.
Tim Kist
1

J'ai également été confronté à ce problème pour les données bitmap passant d'une activité à une autre, mais je fais une solution en transformant mes données en données statiques et cela fonctionne parfaitement pour moi

En activité d'abord:

public static Bitmap bitmap_image;

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

et en deuxième activité:

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

Cela se produisait dans mon application parce que je passais une liste de résultats de recherche dans les arguments d'un fragment, en attribuant cette liste à une propriété du fragment - qui est en fait une référence au même emplacement en mémoire indiqué par les arguments du fragment - puis en ajoutant de nouveaux éléments dans la liste, ce qui a également modifié la taille des arguments du fragment. Lorsque l'activité est suspendue, la classe de fragment de base tente d'enregistrer les arguments du fragment dans onSaveInstanceState, qui se bloque si les arguments sont supérieurs à 1 Mo. Par exemple:

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);
}

La solution la plus simple dans ce cas était d'attribuer une copie de la liste à la propriété du fragment au lieu d'attribuer une référence:

@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"));
    }
}

Une solution encore meilleure serait de ne pas faire circuler autant de données dans les arguments.

Je n'aurais probablement jamais trouvé cela sans l'aide de cette réponse et de TooLargeTool .

Big McLargeÉnorme
la source
1

J'ai également vécu TransactionTooLargeException. Tout d'abord, j'ai travaillé à comprendre où cela se produit. Je connais la raison pour laquelle cela se produit. Chacun de nous le sait grâce au contenu volumineux. Mon problème était comme ça et j'ai résolu. Peut-être que cette solution peut être utile à n'importe qui. J'ai une application qui récupère le contenu de l'API. J'obtiens le résultat de l'API dans le premier écran et l'envoie au deuxième écran. Je peux envoyer ce contenu au deuxième écran avec succès. Après le deuxième écran, si je veux passer au troisième écran, cette exception se produit. Chacun de mes écrans est créé à partir de Fragment. Je l'ai remarqué lorsque je quitte le deuxième écran. Il enregistre le contenu de son bundle. si ce contenu est trop volumineux, cette exception se produit. Ma solution est après avoir obtenu le contenu du bundle, je l'efface.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        myContent = arguments?.getParcelable("mycontent")
        arguments?.clear()
    }
nebyan
la source
Bien que cela soit vrai (dommage pour moi, j'ai compris la même chose après un an), comment le fragment se comportera-t-il s'il recrée (rotation de l'écran)?
CoolMind
0

Une solution consisterait à ce que l'application écrive la ArrayList (ou tout autre objet à l'origine du problème) dans le système de fichiers, puis transmette une référence à ce fichier (par exemple, nom de fichier / chemin d'accès) via l'intention au IntentService, puis laisse le IntentService récupérer le contenu du fichier et le reconvertir en ArrayList.

Lorsque IntentService a terminé avec le fichier, il doit le supprimer ou transmettre l'instruction à l'application via une diffusion locale pour supprimer le fichier qu'il a créé (en renvoyant la même référence de fichier qui lui a été fournie).

Pour plus d'informations, voir ma réponse à ce problème connexe .

interdiction de la géo-ingénierie
la source
0

Comme Intents, les fournisseurs de contenu, Messenger, tous les services système comme le téléphone, le vibreur, etc. utilisent le fournisseur d'infrastructure IPC de Binder. De plus, les rappels du cycle de vie de l'activité utilisent également cette infrastructure.

1 Mo est la limite globale de toutes les transactions de liant exécutées dans le système à un moment particulier.

Dans le cas où de nombreuses transactions se produisent lorsque l'intention est envoyée, elles peuvent échouer même si les données supplémentaires ne sont pas volumineuses. http://codetheory.in/an-overview-of-android-binder-framework/

Shinoo Goyal
la source
0

Avec autant d'endroits où TransactionTooLargeException peut se produire - voici une autre nouveauté pour Android 8 - un crash lorsque quelqu'un commence simplement à taper dans un EditText si le contenu est trop gros.

Il est lié à AutoFillManager (nouveau dans l'API 26) et au code suivant dans StartSessionLocked():

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

Si je comprends bien, cela appelle le service de remplissage automatique - en passant AutofillManagerClient dans le classeur. Et lorsque le EditText a beaucoup de contenu, il semble provoquer le TTLE.

Quelques choses peuvent l'atténuer (ou ont fait comme je android:importantForAutofill="noExcludeDescendants"le testais de toute façon): Ajoutez la déclaration de disposition XML d'EditText. Ou en code:

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);
}

Une deuxième solution de contournement terrible et terrible pourrait également être de remplacer les méthodes performClick()et onWindowFocusChanged()pour détecter l'erreur dans une sous-classe TextEdit elle-même. Mais je ne pense pas que ce soit vraiment sage ...

fattire
la source