Je me suis retrouvé dans l'erreur d'une application TransactionTooLargeException. Non reproductible et jamais auparavant. Dans la documentation, il est dit
La transaction de classeur a échoué car elle était trop volumineuse.
Au cours d'un appel de procédure à distance, les arguments et la valeur de retour de l'appel sont transférés sous la forme d'objets Parcel stockés dans la mémoire tampon de transaction du classeur. Si les arguments ou la valeur de retour sont trop volumineux pour tenir dans le tampon de transaction, l'appel échouera et une exception TransactionTooLargeException sera levée.
...
Il existe deux résultats possibles lorsqu'un appel de procédure à distance lève une exception TransactionTooLargeException. Soit le client n'a pas pu envoyer sa demande au service (probablement si les arguments étaient trop grands pour tenir dans le tampon de transaction), soit le service n'a pas pu renvoyer sa réponse au client (probablement si la valeur de retour était trop grand pour tenir dans le tampon de transaction).
...
Donc, ok, quelque part, je passe ou reçois des arguments dépassant une limite inconnue. Mais où?
Le stacktrace ne montre rien de mes fichiers:
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, parce que toutes les lignes Fenêtre/Vue? Comment est-ce lié à l'appel de procédure à distance? Comment puis-je rechercher la raison de cette erreur?
Dans l'application, j'utilise uniquement des Webservices, je n'utilise pas la classe de service. Les Webservices sont-ils des "appels de procédure distante" ou quoi d'autre pourrait être ...?
P.S. Peut-être que c'est important: Version Android: 4.0.3, Appareil: HTC One X
J'ai rencontré ce problème et j'ai constaté que, lorsqu'un volume important de données était échangé entre un service et une application, (cela implique de transférer beaucoup de vignettes). En réalité, 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 transmettez beaucoup de données via des extras d'intention.
Lorsque vous obtenez cette exception dans votre application, veuillez analyser votre code.
Comment gérer quand vous obtenez cette exception
Si possible, divisez la grosse opération en petites parties, par exemple, au lieu d'appeler applyBatch () avec 1000 opérations, appelez-la avec 100 chacune.
Ne pas échanger d'énormes données (> 1 Mo) entre services et applications
Je ne sais pas comment faire cela, mais, n'interrogez pas Android, qui peut renvoyer d'énormes données :-)
Ce n’est pas une réponse définitive, mais elle pourrait nous éclairer sur les causes d’une variable TransactionTooLargeException
et aider à cerner 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 importants et l'ouverture répétée d'un menu contextuel ActionBar. Le crash se produit en appuyant sur la barre d'action. (il s'agit d'une application de mappage personnalisée)
Les seules données transmises semblent être des contacts du "Input Dispatcher" vers l'application. Je pense que cela ne peut raisonnablement pas atteindre à peu près 1 mb dans le "tampon de transaction".
Mon application fonctionne sur un périphérique quadricœur 1,6 GHz et utilise 3 threads pour le heavy -ifting, laissant un noyau libre pour le thread d'interface utilisateur. En outre, l'application utilise Android: largeHeap, il lui reste 10 Mo de tas inutilisés et 100 Mo d'espace disponible pour agrandir le tas. Donc, je ne dirais pas que c'est une question 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 !!!
Lesquelles ne sont pas nécessairement imprimées dans cet ordre, mais (autant que j'ai vérifié) se passent sur la même milliseconde.
Et la trace de la pile elle-même, par souci 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 me semble que je suis peut-être en train de frapper cette fonctionnalité non documentée, où la transaction échoue pour d'autres raisons qu'une transaction trop lourde. 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.
update: il s'est avéré que mon code avait laissé fuir certains descripteurs de fichier, 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 1024 fois le /dev/zero
, 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 traité/rapporté très proprement dans Android.
La TransactionTooLargeException
nous tourmente depuis environ 4 mois maintenant et nous avons finalement résolu le problème!
Ce qui se passait, c’est que nous utilisons une FragmentStatePagerAdapter
dans une ViewPager
. L'utilisateur peut parcourir et créer plus de 100 fragments (c'est une application de lecture).
Bien que nous gérions les fragments correctement dans destroyItem()
, dans Androids Implémentation de FragmentStatePagerAdapter
, il existe un bogue qui garde une référence à la liste suivante:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
Et lorsque la variable FragmentStatePagerAdapter
d'Android tente de sauvegarder l'état, elle appelle 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 constater, même si vous gérez correctement les fragments de la sous-classe FragmentStatePagerAdapter
, la classe de base conservera tout de même un Fragment.SavedState
pour chaque fragment créé. La TransactionTooLargeException
se produirait lorsque ce tableau serait vidé dans une parcelableArray
et 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 méthode saveState()
et not store rien 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;
}
Si vous avez besoin de savoir quelle parcelle est à l'origine de votre blocage, vous devriez envisager d'essayer TooLargeTool .
(J'ai trouvé cela sous forme de commentaire de @ Max Spencer sous la réponse acceptée et cela m'a été utile dans mon cas.)
Pour ceux qui sont amèrement déçus en cherchant la réponse à la question de savoir pourquoi la TransactionTooLargeException apparaît, essayez de vérifier ce que vous enregistrez dans l'état de l'instance.
Lors de la compilation / targetSdkVersion <= 23, nous n'avons qu'un avertissement interne concernant la taille importante de l'état enregistré, mais rien n'est bloqué:
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 lors de la compilation / targetSdkVersion> = 24, nous avons un crash réel de 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 de l'instance, ce que vous pouvez utiliser pour récupérer ces données.
Il n'y a pas une cause spécifique de ce problème.Pour moi, dans mon cours de 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);
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 de fragmentation de données pour contourner complètement le cycle de vie onSavedInstanceStae
. Ma solution gère également les états d'instance complexes et libère de la mémoire dès que possible.
J'ai d'abord créé un simple Fargment 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’instances sauvegardées et reporte la responsabilité sur mon fragment de données. Il n'est pas nécessaire de l'utiliser sur les fragments eux-mêmes, car leur état est automatiquement ajouté à 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
}
Ce qui reste est simplement de faire apparaître l'instance sauvegardé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() );
}
Plus de détails: http://www.devsbedevin.com/avoiding-transactiontoolargeexception-on-Android-nougat-and-up/
Il est important de comprendre que le tampon de transaction est limité à 1 Mo, quelles que soient les capacités du périphérique ou l'application. Ce tampon est utilisé avec tous les appels d'API que vous effectuez et est partagé entre toutes les transactions en cours d'exécution d'une application.
Je crois qu’il contient également des objets spécifiques tels que des colis et un tel (Parcel.obtain())
. Il est donc important de toujours faire correspondre chaque obtain()
avec un recycle()
.
Cette erreur peut facilement se produire lorsque des appels d'API renvoient beaucoup de données, même si les données renvoyées font moins de 1 Mo (si d'autres transactions sont toujours en cours d'exécution).
Par exemple, l'appel PackageManager.getInstalledApplication()
renvoie la liste de toutes les applications installées. L'ajout d'indicateurs spécifiques permet de récupérer beaucoup de données supplémentaires. Cela risquerait fort d'échouer. Il est donc recommandé de ne pas extraire de données supplémentaires ni de les récupérer application par application.
Cependant, l'appel peut toujours échouer. Il est donc important de l'entourer d'une catch
et de pouvoir le réessayer si nécessaire.
Autant que je sache, il n'y a pas de solution de rechange à ce problème, sauf de réessayer et de s'assurer de récupérer le moins d'informations possible.
J'ai aussi eu cette exception sur un Samsung S3 . Je soupçonne deux causes fondamentales,
Utilisez DDMS et examinez votre segment de mémoire lorsque vous jouez dans votre application, ce qui vous indiquera quel setcontentview crée le problème.
J'ai copié tous les tirages dans tous les dossiers pour éliminer le problème 2.
Le problème est résolu.
Nous avons donc essayé 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 divisée en morceaux distincts de 512 Ko et est envoyée une par une via l'interface. Une solution brutale que je connais mais bon - son Android :(
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
Récemment, j'ai également rencontré un cas intéressant lorsque je travaillais avec le fournisseurContacts d'Android.
Je devais charger des photos de contacts à partir de la base de données de contacts interne. Selon l'architecture du système, toutes ces données sont transmises par le biais de requêtes au fournisseur de contacts.
Comme cela fonctionne comme une application séparée - tous les types de transfert de données sont effectués en utilisant le mécanisme de reliure et le tampon de liant entre en jeu ici.
Mon erreur principale est que je n'ai pas fermé le Cursor
avec des données blob obtenues du fournisseur de contacts, de sorte que la mémoire allouée pour le fournisseur a augmenté et que le tampon Binder a été gonflé jusqu'à ce que des tonnes de messages !!!FAILED BINDER TRANSACTION!!!
apparaissent dans ma sortie LogCat .
L'idée principale est donc que lorsque vous travaillez avec des fournisseurs de contenu externes et que vous obtenez des noms Cursor
, fermez-le toujours lorsque vous avez fini de travailler avec eux.
Vous avez effacé votre ancienne méthode InstanceState de onSaveInstanceState, et cela fonctionnera bien. J'utilise FragmentStatePagerAdapter pour mon viewpager afin que je conserve la méthode ci-dessous Override dans mon activité parent pour effacer InstanceState.
@Override
protected void onSaveInstanceState(Bundle InstanceState) {
super.onSaveInstanceState(InstanceState);
InstanceState.clear();
}
J'ai trouvé cette solution à partir d'ici Android.os.TransactionTooLargeException sur Nougat
Pour moi, c’était aussi la FragmentStatePagerAdapter
; toutefois, remplacer saveState()
ne fonctionnait pas. Voici comment je l'ai corrigé:
Lorsque vous appelez le constructeur FragmentStatePagerAdapter
, conservez une liste séparée 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, dans la Activity
, enregistrez la position ViewPager
et appelez adapter.removeFragments()
dans la méthode onSaveInstanceState()
remplacée:
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 méthode onResume()
remplacée, ré-instanciez l'adaptateur s'il ne s'agit pas de null
. (S'il s'agit de null
, la Activity
est ouverte pour la première fois ou après la suppression de l'application par Android, dans laquelle onCreate
créera l'adaptateur.)
@Override
public void onResume() {
super.onResume();
if (adapter != null) {
adapter = new PagerAdapter(frags);
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(currentTabPosition);
}
}
Dans mon cas, je reçois TransactionTooLargeException comme un incident secondaire après le blocage de la bibliothèque native avec SIGSEGV. Le plantage de la bibliothèque native n’est pas signalé, je ne reçois donc que TransactionTooLargeException.
Assurez-vous de ne pas mettre dans l'objet Intent des données de grande taille. Dans mon cas, j’étais en train d’ajouter String 500k, puis de commencer 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 à les extraire.
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);
Je l'ai eu dans mon syncadapter en essayant de bulkInsert une grande 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));
}
De plus, je faisais face à ce problème pour les données bitmap passant d'une activité à une autre, mais je crée une solution en rendant mes 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;
}
J'ai trouvé la cause première de ceci (nous avons à la fois "l'ajout de la fenêtre a échoué" et une fuite de descripteur de fichier comme le dit mvds).
Il y a un bug in BitmapFactory.decodeFileDescriptor()
sur Android 4.4 . Il ne se produit que lorsque inPurgeable
et inInputShareable
sur BitmapOptions
sont 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 à partir de MediaStore.Images.Thumbnails.getThumbnail()
.
Universal Image Loader est concerné par ce problème. Picasso et Glide ne semblent pas être affectés . https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020
J'ai eu une exception TransactionTooLargeException suite à une erreur Stackoverflow lors d'un test Android Espresso. J'ai trouvé la trace de pile d'erreurs stackoverflow dans les journaux lorsque j'ai retiré le filtre Logcat pour mon application.
J'imagine que Espresso a provoqué l'exception TransactionTooLargeException en essayant de gérer un stacktrace d'exception de très grande taille.
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!
Pour moi cette erreur venait dans le présentateur. J'ai fait des commentaires sur onResume et écrit le même code dans onStart et cela a fonctionné pour moi.
@Override
public void onStart() {
super.onStart();
Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
if (goal != null && goal.getValue() > 0) {
mCurrentValue = (int) goal.getValue();
notifyPropertyChanged(BR.currentValue);
mIsButtonEnabled.set(true);
}
}
/* @Override
public void onResume() {
super.onResume();
Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
if (goal != null && goal.getValue() > 0) {
mCurrentValue = (int) goal.getValue();
notifyPropertyChanged(BR.currentValue);
mIsButtonEnabled.set(true);
}
}*/
J'ai également vécu TransactionTooLargeException. Tout d'abord j'ai travaillé sur comprendre où cela se produit. Je connais la raison pour laquelle cela se produit. Nous sommes tous au courant à cause du contenu important. Mon problème était comme ça et j'ai résolu. Peut-être que cette solution peut être utile à tout le monde. J'ai une application qui récupère du contenu d'api. Je reçois 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 en cas de succès. Après le deuxième écran, si je souhaite passer au troisième écran, cette exception se produit. Chacun de mes écrans est créé à partir de Fragment. Je l'ai remarqué quand je pars du deuxième écran. Il enregistre le contenu de son lot. si ce contenu est trop volumineux, cette exception se produit. Ma solution est après avoir obtenu le contenu du paquet, je l'efface.
class SecondFragment : BaseFragment() {
lateinit var myContent: MyContent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myContent = arguments?.getParcelable("mycontent")
arguments?.clear()
}
J'ai fait face au même problème lorsque j'ai essayé d'envoyer du bitmap via Intent et, au même moment, j'ai plié l'application.
Comment cela est décrit dans cet article entrez la description du lien ici cela se produit quand une activité est en train de s’arrêter; après un changement de configuration ou la mort du processus), mais qu’un ou plusieurs des paquets envoyés étaient trop volumineux.
Je l'ai résolu via un hack en remplaçant onSaveInstanceState dans mon activité:
@Override
protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
}
et comment appeler super. C'est un sale bidouillage mais il fonctionne parfaitement. Bitmap a été envoyé avec succès sans crash . J'espère que cela aidera quelqu'un.
Cela se passait dans mon application parce que je transmettais une liste de résultats de recherche dans les arguments d'un fragment, en affectant cette liste à une propriété du fragment - qui est en fait une référence au même emplacement en mémoire pointé par les arguments du fragment - puis 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 de sauvegarder les arguments du fragment dans onSaveInstanceState, qui se bloque si la taille des arguments est supérieure à 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-ci consistait à affecter 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 transmettre autant de données dans les arguments.
Je n'aurais probablement jamais trouvé cela sans l'aide de cette réponse et TooLargeTool .
J'ai eu le même problème lors du transfert (envoi d'une diffusion) d'un objet bitmap vers un récepteur de diffusion.
intent.putExtra("image", bitmapImage);
Donc, au lieu de les envoyer en tant que bitmap. J'ai converti le bitmap en tableau d'octets. À ma surprise, il a fonctionné!!! Je me demande pourquoi Android ne permet pas de transférer des données volumineuses avec Bitmap mais permet la même chose grâce à un tableau d'octets.
intent.putExtra("imageInByteArray", convertBitmapToByteArray(bitmapImage));
Sur le récepteur, j'ai reconverti le tableau d'octets en bitmap, ce qui a résolu mon problème.
Cette ligne de code dans la méthode writeToParcel (Parcel dest, int flags) m'a permis de me débarrasser de TransactionTooLargeException.
dest=Parcel.obtain();
Après ce code, j’écris toutes les données dans l’objet parcelle i.e dest.writeInt (), etc.
Comme Intents, Content Providers, Messenger, tous les services système tels que Téléphone, Vibrator, etc. utilisent le fournisseur d'infrastructure IPC de Binder. De plus, les rappels du cycle de vie des activités utilisent également cette infrastructure.
1 Mo est la limite globale de toutes les transactions de reliure exécutées dans le système à un moment donné.
Si de nombreuses transactions se produisent lors de l'envoi de l'intention, l'opération peut échouer même si des données supplémentaires ne sont pas volumineuses. http://codetheory.fr/an-overview-of-Android-binder-framework/
parfois, Activity
inclut certains Fragment
quand Activity
nécessité de recréer totalement le contenu de Fragments, si le sportFragmentManager.fragments
sans fragments d’historique clairs
val fragments = sportFragmentManager.fragments
val transaction = sportFragmentManager.beginTransaction()
for (frag in fragments){
transaction.remove(frag)
}
transaction.commitAllowingStateLoss()
fois par serveur recrée Fragments la Activity
va se passer (utilisation du débogage tooLargeTool )
W/ActivityStopInfo: Bundle stats:
W/ActivityStopInfo: Android:viewHierarchyState [size=2304]
W/ActivityStopInfo: Android:views [size=2256]
W/ActivityStopInfo: Android:support:fragments [size=519072]
W/ActivityStopInfo: PersistableBundle stats:
W/ActivityStopInfo: [null]
Une solution serait que l’application écrive ArrayList (ou l’objet causant le problème) dans le système de fichiers, puis transmette une référence à ce fichier (par exemple, nom de fichier/chemin) via Intent à IntentService, puis laisse IntentService. récupérer le contenu du fichier et le reconvertir en un ArrayList.
Lorsque IntentService a terminé avec le fichier, vous devez le supprimer ou renvoyer l'instruction à l'application via une diffusion locale pour supprimer le fichier qu'il a créé (en lui 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 .
@Vaiden réponse m'a aidé. Je ne comprenais pas pourquoi les listes restreintes pouvaient soulever cette exception. J'ai beaucoup de fragments et un couple de ViewPager
s avec des listes. Donc, chaque fois que j'ai commencé une autre activité ou arrêté un programme (éteint un écran), j'ai attrapé cette exception (généralement sur Xiaomi).
J'ai trouvé que tous les fragments appelés leurs événements onSaveInstanceState()
et, à la fin, l'activité hosting appelée onSaveInstanceState()
avant onStop()
. Après cela, un crash est survenu. J'ai donc compris que de nombreuses listes courtes (de 10 à 100 Kb) peuvent générer une exception TransactionTooLargeException
.
Vous pouvez résoudre ce problème en écrivant des données dans une base de données, puis obtenir les éléments par leur id
s. De cette façon, vous pouvez passer une liste de id
s à une activité/un fragment.
Mais si vous souhaitez une méthode rapide temporaire, faites-le.
1) Créez un fragment conservant-instance, qui survivra pendant la récréation d'activité.
class SavedInstanceFragment : Fragment() {
// This map will hold bundles from different sources (tag -> bundle).
private lateinit var bundleMap: HashMap<String, Bundle?>
// This method is only called once for this fragment.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
bundleMap = HashMap()
}
fun pushData(key: String, bundle: Bundle): SavedInstanceFragment {
if (bundleMap[key] == null) {
bundleMap[key] = bundle
} else {
bundleMap[key]!!.putAll(bundle)
}
return this
}
fun popData(key: String): Bundle? {
val data = bundleMap[key]
bundleMap[key] = null
return data
}
companion object {
private const val TAG = "SavedInstanceFragment"
// Create the fragment with this method in `onCreate()` of an activity.
// Then you can get this fragment with this method again.
fun getInstance(fragmentManager: FragmentManager): SavedInstanceFragment {
var out = fragmentManager.findFragmentByTag(TAG) as SavedInstanceFragment?
if (out == null) {
out = SavedInstanceFragment()
fragmentManager.beginTransaction().add(out, TAG).commit()
}
return out
}
}
}
2) Dans onCreate()
de vos activités contenant des fragments de problème, créez ce fragment. Il survivra aux activités de récréation. Vous devez le créer avant l'ajout d'autres fragments à la variable FragmentManager
, car cette opération est asynchrone.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity)
if (savedInstanceState == null) {
SavedInstanceFragment.getInstance(supportFragmentManager)
}
...
}
3) Dans chaque fragment de problème, ajoutez ces lignes. Vous devez accéder à un fragment de conservation-instance avec getInstance(activity?.supportFragmentManager!!)
partout, afin de pouvoir le trouver dans FragmentManager
. Si vous utilisez childFragmentManager
comme paramètre de getInstance()
, vous créez un autre fragment et vous plantez.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bundle = SavedInstanceFragment.getInstance(activity?.supportFragmentManager!!).popData(TAG)
(bundle ?: arguments)?.run { // I access 'bundle' or 'arguments', depending if it is not first or first call of 'onCreate()'.
token = getString(ARG_TOKEN)!!
id = getInt(ARG_ID)
items = getParcelableArrayList(ARG_ITEMS)
}
}
override fun onSaveInstanceState(outState: Bundle) {
// Create a copy of savedInstanceState and Push to the retain-instance fragment.
val bundle = (outState.clone() as Bundle).apply {
putString(ARG_TOKEN, token)
putInt(ARG_ID, id)
putParcelableArrayList(ARG_ITEMS, items)
}
SavedInstanceFragment.getInstance(activity?.supportFragmentManager!!).pushData(TAG, bundle)
super.onSaveInstanceState(outState)
}
companion object {
const val TAG = "YourFragment"
private const val ARG_TOKEN = "token"
private const val ARG_ID = "id"
private const val ARG_ITEMS = "items"
fun newInstance(token: String, id: Int, items: ArrayList<Item>?) =
YourFragment().apply {
arguments = Bundle().apply {
putString(ARG_TOKEN, token)
putInt(ARG_ID, id)
putParcelableArrayList(ARG_ITEMS, items)
}
}
}
Au lieu d'un fragment persistant, vous pouvez utiliser Singleton. Lisez également https://medium.com/@mdmasudparvez/Android-os-transactiontoolargeexception-on-nougat-solved-3b6e30597345 pour plus d'informations sur libraries, solutions.
Pour moi, TransactionTooLargeException est survenu lorsque j'ai tenté d'envoyer l'image large bitmap d'une activité à une autre via l'intention. J'ai résolu ce problème en utilisant Variables globales de l'application .
Par exemple, si vous souhaitez envoyer une grande image bitmap d'une activité A à une activité B, stockez cette image bitmap dans la variable globale.
((Global) this.getApplication()).setBitmap(bitmap);
puis commencez l'activité B et lisez à partir de la variable globale
Bitmap bitmap = ((Global) this.getApplication()).getBitmap();
Essayez d'utiliser EventBus
ou ContentProvider
comme 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, vous n'avez donc pas à vous soucier de la taille de vos données. (Vous pouvez simplement utiliser un 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 de classeur a échoué car elle était trop volumineuse.
Au cours d'un appel de procédure à distance, les arguments et la valeur de retour de l'appel sont transférés sous la forme d'objets Parcel stockés dans la mémoire tampon de transaction du classeur. Si les arguments ou la valeur de retour sont trop volumineux pour tenir dans le tampon de transaction, l'appel échouera et une exception TransactionTooLargeException sera levée.
Lorsque je traite avec la WebView
dans mon application, cela se produit. Je pense que cela est lié à addView
et aux ressources de l'interface utilisateur . Dans mon application, j'ajoute du code dans WebViewActivity
comme ceci ci-dessous, puis ça marche:
@Override
protected void onDestroy() {
if (mWebView != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}
Si vous avez converti Bitmap en Base64 dans des projets et que vous l’avez sauvegardé en tant qu’objet parcellisable, vous devez redimensionner le bitmap avec le code ci-dessous,
remplacez png par jpeg et remplacez les qualités 100 à 75 ou 60:
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, byteArrayOutputStream)
cette solution fonctionne pour moi
Avec autant d'endroits où TransactionTooLargeException peut survenir, voici une nouvelle version d'Android 8: un crash lorsqu'un utilisateur commence simplement à taper dans un EditText si le contenu est trop volumineux.
Cela concerne le AutoFillManager (nouveauté de l'API 26) et le 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 transmettant le composant AutofillManagerClient dans le classeur. Et quand EditText a beaucoup de contenu, il semble causer le TTLE.
Quelques choses peuvent l'atténuer (ou l'ont fait quand même de toute façon): Ajoutez Android:importantForAutofill="noExcludeDescendants"
dans la déclaration de mise en page 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 pourrait également consister à remplacer les méthodes performClick()
et onWindowFocusChanged()
pour intercepter l'erreur dans une sous-classe TextEdit elle-même. Mais je ne pense pas que ce soit vraiment sage ...