Ich habe Nexus 5X auf Android N aktualisiert und jetzt, wenn ich die App darauf installiere (Debug oder Release), erhalte ich TransactionTooLargeException für jeden Bildschirmübergang, der Bundle in Extras enthält. Die App funktioniert auf allen anderen Geräten. Die alte App, die im PlayStore verfügbar ist und größtenteils denselben Code enthält, funktioniert unter Nexus 5X. Hat jemand das gleiche Problem?
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)
android
android-7.0-nougat
Vladimir Jovanović
quelle
quelle
Antworten:
Wenn Sie sehen
TransactionTooLargeException
, dass ein EreignisActivity
gerade gestoppt wird, bedeutet dies, dassActivity
versucht wurde, seinen gespeicherten StatusBundles
an das Systembetriebssystem zu senden , um ihn später (nach einer Konfigurationsänderung oder einem Prozesstod) sicher wiederherzustellen, aber einer oder mehrere derBundles
es schickte waren zu groß. Es gibt ein maximales Limit von ungefähr 1 MB für alle derartigen Transaktionen, die gleichzeitig stattfinden, und dieses Limit kann erreicht werden, selbst wenn kein EinzelnerBundle
dieses Limit überschreitet.Der Hauptschuldige hier ist im Allgemeinen das Speichern zu vieler Daten innerhalb
onSaveInstanceState
desActivity
oder einesFragments
von dem gehostetenActivity
. In der Regel geschieht dies beim Speichern von besonders großenBitmap
Daten wie a, aber auch beim Senden großer Mengen kleinerer Daten, z. B. vonParcelable
Objektlisten. Das Android-Team hat mehrfach deutlich gemacht, dass nur geringe Mengen an ansichtsbezogenen Daten gespeichert werden solltenonSavedInstanceState
. Entwickler haben jedoch häufig Seiten mit Netzwerkdaten gespeichert, um Konfigurationsänderungen so reibungslos wie möglich zu gestalten, da dieselben Daten nicht erneut abgerufen werden müssen. Ab Google I / O 2017 hat das Android-Team klargestellt, dass die bevorzugte Architektur für eine Android-App Netzwerkdaten speichertDas neue
ViewModel
Framework und dieRoom
Persistenzbibliothek sollen Entwicklern dabei helfen, dieses Muster anzupassen. Wenn Ihr Problem darin besteht, zu viele Daten zu speichernonSaveInstanceState
, sollte das Problem durch Aktualisieren auf eine Architektur wie diese mit diesen Tools behoben werden.Persönlich möchte ich vor dem Aktualisieren auf dieses neue Muster meine vorhandenen Apps übernehmen und mich
TransactionTooLargeException
in der Zwischenzeit einfach darum kümmern . Ich habe eine schnelle Bibliothek geschrieben, um genau das zu tun: https://github.com/livefront/bridge . Es verwendet dieselben allgemeinen Ideen zum Wiederherstellen des Status aus dem Speicher bei Konfigurationsänderungen und von der Festplatte nach dem Prozessabschluss, anstatt den gesamten Status über an das Betriebssystem zu sendenonSaveInstanceState
, erfordert jedoch nur minimale Änderungen an Ihrem vorhandenen Code. Jede Strategie, die zu diesen beiden Zielen passt, sollte Ihnen jedoch helfen, die Ausnahme zu vermeiden, ohne Ihre Fähigkeit zu beeinträchtigen, den Status zu speichern.Zum Schluss hier: Der einzige Grund, warum Sie dies bei Nougat + sehen, ist, dass der Prozess zum Senden des gespeicherten Status an das Betriebssystem ursprünglich fehlschlug, wenn nur das Binder-Transaktionslimit überschritten wurde und nur dieser Fehler in Logcat angezeigt wurde:
!!! FEHLGESCHLAGENE BINDERTRANSAKTION !!!
In Nougat wurde dieser stille Fehler zu einem harten Absturz aufgewertet. Dies ist etwas, was das Entwicklungsteam in den Versionshinweisen für Nougat dokumentiert hat :
quelle
Am Ende war mein Problem mit Dingen, die auf SaveInstance gespeichert wurden, und nicht mit Dingen, die an die nächste Aktivität gesendet wurden. Ich habe alle Speicherungen entfernt, bei denen ich die Größe von Objekten (Netzwerkantworten) nicht steuern kann, und jetzt funktioniert es.
Aktualisieren:
Um große Datenmengen zu erhalten, empfiehlt Google, dies mit Fragment zu tun, das die Instanz beibehält. Die Idee ist, ein leeres Fragment ohne Ansicht mit allen erforderlichen Feldern zu erstellen, die andernfalls im Bundle gespeichert würden. In
setRetainInstance(true);
zu onCreate Methode des Fragments. Speichern Sie dann die Daten in Fragment on onestest von Activity und laden Sie sie onCreate. Hier ist ein Beispiel für Aktivität: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()); } }
Und Beispiel eines Fragments:
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; } }
Mehr dazu können Sie hier lesen .
quelle
Die TransactionTooLargeException plagt uns seit ungefähr 4 Monaten und wir haben das Problem endlich gelöst!
Was geschah, war, dass wir einen FragmentStatePagerAdapter in einem ViewPager verwenden. Der Benutzer blättert durch und erstellt mehr als 100 Fragmente (es ist eine Leseanwendung).
Obwohl wir die Fragmente in destroyItem () ordnungsgemäß verwalten, gibt es in der Androids-Implementierung von FragmentStatePagerAdapter einen Fehler, bei dem auf die folgende Liste verwiesen wird:
private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
Und wenn der FragmentStatePagerAdapter von Android versucht, den Status zu speichern, wird die Funktion aufgerufen
@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; }
Wie Sie sehen können, speichert die Basisklasse auch dann, wenn Sie die Fragmente in der Unterklasse FragmentStatePagerAdapter ordnungsgemäß verwalten, ein Fragment.SavedState für jedes einzelne jemals erstellte Fragment. Die TransactionTooLargeException würde auftreten, wenn dieses Array in ein parcelableArray kopiert wurde und das Betriebssystem nicht mehr als 100 Elemente mag.
Daher bestand die Lösung für uns darin, die saveState () -Methode zu überschreiben und nichts für "Zustände" zu speichern.
@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; }
quelle
Habe einen Treffer und einen Versuch gemacht, und schließlich hat dies mein Problem gelöst. Fügen Sie dies Ihrem hinzu
Activity
@Override protected void onSaveInstanceState(Bundle oldInstanceState) { super.onSaveInstanceState(oldInstanceState); oldInstanceState.clear(); }
quelle
Activity
ist eine schlechte Praxis, alle "Fortschritte" zu löschen, die Benutzer in Ihrem Unternehmen gemacht haben .//Create instance variable, Bundle oldInstance; @Override protected void onSaveInstanceState(Bundle oldInstanceState) { super.onSaveInstanceState(oldInstanceState); //save your instance here, here oldInstance = oldInstanceState oldInstanceState.clear(); }
Ich habe dieses Problem auch auf meinen Nougat-Geräten. Meine App verwendet ein Fragment mit einem Ansichtspager, der 4 Fragmente enthält. Ich habe einige große Konstruktionsargumente an die 4 Fragmente übergeben, die das Problem verursacht haben.
Ich habe die Größe
Bundle
der Ursache mithilfe von TooLargeTool ermittelt .Schließlich habe ich es mit
putSerializable
einem POJO-Objekt gelöst , das während der Fragmentinitialisierung implementiert,Serializable
anstatt ein großes Raw zu übergeben . Diese reduzierte Größe um die Hälfte und wirft die nicht . Stellen Sie daher sicher, dass Sie keine Argumente mit großer Größe an übergeben .String
putString
Bundle
TransactionTooLargeException
Fragment
PS-bezogenes Problem im Google Issue Tracker: https://issuetracker.google.com/issues/37103380
quelle
Ich stehe vor einem ähnlichen Problem. Das Problem und das Szenario unterscheiden sich kaum und ich behebe es folgendermaßen. Bitte überprüfen Sie das Szenario und die Lösung.
Szenario: Der Kunde hat einen seltsamen Fehler im Google Nexus 6P-Gerät (7 OS) erhalten, da meine Anwendung nach 4 Stunden Arbeit abstürzt. Später stelle ich fest, dass es die ähnliche Ausnahme (android.os.TransactionTooLargeException :) auslöst.
Lösung: Das Protokoll zeigte nicht auf eine bestimmte Klasse in der Anwendung, und später stellte ich fest, dass dies geschieht, weil der hintere Stapel von Fragmenten beibehalten wurde. In meinem Fall werden 4 Fragmente mithilfe einer automatischen Bildschirmbewegungsanimation wiederholt zum hinteren Stapel hinzugefügt. Also überschreibe ich das onBackstackChanged () wie unten erwähnt.
@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(); } }
Wenn der Stapel das Limit überschreitet, wird er automatisch zum ursprünglichen Fragment verschoben. Ich hoffe, jemand wird diese Antwort unterstützen, da die Ausnahme- und Stack-Trace-Protokolle identisch sind. Wenn dieses Problem auftritt, überprüfen Sie bitte die Anzahl der Rückstapel, wenn Sie Fragmente und Rückstapel verwenden.
quelle
In meinem Fall habe ich diese Ausnahme in einem Fragment erhalten, weil eines seiner Argumente eine sehr große Zeichenfolge war, die ich vergessen habe, zu löschen (ich habe nur diese große Zeichenfolge in der onViewCreated () -Methode verwendet). Um dies zu lösen, habe ich dieses Argument einfach gelöscht. In Ihrem Fall müssen Sie verdächtige Felder löschen oder aufheben, bevor Sie onPause () aufrufen.
Aktivitätscode
Fragment fragment = new Fragment(); Bundle args = new Bundle(); args.putString("extremely large string", data.getValue()); fragment.setArguments(args);
Fragmentcode
@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 }
quelle
Das Problem in meiner App war, dass ich versucht habe, zu viel im savedInstanceState zu speichern. Die Lösung bestand darin, genau zu identifizieren, welche Daten zum richtigen Zeitpunkt gespeichert werden sollten. Schauen Sie sich Ihren onSaveInstanceState genau an, um sicherzustellen, dass Sie ihn nicht dehnen:
@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); }
quelle
Ich stand vor dem gleichen Problem. Durch meine Problemumgehung werden savedInstanceState in Dateien im Cache-Verzeichnis ausgelagert.
Ich habe die folgende Utility-Klasse erstellt.
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; } }
Vollständige Codes: https://github.com/cattaka/AndroidSnippets/pull/37
Ich mache mir Sorgen, dass Parcel # Marshall nicht für dauerhafte verwendet werden sollte. Aber ich habe keine andere Idee.
quelle
Keine der oben genannten Antworten hat bei mir funktioniert. Der Grund für das Problem war recht einfach, wie von einigen angegeben, die ich mit FragmentStatePagerAdapter verwendet habe. Die saveState-Methode speichert den Status der Fragmente, da eines meiner Fragmente ziemlich groß war, sodass dieses Fragment gespeichert wurde führt zu dieser TransactionTooLargeExecption.
Ich habe versucht, die saveState-Methode in meiner Implementierung von Pager zu überschreiben, wie von @ IK828 angegeben, aber dies konnte den Absturz nicht beheben.
Mein Fragment hatte einen EditText, der früher sehr großen Text enthielt, was in meinem Fall der Grund für das Problem war. In onPause () des Fragments habe ich den edittext-Text einfach auf eine leere Zeichenfolge gesetzt. dh:
@Override public void onPause() { edittext.setText(""); }
Wenn FragmentStatePagerAdapter nun versucht, State zu speichern, ist dieser große Textblock nicht mehr vorhanden, um einen größeren Teil davon zu verbrauchen, wodurch der Absturz behoben wird.
In Ihrem Fall müssen Sie herausfinden, was der Schuldige ist. Dies kann eine ImageView mit einer Bitmap, eine TextView mit einem großen Textblock oder eine andere Ansicht mit hohem Speicherbedarf sein. Sie müssen den Speicher freigeben. Sie können imageview.setImageResource ( null) oder ähnliches in onPause () Ihres Fragments.
Update: onSaveInstanceState ist ein besserer Ort für den Zweck, bevor Sie super aufrufen wie:
@Override public void onSaveInstanceState(Bundle outState) { edittext.setText(""); super.onSaveInstanceState(outState); }
oder wie von @Vladimir gezeigt, können Sie android verwenden: saveEnabled = "false" oder view.setSaveEnabled (false); Stellen Sie in der Ansicht oder in der benutzerdefinierten Ansicht sicher, dass der Text wieder in onResume festgelegt ist. Andernfalls ist er leer, wenn die Aktivität fortgesetzt wird.
quelle
android:saveEnabled="false"
in einer bestimmten Ansicht. Mehr dazu lesen Sie hier .Überschreiben Sie diese Methode einfach für Ihre Aktivität:
@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); }
Weitere Informationen finden Sie unter https://code.google.com/p/android/issues/detail?id=212316#makechanges .
quelle
Wenn Android N das Verhalten ändert und TransactionTooLargeException auslöst, anstatt den Fehler zu protokollieren.
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(); }
Meine Lösung besteht darin, die ActivityMangerProxy-Instanz zu verknüpfen und die activityStopped-Methode abzufangen.
Hier ist der Code:
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); } }
Und die Reflect Helper Klasse ist
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; } } }
quelle
In meinem Fall habe ich TooLargeTool verwendet, um zu verfolgen, woher das Problem stammt, und ich habe herausgefunden, dass der
android:support:fragments
Schlüssel inBundle
meinemonSaveInstanceState
verwendet wurde, um fast 1 MB zu erreichen, als die App abstürzte. Die Lösung war also wie folgt:@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.remove("android:support:fragments"); }
Auf diese Weise habe ich vermieden, die Zustände aller Fragmente zu speichern, und mich an andere Dinge gehalten, die gespeichert werden müssen.
quelle
TransactionTooLargeException
. Wenn das bei Ihnen nicht funktioniert hat, überprüfen Sie besser, was mit github.com/guardian/toolargetool