nougat - android 7.0 samsung
android.os.TransactionTooLargeException en Nougat (13)
Actualicé Nexus 5X a Android N, y ahora cuando instalo la aplicación (depuración o lanzamiento) en él obtengo TransactionTooLargeException en cada transición de pantalla que tiene Bundle en extras. La aplicación funciona en todos los demás dispositivos. La aplicación anterior que se encuentra en PlayStore y tiene principalmente el mismo código funciona en Nexus 5X. ¿Alguien tiene el mismo problema?
java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 592196 bytes
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3752)
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:6077)
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 592196 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Binder.java:615)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3606)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3744)
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:6077)
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)
En mi caso, utilicé
TooLargeTool
para rastrear de dónde venía el problema y descubrí que la
android:support:fragments
clave
Bundle
de mi
onSaveInstanceState
solía llegar a casi 1mb cuando la aplicación se bloqueó.
Entonces la solución fue como:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.remove("android:support:fragments");
}
Al hacer eso, evité guardar los estados de todos los fragmentos y seguí con otras cosas que necesitan ser guardadas.
A medida que Android N cambia el comportamiento y lanza TransactionTooLargeException en lugar de registrar el error.
try {
if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
ActivityManagerNative.getDefault().activityStopped(
activity.token, state, persistentState, description);
} catch (RemoteException ex) {
if (ex instanceof TransactionTooLargeException
&& activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
return;
}
throw ex.rethrowFromSystemServer();
}
mi solución es conectar la instancia de ActivityMangerProxy e intentar atrapar el método activityStopped.
Aquí está el código:
private boolean hookActivityManagerNative() {
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Field singletonField = ReflectUtils.findField(loader.loadClass("android.app.ActivityManagerNative"), "gDefault");
ReflectUtils.ReflectObject singletonObjWrap = ReflectUtils.wrap(singletonField.get(null));
Object realActivityManager = singletonObjWrap.getChildField("mInstance").get();
Object fakeActivityManager = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{loader.loadClass("android.app.IActivityManager")}, new ActivityManagerHook(realActivityManager));
singletonObjWrap.setChildField("mInstance", fakeActivityManager);
return true;
} catch (Throwable e) {
AppHolder.getThirdPartUtils().markException(e);
return false;
}
}
private static class ActivityManagerHook implements InvocationHandler {
private Object origin;
ActivityManagerHook(Object origin) {
this.origin = origin;
}
public Object getOrigin() {
return origin;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
//ActivityManagerNative.getDefault().activityStopped(activity.token, state, persistentState, description);
case "activityStopped": {
try {
return method.invoke(getOrigin(), args);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
return method.invoke(getOrigin(), args);
}
}
Y la clase de ayuda de reflejo es
public class ReflectUtils {
private static final HashMap<String, Field> fieldCache = new HashMap<>();
private static final HashMap<String, Method> methodCache = new HashMap<>();
public static Field findField(Class<?> clazz, String fieldName) throws Throwable {
String fullFieldName = clazz.getName() + ''#'' + fieldName;
if (fieldCache.containsKey(fullFieldName)) {
Field field = fieldCache.get(fullFieldName);
if (field == null)
throw new NoSuchFieldError(fullFieldName);
return field;
}
try {
Field field = findFieldRecursiveImpl(clazz, fieldName);
field.setAccessible(true);
fieldCache.put(fullFieldName, field);
return field;
} catch (NoSuchFieldException e) {
fieldCache.put(fullFieldName, null);
throw new NoSuchFieldError(fullFieldName);
}
}
private static Field findFieldRecursiveImpl(Class<?> clazz, String fieldName) throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
while (true) {
clazz = clazz.getSuperclass();
if (clazz == null || clazz.equals(Object.class))
break;
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException ignored) {
}
}
throw e;
}
}
public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws Throwable {
String fullMethodName = clazz.getName() + ''#'' + methodName + getParametersString(parameterTypes) + "#exact";
if (methodCache.containsKey(fullMethodName)) {
Method method = methodCache.get(fullMethodName);
if (method == null)
throw new NoSuchMethodError(fullMethodName);
return method;
}
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
methodCache.put(fullMethodName, method);
return method;
} catch (NoSuchMethodException e) {
methodCache.put(fullMethodName, null);
throw new NoSuchMethodError(fullMethodName);
}
}
/**
* Returns an array of the given classes.
*/
public static Class<?>[] getClassesAsArray(Class<?>... clazzes) {
return clazzes;
}
private static String getParametersString(Class<?>... clazzes) {
StringBuilder sb = new StringBuilder("(");
boolean first = true;
for (Class<?> clazz : clazzes) {
if (first)
first = false;
else
sb.append(",");
if (clazz != null)
sb.append(clazz.getCanonicalName());
else
sb.append("null");
}
sb.append(")");
return sb.toString();
}
/**
* Retrieve classes from an array, where each element might either be a Class
* already, or a String with the full class name.
*/
private static Class<?>[] getParameterClasses(ClassLoader classLoader, Object[] parameterTypes) throws ClassNotFoundException {
Class<?>[] parameterClasses = null;
for (int i = parameterTypes.length - 1; i >= 0; i--) {
Object type = parameterTypes[i];
if (type == null)
throw new ClassNotFoundException("parameter type must not be null", null);
if (parameterClasses == null)
parameterClasses = new Class<?>[i + 1];
if (type instanceof Class)
parameterClasses[i] = (Class<?>) type;
else if (type instanceof String)
parameterClasses[i] = findClass((String) type, classLoader);
else
throw new ClassNotFoundException("parameter type must either be specified as Class or String", null);
}
// if there are no arguments for the method
if (parameterClasses == null)
parameterClasses = new Class<?>[0];
return parameterClasses;
}
public static Class<?> findClass(String className, ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader == null)
classLoader = ClassLoader.getSystemClassLoader();
return classLoader.loadClass(className);
}
public static ReflectObject wrap(Object object) {
return new ReflectObject(object);
}
public static class ReflectObject {
private Object object;
private ReflectObject(Object o) {
this.object = o;
}
public ReflectObject getChildField(String fieldName) throws Throwable {
Object child = ReflectUtils.findField(object.getClass(), fieldName).get(object);
return ReflectUtils.wrap(child);
}
public void setChildField(String fieldName, Object o) throws Throwable {
ReflectUtils.findField(object.getClass(), fieldName).set(object, o);
}
public ReflectObject callMethod(String methodName, Object... args) throws Throwable {
Class<?>[] clazzs = new Class[args.length];
for (int i = 0; i < args.length; i++) {
clazzs[i] = args.getClass();
}
Method method = ReflectUtils.findMethodExact(object.getClass(), methodName, clazzs);
return ReflectUtils.wrap(method.invoke(object, args));
}
public <T> T getAs(Class<T> clazz) {
return (T) object;
}
public <T> T get() {
return (T) object;
}
}
}
Al final, mi problema era con las cosas que se guardaban en SaveInstance, y no con las cosas que se enviaban a la siguiente actividad. Eliminé todos los archivos guardados donde no puedo controlar el tamaño de los objetos (respuestas de red), y ahora está funcionando.
Actualizar:
Para preservar grandes cantidades de datos, Google sugiere hacerlo con Fragment que conserva la instancia.
La idea es crear un Fragmento vacío sin una vista con todos los campos necesarios, que de otro modo se guardarían en Bundle.
Añadir
setRetainInstance(true);
al método onCreate de Fragment.
Y luego guarde los datos en Fragment on Activity''s onDestroy y cárguelos en Create.
Aquí hay un ejemplo de Actividad:
public class MyActivity extends Activity {
private DataFragment dataFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
// load the data from the web
dataFragment.setData(loadMyData());
}
// the data is available in dataFragment.getData()
...
}
@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
dataFragment.setData(collectMyLoadedData());
}
}
Y ejemplo de Fragmento:
public class DataFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
Más sobre esto, puedes leer here .
Cada vez que ve que se produce
TransactionTooLargeException
cuando una
Activity
está en proceso de detención, eso significa que la
Activity
estaba tratando de enviar sus
Bundles
estado guardados al sistema operativo del sistema para mantenerlos seguros para su restauración posterior (después de un cambio de configuración o muerte del proceso), pero ese o más de los
Bundles
que envió eran demasiado grandes.
Hay un límite máximo de aproximadamente 1 MB para todas esas transacciones que ocurren a la vez y ese límite se puede alcanzar incluso si ningún
Bundle
excede ese límite.
El principal culpable aquí es generalmente guardar demasiados datos dentro de
onSaveInstanceState
de la
Activity
o de cualquier
Fragments
alojado por la
Activity
.
Por lo general, esto sucede al guardar algo particularmente grande como un
Bitmap
pero también puede ocurrir al enviar grandes cantidades de datos más pequeños, como listas de objetos
Parcelable
.
El equipo de Android ha dejado muy claro en numerosas ocasiones que solo se deben guardar pequeñas cantidades de datos relacionados con la vista en
onSavedInstanceState
.
Sin embargo, los desarrolladores a menudo han guardado páginas de datos de red para que los cambios de configuración parezcan lo más fluidos posible al no tener que volver a buscar los mismos datos nuevamente.
A partir de Google I / O 2017, el equipo de Android ha dejado en claro que la arquitectura preferida para una aplicación de Android guarda datos de red
- en memoria para que pueda reutilizarse fácilmente a través de cambios de configuración
- al disco para que pueda restaurarse fácilmente después de la muerte del proceso y las sesiones de la aplicación
Su nuevo marco
ViewModel
y la biblioteca de persistencia
Room
están destinados a ayudar a los desarrolladores a adaptarse a este patrón.
Si su problema es guardar demasiados datos en
onSaveInstanceState
, la actualización a una arquitectura como esta usando esas herramientas debería solucionar su problema.
Personalmente, antes de actualizar a ese nuevo patrón, me gustaría tomar mis aplicaciones existentes y, mientras tanto, sortear la
TransactionTooLargeException
.
Escribí una biblioteca rápida para hacer precisamente eso:
https://github.com/livefront/bridge
.
Utiliza las mismas ideas generales de restaurar el estado de la memoria a través de los cambios de configuración y del disco después de la muerte del proceso, en lugar de enviar todo ese estado al sistema operativo a través de
onSaveInstanceState
, pero requiere cambios muy mínimos en su código existente para usar.
Sin embargo, cualquier estrategia que se ajuste a esos dos objetivos debería ayudarlo a evitar la excepción, sin sacrificar su capacidad de salvar el estado.
En la nota final aquí: la única razón por la que ve esto en Nougat + es que originalmente si se excedía el límite de transacción de la carpeta, el proceso para enviar el estado guardado al sistema operativo fallaría en silencio con solo este error que aparece en Logcat:
!!! ¡FALTA LA TRANSACCIÓN DE LA BINDER!
En Nougat, esa falla silenciosa se actualizó a un choque duro. Para su crédito, esto es algo que el equipo de desarrollo documentó en las notas de lanzamiento de Nougat :
Muchas API de plataforma ahora han comenzado a buscar cargas útiles grandes que se envían a través de transacciones de Binder, y el sistema ahora vuelve a lanzar TransactionTooLargeExceptions como RuntimeExceptions, en lugar de registrarlas o suprimirlas silenciosamente. Un ejemplo común es almacenar demasiados datos en Activity.onSaveInstanceState (), lo que hace que ActivityThread.StopInfo arroje una RuntimeException cuando su aplicación se dirige a Android 7.0.
El problema en mi aplicación era que estaba tratando de guardar demasiado en el estado guardado, la solución era identificar exactamente qué datos deberían guardarse en el momento adecuado. Básicamente, mire cuidadosamente su onSaveInstanceState para asegurarse de no estirarlo:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user''s current state
// Check carefully what you''re adding into the savedInstanceState before saving it
super.onSaveInstanceState(savedInstanceState);
}
En mi caso, obtuve esa excepción dentro de un fragmento porque uno de sus argumentos era una cadena muy grande que olvidé eliminar (solo usé esa cadena grande dentro del método onViewCreated ()). Entonces, para resolver esto, simplemente eliminé ese argumento. En su caso, debe borrar o anular cualquier campo sospechoso antes de llamar a onPause ().
Código de actividad
Fragment fragment = new Fragment();
Bundle args = new Bundle();
args.putString("extremely large string", data.getValue());
fragment.setArguments(args);
Código de fragmento
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
String largeString = arguments.get("extremely large string");
//Do Something with the large string
arguments.clear() //I forgot to execute this
}
Hice un éxito y una prueba, y finalmente esto resolvió mi problema.
Agregue esto a su
Activity
@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
super.onSaveInstanceState(oldInstanceState);
oldInstanceState.clear();
}
La TransactionTooLargeException nos ha estado atormentando durante aproximadamente 4 meses, ¡y finalmente hemos resuelto el problema!
Lo que sucedía es que estamos usando un FragmentStatePagerAdapter en un ViewPager. El usuario hojearía y crearía más de 100 fragmentos (es una aplicación de lectura).
Aunque gestionamos los fragmentos correctamente en destroyItem (), en la implementación de Android de FragmentStatePagerAdapter hay un error, donde guardaba una referencia a la siguiente lista:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
Y cuando el FragmentStatePagerAdapter de Android intente guardar el estado, llamará a la función
@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 puede ver, incluso si administra adecuadamente los fragmentos en la subclase FragmentStatePagerAdapter, la clase base seguirá almacenando un Fragment.SavedState para cada fragmento creado. La TransactionTooLargeException se produciría cuando esa matriz se volcara a una matriz parcelable y al sistema operativo no le gustaría tener más de 100 elementos.
Por lo tanto, la solución para nosotros fue anular el método saveState () y no almacenar nada para "estados".
@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;
}
Me enfrenté al mismo problema. Mi solución alternativa descarga salvadoInstanceState a los archivos en el caché dir.
Hice la siguiente clase de utilidad.
package net.cattaka.android.snippets.issue;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* To parry BUG of Android N. https://code.google.com/p/android/issues/detail?id=212316
* <p>
* Created by cattaka on 2017/01/12.
*/
public class Issue212316Parrier {
public static final String DEFAULT_NAME = "Issue212316Parrier";
private static final String KEY_STORED_BUNDLE_ID = "net.cattaka.android.snippets.issue.Issue212316Parrier.KEY_STORED_BUNDLE_ID";
private String mName;
private Context mContext;
private String mAppVersionName;
private int mAppVersionCode;
private SharedPreferences mPreferences;
private File mDirForStoredBundle;
public Issue212316Parrier(Context context, String appVersionName, int appVersionCode) {
this(context, appVersionName, appVersionCode, DEFAULT_NAME);
}
public Issue212316Parrier(Context context, String appVersionName, int appVersionCode, String name) {
mName = name;
mContext = context;
mAppVersionName = appVersionName;
mAppVersionCode = appVersionCode;
}
public void initialize() {
mPreferences = mContext.getSharedPreferences(mName, Context.MODE_PRIVATE);
File cacheDir = mContext.getCacheDir();
mDirForStoredBundle = new File(cacheDir, mName);
if (!mDirForStoredBundle.exists()) {
mDirForStoredBundle.mkdirs();
}
long lastStoredBundleId = 1;
boolean needReset = true;
String fingerPrint = (Build.FINGERPRINT != null) ? Build.FINGERPRINT : "";
needReset = !fingerPrint.equals(mPreferences.getString("deviceFingerprint", null))
|| !mAppVersionName.equals(mPreferences.getString("appVersionName", null))
|| (mAppVersionCode != mPreferences.getInt("appVersionCode", 0));
lastStoredBundleId = mPreferences.getLong("lastStoredBundleId", 1);
if (needReset) {
clearDirForStoredBundle();
mPreferences.edit()
.putString("deviceFingerprint", Build.FINGERPRINT)
.putString("appVersionName", mAppVersionName)
.putInt("appVersionCode", mAppVersionCode)
.putLong("lastStoredBundleId", lastStoredBundleId)
.apply();
}
}
/**
* Call this from {@link android.app.Activity#onCreate(Bundle)}, {@link android.app.Activity#onRestoreInstanceState(Bundle)} or {@link android.app.Activity#onPostCreate(Bundle)}
*/
public void restoreSaveInstanceState(@Nullable Bundle savedInstanceState, boolean deleteStoredBundle) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_STORED_BUNDLE_ID)) {
long storedBundleId = savedInstanceState.getLong(KEY_STORED_BUNDLE_ID);
File storedBundleFile = new File(mDirForStoredBundle, storedBundleId + ".bin");
Bundle storedBundle = loadBundle(storedBundleFile);
if (storedBundle != null) {
savedInstanceState.putAll(storedBundle);
}
if (deleteStoredBundle && storedBundleFile.exists()) {
storedBundleFile.delete();
}
}
}
}
/**
* Call this from {@link android.app.Activity#onSaveInstanceState(Bundle)}
*/
public void saveInstanceState(Bundle outState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (outState != null) {
long nextStoredBundleId = mPreferences.getLong("lastStoredBundleId", 1) + 1;
mPreferences.edit().putLong("lastStoredBundleId", nextStoredBundleId).apply();
File storedBundleFile = new File(mDirForStoredBundle, nextStoredBundleId + ".bin");
saveBundle(outState, storedBundleFile);
outState.clear();
outState.putLong(KEY_STORED_BUNDLE_ID, nextStoredBundleId);
}
}
}
private void saveBundle(@NonNull Bundle bundle, @NonNull File storedBundleFile) {
byte[] blob = marshall(bundle);
OutputStream out = null;
try {
out = new GZIPOutputStream(new FileOutputStream(storedBundleFile));
out.write(blob);
out.flush();
out.close();
} catch (IOException e) {
// ignore
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// ignore
}
}
}
}
@Nullable
private Bundle loadBundle(File storedBundleFile) {
byte[] blob = null;
InputStream in = null;
try {
in = new GZIPInputStream(new FileInputStream(storedBundleFile));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
int n;
byte[] buffer = new byte[1024];
while ((n = in.read(buffer)) > -1) {
bout.write(buffer, 0, n); // Don''t allow any extra bytes to creep in, final write
}
bout.close();
blob = bout.toByteArray();
} catch (IOException e) {
// ignore
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// ignore
}
}
}
try {
return (blob != null) ? (Bundle) unmarshall(blob) : null;
} catch (Exception e) {
return null;
}
}
private void clearDirForStoredBundle() {
for (File file : mDirForStoredBundle.listFiles()) {
if (file.isFile() && file.getName().endsWith(".bin")) {
file.delete();
}
}
}
@NonNull
private static <T extends Parcelable> byte[] marshall(@NonNull final T object) {
Parcel p1 = Parcel.obtain();
p1.writeValue(object);
byte[] data = p1.marshall();
p1.recycle();
return data;
}
@SuppressWarnings("unchecked")
@NonNull
private static <T extends Parcelable> T unmarshall(@NonNull byte[] bytes) {
Parcel p2 = Parcel.obtain();
p2.unmarshall(bytes, 0, bytes.length);
p2.setDataPosition(0);
T result = (T) p2.readValue(Issue212316Parrier.class.getClassLoader());
p2.recycle();
return result;
}
}
Códigos completos: https://github.com/cattaka/AndroidSnippets/pull/37
Me preocupa que Parcel # marshall no deba usarse para persistente. Pero no tengo otra idea.
Me enfrento al problema similar. El problema y el escenario son un poco diferentes y lo soluciono de la siguiente manera. Por favor verifique el escenario y la solución.
Escenario: recibí un error extraño del cliente en el dispositivo Google Nexus 6P (7 OS) ya que mi aplicación se bloqueará después de 4 horas de trabajo. Más tarde identifico que está lanzando la excepción similar (android.os.TransactionTooLargeException :).
Solución: el registro no apuntaba a ninguna clase en particular en la aplicación y más tarde descubrí que esto está sucediendo debido a que mantiene la pila de fragmentos. En mi caso, se agregan 4 fragmentos a la pila de respaldo repetidamente con la ayuda de una animación de movimiento de pantalla automática. Así que anulo el onBackstackChanged () como se menciona a continuación.
@Override
public void onBackStackChanged() {
try {
int count = mFragmentMngr.getBackStackEntryCount();
if (count > 0) {
if (count > 30) {
mFragmentMngr.popBackStack(1, FragmentManager.POP_BACK_STACK_INCLUSIVE);
count = mFragmentMngr.getBackStackEntryCount();
}
FragmentManager.BackStackEntry entry = mFragmentMngr.getBackStackEntryAt(count - 1);
mCurrentlyLoadedFragment = Integer.parseInt(entry.getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Si la pila excede el límite, aparecerá automáticamente en el fragmento inicial. Espero que alguien ayude a esta respuesta porque la excepción y los registros de seguimiento de pila son los mismos. Entonces, cuando ocurra este problema, verifique el recuento de la pila de respaldo, si está usando Fragmentos y pila de respaldo.
Ninguna de las respuestas anteriores funcionó para mí, la razón del problema fue bastante simple, como afirman algunos, estaba usando FragmentStatePagerAdapter y su método saveState guarda el estado de los fragmentos, porque uno de mis fragmentos era bastante grande, por lo que guardar este fragmento conduce a esta TransactionTooLargeExecption.
Intenté anular el método saveState en mi implementación del buscapersonas como lo indica @ IK828, pero esto no pudo resolver el bloqueo.
Mi fragmento tenía un EditText que solía contener texto muy grande, que era el culpable del problema en mi caso, así que simplemente en onPause () del fragmento, configuré el texto edittext en una cadena vacía. es decir:
@Override
public void onPause() {
edittext.setText("");
}
Ahora, cuando FragmentStatePagerAdapter intente guardar saveState, este gran fragmento de texto no estará allí para consumir una mayor parte de él, por lo tanto, resuelve el bloqueo.
En su caso, necesita encontrar el culpable, podría ser un ImageView con algún mapa de bits, un TextView con una gran cantidad de texto o cualquier otra vista que consuma mucha memoria, necesita liberar su memoria, puede configurar imageview.setImageResource ( nulo) o similar en onPause () de su fragmento.
actualización: onSaveInstanceState es mejor lugar para el propósito antes de llamar a super como:
@Override
public void onSaveInstanceState(Bundle outState) {
edittext.setText("");
super.onSaveInstanceState(outState);
}
o como lo señaló @Vladimir, puede usar android: saveEnabled = "false" o view.setSaveEnabled (false); en la vista o vista personalizada y asegúrese de volver a configurar el texto en onResume, de lo contrario estará vacío cuando se reanude la actividad.
Simplemente anule este método en su actividad:
@Override
protected void onSaveInstanceState(Bundle outState) {
// below line to be commented to prevent crash on nougat.
// http://blog.sqisland.com/2016/09/transactiontoolargeexception-crashes-nougat.html
//
//super.onSaveInstanceState(outState);
}
Vaya a https://code.google.com/p/android/issues/detail?id=212316#makechanges para obtener más información.
También enfrento este problema en mis dispositivos Nougat. Mi aplicación utiliza un fragmento con un buscapersonas que contiene 4 fragmentos. Pasé algunos argumentos de construcción grandes a los 4 fragmentos que causaron el problema.
Rastreé el tamaño de
Bundle
causando esto con la ayuda de
TooLargeTool
.
Finalmente, lo resolví usando
putSerializable
en un objeto POJO que implementa
Serializable
lugar de pasar una
String
procesar grande usando
putString
durante la inicialización del fragmento.
Este tamaño reducido de
Bundle
a la mitad y no arroja la
TransactionTooLargeException
.
Por lo tanto, asegúrese de no pasar argumentos de gran tamaño a
Fragment
.
Problema relacionado con PS en el rastreador de problemas de Google: https://issuetracker.google.com/issues/37103380