สิ่งที่ต้องทำใน TransactionTooLargeException


239

TransactionTooLargeExceptionผมได้ ไม่สามารถทำซ้ำได้ ในเอกสารมันบอกว่า

ธุรกรรม Binder ล้มเหลวเนื่องจากมีขนาดใหญ่เกินไป

ระหว่างการเรียกโพรซีเดอร์แบบรีโมตอาร์กิวเมนต์และค่าส่งคืนของการโทรจะถูกถ่ายโอนเป็นวัตถุพัสดุที่เก็บไว้ในบัฟเฟอร์ธุรกรรม Binder หากอาร์กิวเมนต์หรือค่าส่งคืนมีขนาดใหญ่เกินกว่าที่จะพอดีกับบัฟเฟอร์ของธุรกรรมการโทรจะล้มเหลวและ TransactionTooLargeException จะถูกส่งออกไป

...

มีสองผลลัพธ์ที่เป็นไปได้เมื่อการเรียกขั้นตอนระยะไกลโยน TransactionTooLargeException ไคลเอ็นต์ไม่สามารถส่งคำขอไปยังบริการ (เป็นไปได้มากว่าข้อโต้แย้งมีขนาดใหญ่เกินไปที่จะใส่ในบัฟเฟอร์ของธุรกรรม) หรือบริการไม่สามารถส่งการตอบกลับกลับไปยังไคลเอนต์ได้ มีขนาดใหญ่เกินกว่าที่จะพอดีกับบัฟเฟอร์ของธุรกรรม)

...

ดังนั้นฉันจะผ่านหรือรับข้อโต้แย้งที่เกินขีด จำกัด ที่ไม่รู้จัก ที่ไหน?

stacktrace ไม่แสดงสิ่งใดที่มีประโยชน์:

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)

ดูเหมือนว่าจะเกี่ยวข้องกับมุมมอง? สิ่งนี้เกี่ยวข้องกับการเรียกขั้นตอนระยะไกลอย่างไร

อาจสำคัญ: รุ่น Android: 4.0.3, อุปกรณ์: HTC One X


ไม่ แต่ฉันไม่ได้รับมันอีก มีตัวติดตามข้อผิดพลาดในแอพถ่ายทอดสดและรับเพียงครั้งเดียวในเวลาประมาณ 3 สัปดาห์ อย่างน้อยก็ดูเหมือนจะไม่เกิดขึ้นบ่อยครั้ง อาจคุ้มค่าที่จะเปิดปัญหาที่ Android แม้ว่า ...
Ixx

ฉันไม่มีคำตอบ แต่สิ่งนี้ทำให้ Galaxy S2 ของฉันรีเซ็ตฮาร์ดไดรฟ์ได้อย่างน่าเชื่อถือ
Timmmm

ฉันเพิ่งเกิดเหตุการณ์นี้ขึ้นในแอปพลิเคชันของฉันวันนี้ สิ่งนี้เกิดขึ้นเพียงครั้งเดียวและกับ Galaxy S3 มันน่าสนใจว่านี่น่าจะเป็นเพียงอุปกรณ์ที่มีประสิทธิภาพมากขึ้น
danwms

มีการเพิ่มข้อยกเว้นนั้นใน API 15, developer.android.com/reference/android/os/ ...... และฉันได้ทำซ้ำใน MapView ในขณะที่เลื่อนไปรอบ ๆ แผนที่ . จนกว่า GC เขียนว่าฉันมีหน่วยความจำไม่เหลือ (ผมใช้เวลาไม่กี่นาที)
Meh

บัฟเฟอร์การทำธุรกรรมถูก จำกัด ที่ 1MB ในอุปกรณ์ทั้งหมดและบัฟเฟอร์นี้เก่าทุกธุรกรรม ดังนั้นอุปกรณ์ที่มีประสิทธิภาพยิ่งทำธุรกรรมมากขึ้นก็อาจทำงานพร้อมกันซึ่งทั้งหมดใช้บัฟเฟอร์ 1MB เดียวกัน ที่กล่าวว่าคำตอบของคุณไม่ใช่คำตอบ แต่เป็นความคิดเห็น
3c71

คำตอบ:


158

ฉันพบปัญหานี้และพบว่าเมื่อมีการแลกเปลี่ยนข้อมูลจำนวนมากระหว่างบริการและแอปพลิเคชัน (เกี่ยวข้องกับการถ่ายโอนภาพขนาดย่อจำนวนมาก) ขนาดข้อมูลจริงอยู่ที่ประมาณ 500kb และขนาดบัฟเฟอร์ธุรกรรม IPC ถูกตั้งค่าเป็น 1024KB ฉันไม่แน่ใจว่าทำไมมันเกินบัฟเฟอร์การทำธุรกรรม

สิ่งนี้สามารถเกิดขึ้นได้เช่นกันเมื่อคุณส่งข้อมูลจำนวนมากผ่านความตั้งใจพิเศษ

เมื่อคุณได้รับข้อยกเว้นนี้ในใบสมัครของคุณโปรดวิเคราะห์รหัสของคุณ

  1. คุณแลกเปลี่ยนข้อมูลจำนวนมากระหว่างบริการและแอปพลิเคชันของคุณหรือไม่
  2. การใช้ intents เพื่อแชร์ข้อมูลขนาดใหญ่ (ตัวอย่างเช่นผู้ใช้เลือกไฟล์จำนวนมากจากแชร์แกลเลอรี่แชร์กด URIs ของไฟล์ที่เลือกจะถูกถ่ายโอนโดยใช้ intents)
  3. รับไฟล์บิตแมปจากบริการ
  4. รอให้ android ตอบกลับด้วยข้อมูลขนาดใหญ่ (ตัวอย่างเช่น getInstalledApplications () เมื่อผู้ใช้ติดตั้งแอปพลิเคชันจำนวนมาก)
  5. ใช้ ApplyBatch () กับการดำเนินงานจำนวนมากที่รอดำเนินการ

วิธีจัดการเมื่อคุณได้รับข้อยกเว้นนี้

หากเป็นไปได้ให้แยกการดำเนินการขนาดใหญ่เป็นชิ้นเล็ก ๆ แทนการเรียกใช้ ApplyBatch () ด้วยการปฏิบัติการ 1,000 ครั้งเรียกมันด้วยจำนวน 100 ครั้ง

อย่าแลกเปลี่ยนข้อมูลขนาดใหญ่ (> 1MB) ระหว่างบริการและแอปพลิเคชัน

ฉันไม่รู้วิธีการทำเช่นนี้ แต่อย่าสืบค้น android ซึ่งสามารถส่งคืนข้อมูลขนาดใหญ่ :-)


1
สมมติว่าฉันกำลังตรวจสอบ. apk ของฉันติดตั้งอยู่หรือไม่ เวลาติดตั้ง ... ฉันได้รับข้อยกเว้นเดียวกันขณะตรวจสอบแพ็คเกจ com.test.installedornot.My .apk ของฉันมีขนาดมากกว่า 9MB ในกรณีนี้ฉันจะจัดการข้อยกเว้นนี้ได้อย่างไร
DJhon

15
getInstalledApplicationsฉันได้รับข้อยกเว้นนี้ว่าในระหว่างการโทรไปยัง จะแก้ไขอะไรได้บ้าง?
Stan

1
@Stan API นี้เป็นเรื่องปกติและใช้กันอย่างแพร่หลายทั่วแอพ Android ข้อยกเว้นนี้ทำให้ฉันกังวลจริงๆเมื่อฉันใช้ API นี้
สงบสุข

7
ฉันสามารถยืนยันข้อสรุปของคุณเกี่ยวกับขีด จำกัด ที่บางแห่งประมาณ 500KB แต่เป็นอุปกรณ์เฉพาะในอุปกรณ์บางอย่างที่คุณสามารถถ่ายโอนได้เกือบ 1MB ทั้งหมด ฉันมีข้อยกเว้นนี้เช่นกันดังนั้นฉันจึงทำการตรวจสอบและเขียนโพสต์ที่น่าสนใจสำหรับผู้อ่านที่มีปัญหานี้ nemanjakovacevic.net/blog/english/2015/03/24/…
Nemanja Kovacevic

11
หากคุณพบว่ามันยากที่จะติดตามว่าสถานะใดเป็นสาเหตุของความผิดพลาดคุณอาจพบว่าTooLargeToolมีประโยชน์
Max Spencer

48

หากคุณจำเป็นต้องตรวจสอบพัสดุซึ่งเป็นสาเหตุของความผิดพลาดของคุณคุณควรพิจารณาพยายามTooLargeTool

(ฉันพบสิ่งนี้เป็นความคิดเห็นจาก @Max Spencer ภายใต้คำตอบที่ยอมรับและเป็นประโยชน์ในกรณีของฉัน)


9
วิธีการแก้ปัญหาที่ underrated มากที่สุด เครื่องมือนี้ช่วยให้คุณ จำกัด กิจกรรมที่ละเมิดให้แคบลง
Kedar Paranjape

เครื่องมือนี้ช่วยแก้ไขปัญหาของฉัน ติดตั้งและใช้งานง่าย
Carlos

เครื่องมือนี้ใช้สำหรับ kotlin: / ทางเลือกอื่นสำหรับ java?
maxwellnewage

2
@maxwellnewage: ดูเหมือนว่ารุ่นล่าสุด (0.2.1, 0.2.0 ด้วย) ปัจจุบันไม่ทำงานใน Java-only-applications ฉันต้องใช้เวอร์ชัน 0.1.6 และมันก็ใช้งานได้ดี
heisenberg

การใช้เครื่องมือนี้ฉันตรวจพบว่าฉันใช้มัดขนาดใหญ่ในชิ้นส่วนของฉัน สิ่งที่ฉันทำคือแยกข้อโต้แย้งจากชุดข้อมูลและล้างชุดโดยใช้bundle.clear()
EJ Chathuranga

41

นี่ไม่ใช่คำตอบที่ชัดเจน แต่มันอาจทำให้เข้าใจถึงสาเหตุของ a TransactionTooLargeExceptionและช่วยระบุปัญหา

แม้ว่าคำตอบส่วนใหญ่อ้างถึงการถ่ายโอนข้อมูลจำนวนมาก แต่ฉันเห็นข้อยกเว้นนี้เกิดขึ้นหลังจากการเลื่อนและซูมอย่างหนักและเปิดเมนูสปินเนอร์ของ ActionBar ซ้ำ ๆ ความผิดพลาดเกิดขึ้นเมื่อแตะที่แถบการกระทำ (นี่คือแอปแผนที่ที่กำหนดเอง)

ดูเหมือนว่าข้อมูลเพียงอย่างเดียวที่ถูกส่งผ่านจาก "Input Dispatcher" ไปยังแอป ฉันคิดว่าสิ่งนี้ไม่สามารถเทียบได้กับจำนวน 1 mb ใน "Transaction Buffer" อย่างสมเหตุสมผล

แอปของฉันกำลังทำงานบนอุปกรณ์ quad core 1.6 GHz และใช้ 3 เธรดสำหรับการเพิ่มความหนักเบาทำให้แกนหนึ่งว่างสำหรับเธรด UI นอกจากนี้แอพนี้ใช้ Android: largeHeap มี heap ที่ไม่ได้ใช้ 10 mb เหลืออยู่และมีที่เหลือ 100 mb ในการขยาย heap ดังนั้นฉันจะไม่พูดว่ามันเป็นปัญหาทรัพยากร

การขัดข้องจะถูกนำหน้าในทันทีโดยบรรทัดเหล่านี้:

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

ซึ่งไม่ได้พิมพ์โดยไม่จำเป็นในลำดับนั้น แต่ (เท่าที่ฉันตรวจสอบ) เกิดขึ้นในมิลลิวินาทีเดียวกัน

และสแต็กติดตามตัวเองเพื่อความชัดเจนเป็นเช่นเดียวกับในคำถาม:

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

การขุดลงไปในซอร์สโค้ดของ Android หนึ่งพบว่าบรรทัดเหล่านี้

กรอบ / ฐาน / 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);

สำหรับฉันดูเหมือนว่าฉันอาจตีคุณลักษณะที่ไม่มีเอกสารนี้ซึ่งการทำธุรกรรมล้มเหลวด้วยเหตุผลอื่นนอกเหนือจากธุรกรรมที่กำลัง TooLarge TransactionTooLargeOrAnotherReasonExceptionพวกเขาควรจะตั้งชื่อมันว่า

ในเวลานี้ฉันไม่ได้แก้ปัญหา แต่ถ้าฉันพบสิ่งที่มีประโยชน์ฉันจะอัปเดตคำตอบนี้

ปรับปรุง:มันเปิดรหัสของฉันรั่วไหลออกมาอธิบายไฟล์บางส่วนจำนวนสูงสุดในลินุกซ์ (ปกติ 1024) และดูเหมือนว่าจะมีการเรียกข้อยกเว้น ดังนั้นจึงเป็นปัญหาทรัพยากรหลังจากทั้งหมด ฉันตรวจสอบสิ่งนี้ด้วยการเปิด/dev/zero1024 ครั้งซึ่งส่งผลให้เกิดข้อยกเว้นแปลก ๆ ทุกอย่างในการดำเนินการที่เกี่ยวข้องกับ UI รวมถึงข้อยกเว้นข้างต้นและแม้กระทั่ง SIGSEGV บางอย่าง เห็นได้ชัดว่าความล้มเหลวในการเปิดไฟล์ / ซ็อกเก็ตไม่ใช่สิ่งที่จัดการ / รายงานได้อย่างหมดจดทั่ว Android


36

สิ่งTransactionTooLargeExceptionนี้ได้รับการรบกวนเราเป็นเวลาประมาณ 4 เดือนแล้วและในที่สุดเราก็ได้แก้ไขปัญหานี้แล้ว!

สิ่งที่เกิดขึ้นคือการที่เราจะใช้ในFragmentStatePagerAdapter ViewPagerผู้ใช้จะเลื่อนดูและสร้าง 100 แฟรกเมนต์ (แอปพลิเคชันการอ่าน)

แม้ว่าเราจะจัดการชิ้นส่วนอย่างถูกต้องในdestroyItem()การใช้งาน Android ของFragmentStatePagerAdapterมีข้อผิดพลาดที่มันเก็บการอ้างอิงถึงรายการต่อไปนี้:

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

และเมื่อ Android FragmentStatePagerAdapterพยายามบันทึกสถานะมันจะเรียกใช้ฟังก์ชัน

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

อย่างที่คุณเห็นแม้ว่าคุณจะจัดการชิ้นส่วนในFragmentStatePagerAdapterคลาสย่อยได้อย่างถูกต้องคลาสฐานจะยังคงเก็บFragment.SavedStateชิ้นส่วนทุกชิ้นที่เคยสร้างขึ้น TransactionTooLargeExceptionจะเกิดขึ้นเมื่ออาเรย์ที่ถูกทิ้งไปparcelableArrayและระบบปฏิบัติการจะไม่ชอบมันมากกว่า 100 รายการ

ดังนั้นการแก้ไขสำหรับเราก็คือแทนที่saveState()วิธีการและไม่เก็บอะไร"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;
}

คำตอบที่ดีที่สุดสำหรับฉัน ขอบคุณมาก
pavel

สำหรับความเป็นไปได้ทั้งหมดนี้สำหรับฉันนี่ดูเหมือนจะเป็นทางเลือกเท่านั้น สิ่งที่เก็บไว้ในสถานะที่จะอนุญาตให้มีขนาดนี้ หากจำเป็นต้องมีสถานะแย่มากจนมีรหัสให้บันทึกสิ่งนี้จะไม่ทำให้เกิดปัญหาอื่น ๆ ได้อย่างไร ขอบคุณ
Kenny

คำถามเดียวกับ @Kenny
Mr. Rabbit

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

ฉันแค่ให้ 3 สถานะล่าสุดเท่านั้นตรวจสอบรหัสด้านบน
Ramy Sabry

20

สำหรับผู้ที่ผิดหวังอย่างขมขื่นในการค้นหาคำตอบว่าทำไม TransactionTooLargeException apears พยายามตรวจสอบว่าคุณบันทึกข้อมูลจำนวนเท่าใดในสถานะอินสแตนซ์

ในคอมไพล์ / targetSdkVersion <= 23 เรามีคำเตือนภายในเกี่ยวกับสถานะที่บันทึกไว้จำนวนมาก แต่ไม่มีสิ่งใดขัดข้อง:

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)

แต่ในคอมไพล์ / targetSdkVersion> = 24 เรามีความผิดพลาด RuntimeException จริงในกรณีนี้:

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) 

จะทำอย่างไร?

บันทึกข้อมูลในฐานข้อมูลท้องถิ่นและเก็บเฉพาะสถานะอินสแตนซ์ซึ่งคุณสามารถใช้เพื่อดึงข้อมูลนี้


เป็นไปได้หรือไม่ที่จะใช้เป็นพารามิเตอร์ส่วนกลางและใช้ในภายหลัง
Jitendra ramoliya

@Jitendraramoliya ใช่คุณทำได้ นั่นคือสิ่งที่ฉันหมายถึง
Yazon2006

13

โดยปกติแล้วข้อยกเว้นนี้จะเกิดขึ้นเมื่อแอปถูกส่งไปยังพื้นหลัง

ดังนั้นฉันจึงตัดสินใจใช้วิธีการแยกส่วนข้อมูลเพื่อหลีกเลี่ยงonSavedInstanceStaeวงจรชีวิตทั้งหมด โซลูชันของฉันยังจัดการกับสถานะอินสแตนซ์ที่ซับซ้อนและเพิ่มหน่วยความจำโดยเร็ว

ก่อนอื่นฉันได้สร้าง Fargment ง่าย ๆ เพื่อเก็บข้อมูล:

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

จากนั้นในกิจกรรมหลักของฉันฉันหลีกเลี่ยงรอบอินสแตนซ์ที่บันทึกไว้อย่างสมบูรณ์และเลื่อนการตอบสนองไปยังส่วนข้อมูลของฉัน ไม่จำเป็นต้องใช้สิ่งนี้ในแฟรกเมนต์เองชิ้นส่วนของสถานะจะถูกเพิ่มในสถานะของกิจกรรมโดยอัตโนมัติ):

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

สิ่งที่เหลืออยู่ก็คือการเปิดอินสแตนซ์ที่บันทึกไว้:

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

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

รายละเอียดทั้งหมด: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
จะเกิดอะไรขึ้นถ้ากิจกรรมถูกทำลายเมื่อแอปอยู่ในพื้นหลังและคุณพยายามทำกิจกรรมเบื้องหน้า
Master Disaster

@MasterDisaster ในกรณีนั้นไม่มีสถานะใดถูกบันทึกไว้เนื่องจากกระบวนการที่เก็บส่วนอินสแตนซ์เสียชีวิต
Vaiden

สิทธิดังนั้นกรณีนี้ใช้ได้เฉพาะกับการเปลี่ยนแปลงการกำหนดค่า
Master Disaster

มันจะทำงานเมื่อใดก็ตามที่ระบบปฏิบัติการทริกเกอร์onSavedState()ซึ่งเกิดขึ้นในหลายกรณี การเปลี่ยนแปลงการกำหนดค่าเป็นหนึ่ง การสลับแอพและไปยังพื้นหลังเป็นอีกแอปหนึ่ง และมีมากขึ้น
Vaiden

1
ฉันคิดว่าโซลูชันนี้ควรขยายเพื่อบันทึกข้อมูลจากแหล่งต่าง ๆ อาจเป็น HashMap ที่มีแท็กเป็นคีย์และบันเดิลเป็นค่า ..
CoolMind

11

ไม่มีสาเหตุเฉพาะของปัญหานี้สำหรับฉันในชั้น Fragment ของฉันฉันกำลังทำสิ่งนี้:

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

แทนสิ่งนี้:

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

9

สิ่งสำคัญคือต้องเข้าใจว่าบัฟเฟอร์การทำธุรกรรมถูก จำกัด ที่ 1 MB โดยไม่คำนึงถึงความสามารถของอุปกรณ์หรือแอป บัฟเฟอร์นี้ใช้กับการเรียก API ทุกครั้งที่คุณใช้และแชร์ระหว่างการทำธุรกรรมทั้งหมดที่แอปกำลังทำงานอยู่

ผมเชื่อว่ามันยังถือวัตถุที่เฉพาะเจาะจงบางอย่างเช่นพัสดุและเช่น(Parcel.obtain())ดังนั้นจึงเป็นเรื่องสำคัญที่มักจะตรงกับทุกด้วยobtain()recycle()

ข้อผิดพลาดนี้สามารถเกิดขึ้นได้อย่างง่ายดายบนการเรียก API ที่ส่งคืนข้อมูลจำนวนมากแม้ว่าข้อมูลที่ส่งคืนจะน้อยกว่า 1 MB (หากธุรกรรมอื่นยังคงทำงานอยู่)

ตัวอย่างเช่นการPackageManager.getInstalledApplication()โทรจะส่งคืนรายการแอพทั้งหมดที่ติดตั้ง การเพิ่มการตั้งค่าสถานะเฉพาะช่วยให้สามารถดึงข้อมูลพิเศษมากมาย การทำเช่นนั้นมีแนวโน้มที่จะล้มเหลวดังนั้นจึงไม่แนะนำให้ดึงข้อมูลพิเศษใด ๆ และดึงข้อมูลเหล่านั้นตามแอป

อย่างไรก็ตามการโทรอาจยังล้มเหลวดังนั้นสิ่งสำคัญคือต้องล้อมรอบด้วยcatchและสามารถลองใหม่ได้หากจำเป็น

เท่าที่ฉันรู้ไม่มีการแก้ไขปัญหาดังกล่าวยกเว้นลองใหม่และทำให้แน่ใจว่าได้รับข้อมูลน้อยที่สุดเท่าที่จะทำได้


ขอบคุณสำหรับข้อมูลบัฟเฟอร์นั้นถูกแชร์ในการทำธุรกรรมทั้งหมดภายในแอปพลิเคชัน!
Artem Mostyaev

1
หากเป็นกรณีนี้เหตุใดฉันจึงได้รับบนโทรศัพท์ Android 4.4 เท่านั้นและไม่มีที่ไหนอีกแล้ว ฉันคิดว่ามันเป็นข้อผิดพลาดมากกว่าใน 4.4 ที่ฉันไม่สามารถหาสาเหตุได้
JPM

9

ฉันได้รับข้อยกเว้นนี้เช่นกันใน Samsung S3 ฉันสงสัยสาเหตุที่ 2

  1. คุณมีบิตแมปที่โหลดและใช้หน่วยความจำมากเกินไปใช้การลดขนาด
  2. คุณมีบางสิ่งที่ขาดหายไปจากโฟลเดอร์ drawable-_dpi, android มองหาพวกมันในรูปวาดได้และปรับขนาดทำให้ setContentView ของคุณกระโดดได้ทันทีและใช้หน่วยความจำจำนวนมาก

ใช้ DDMS และดูฮีปของคุณเมื่อคุณเล่นแอพของคุณซึ่งจะทำให้คุณมีข้อบ่งชี้ว่า setcontentview กำลังสร้างปัญหา

ฉันคัดลอก drawable ทั้งหมดในโฟลเดอร์ทั้งหมดเพื่อกำจัดปัญหาที่ 2

ปัญหาได้รับการแก้ไขแล้ว


ข้อยกเว้นหน่วยความจำ / บิตแมปมักจะดูแตกต่างกัน ฉันได้เห็นพวกเขาจำนวนมากทดสอบกับ Android 2.x - 4.x และข้อยกเว้นนั้นดูแตกต่างออกไปเสมอ แต่ใครจะรู้บางทีนี่อาจเกี่ยวข้องด้วย แต่เฉพาะกับรุ่น 4.x
Ixx

10
มันเป็นข้อยกเว้นที่แย่มากเกี่ยวกับข้อมูลเนื่องจากมันไม่ได้ให้เบาะแสว่าปัญหามาจากไหน
Ixx

ฉันคิดว่าไม่กี่วันที่ผ่านมาสิ่งที่คุณค้นพบคืออะไร?
Denny

8

เพิ่มลงในกิจกรรมของคุณ

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

มันใช้งานได้สำหรับฉันหวังว่ามันจะช่วยให้คุณ


7
ฉันคิดว่ามันเป็นคำใบ้ที่อันตรายที่สุด ทำไมเราควรข้อมูลที่ชัดเจนว่าเราต้องการที่จะเรียกคืนในonCreate()?
CoolMind

2
ความหมายของรหัสนี้คือการที่คุณจะจบลงไม่ประหยัดรัฐเช่นคุณ ...
จัสติน

4

ดังนั้นสำหรับเราแล้วเรากำลังพยายามส่งวัตถุที่มีขนาดใหญ่เกินไปผ่านอินเทอร์เฟซ AIDL ของเราไปยังบริการระยะไกล ขนาดของธุรกรรมต้องไม่เกิน 1MB คำร้องขอถูกแบ่งย่อยเป็นส่วนแยกขนาด 512KB และส่งครั้งละหนึ่งอินเทอร์เฟซ วิธีการแก้ปัญหาที่โหดร้ายฉันรู้ แต่เฮ้ - Android :(


4

คุณได้ล้าง InstanceState เก่าของคุณจาก วิธีonSaveInstanceStateและมันจะทำงานได้ดี ฉันกำลังใช้FragmentStatePagerAdapterสำหรับ viewpager ของฉันดังนั้นฉันใช้เมธอด Override ในกิจกรรมหลักของฉันเพื่อล้าง InstanceState

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

ฉันพบโซลูชันนี้จากที่นี่android.os.TransactionTooLargeException บน Nougat


ขอบคุณมาก.
Andrain

ขอบคุณคำตอบของคุณช่วยฉัน
Jatin Patel

3

เร็ว ๆ นี้ผมยังได้พบกับกรณีที่น่าสนใจในขณะที่ทำงานกับ Android ของผู้ให้บริการรายชื่อ

ฉันต้องการโหลดรูปถ่ายของผู้ติดต่อจากฐานข้อมูลผู้ติดต่อภายในและตามโครงสร้างของระบบข้อมูลทั้งหมดจะถูกส่งโดยการสอบถามไปยังผู้ให้บริการที่ติดต่อ

เนื่องจากทำงานเป็นแอปพลิเคชันแยกต่างหาก - การถ่ายโอนข้อมูลทุกประเภททำได้โดยใช้กลไก Binder ดังนั้นบัฟเฟอร์ของ Binder จึงมาที่นี่

ความผิดพลาดหลักของฉันคือการที่ฉันไม่ได้ใกล้Cursorกับข้อมูลหยดอากาศจากผู้ให้บริการรายชื่อเพื่อให้หน่วยความจำที่จัดสรรไว้สำหรับผู้ให้บริการที่เพิ่มขึ้นและสูงขึ้นนี้เครื่องผูก buffer จนฉันได้ตันของ!!!FAILED BINDER TRANSACTION!!!ข้อความในการส่งออก LogCat ของฉัน

ดังนั้นแนวคิดหลักคือเมื่อคุณทำงานกับผู้ให้บริการเนื้อหาภายนอกและได้รับCursorจากพวกเขาให้ปิดมันเสมอเมื่อคุณเสร็จสิ้นการทำงานกับพวกเขา


3

ฉันประสบกับปัญหาเดียวกันเมื่อฉันพยายามส่งบิตแมปผ่านทางเจตนาและในเวลาเดียวกันเมื่อมันเกิดขึ้นฉันพับแอปพลิเคชัน

วิธีที่อธิบายไว้ในบทความนี้ป้อนคำอธิบายการเชื่อมโยงที่นี่มันเกิดขึ้นเมื่อกิจกรรมอยู่ในกระบวนการของการหยุดซึ่งหมายความว่ากิจกรรมพยายามส่งสถานะที่บันทึกไว้ไปยังระบบปฏิบัติการของระบบเพื่อการรักษาที่ปลอดภัยสำหรับการคืนค่าในภายหลัง หรือกระบวนการตาย) แต่ชุดที่ส่งอย่างน้อยหนึ่งชุดมีขนาดใหญ่เกินไป

ฉันแก้ไขมันผ่านการแฮ็คโดยแทนที่ onSaveInstanceState ในกิจกรรมของฉัน:

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

และแสดงความคิดเห็นโทรสุด แฮ็คสกปรก แต่ทำงานได้อย่างสมบูรณ์ บิตแมปถูกส่งเรียบร้อยแล้วโดยไม่ขัดข้อง หวังว่านี่จะช่วยใครซักคน


2

ในกรณีของฉันฉันได้รับ TransactionTooLargeException เป็นความผิดพลาดครั้งที่สองหลังจากไลบรารีดั้งเดิมล้มเหลวด้วย SIGSEGV ความผิดพลาดของไลบรารีดั้งเดิมไม่ได้รับการรายงานดังนั้นฉันจึงได้รับ TransactionTooLargeException เท่านั้น


2

ฉันได้รับสิ่งนี้ใน syncadapter ของฉันเมื่อพยายาม bulkInsert ContentValues ​​ที่มีขนาดใหญ่ [] ฉันตัดสินใจที่จะแก้ไขดังต่อไปนี้:

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

2
เกิดอะไรขึ้นถ้าคนอื่นล้มเหลว? คุณต้องทำการหารให้มากขึ้นโดยใช้การวนซ้ำ อาจมีวิธีในการรับขนาดธุรกรรมและรับขนาดธุรกรรมสูงสุดหรือไม่
นักพัฒนา android

2

สำหรับฉันมันก็เป็นเช่นFragmentStatePagerAdapterนั้น แต่การเอาชนะsaveState()ไม่ได้ผล นี่คือวิธีที่ฉันแก้ไขมัน:

เมื่อเรียกตัวFragmentStatePagerAdapterสร้างให้เก็บรายการของแฟรกเมนต์แยกต่างหากภายในคลาสและเพิ่มวิธีการในการลบแฟรกเมนต์:

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

จากนั้นในActivityบันทึกViewPagerตำแหน่งและการโทรadapter.removeFragments()ในonSaveInstanceState()วิธีการแทนที่:

private int pagerPosition;

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

สุดท้ายในแทนที่วิธีการใหม่อินสแตนซ์อะแดปเตอร์ถ้ามันไม่ได้onResume() null(ถ้าเป็นnullเช่นนั้นActivityจะมีการเปิดเป็นครั้งแรกหรือหลังจากที่แอปถูกฆ่าโดย Android ซึ่งonCreateจะเป็นการสร้างอะแดปเตอร์)

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

1

ตรวจสอบให้แน่ใจว่าคุณไม่ได้ใส่ข้อมูลวัตถุเจตนาที่มีขนาดใหญ่ ในกรณีของฉันฉันเพิ่มขนาดสตริง 500k แล้วเริ่มกิจกรรมอื่น มันล้มเหลวเสมอด้วยข้อยกเว้นนี้ ฉันหลีกเลี่ยงการแบ่งปันข้อมูลระหว่างกิจกรรมโดยใช้ตัวแปรคงที่ของกิจกรรม - คุณไม่จำเป็นต้องส่งพวกเขาไปยัง Intent แล้วดึงมันออกมา

สิ่งที่ฉันมี:

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

นี่เป็นความคิดที่แย่มาก การใช้ตัวแปรแบบคงที่ในลักษณะนั้นคือกลิ่นรหัส นอกจากนี้ลองเรียกใช้รหัสนี้ด้วยการตั้งค่าสถานะ "ไม่เก็บกิจกรรม" และกดปุ่มโฮมหลังจากที่คุณนำทางไปยัง หลังจากที่คุณเปิดกิจกรรมของคุณอีกครั้งคุณอาจจะได้รับความผิดพลาดเนื่องจาก MainActivity จะตาย 100% พร้อมกับตัวแปรทั้งหมด
Kyrylo Zapylaiev

1

เมื่อฉันจัดการกับWebViewในแอพของฉันมันเกิดขึ้น ฉันคิดว่ามันเกี่ยวข้องกับaddViewและแหล่งข้อมูล UI ในแอพของฉันฉันเพิ่มโค้ดบางอย่างในWebViewActivityลักษณะนี้ด้านล่างมันจะทำงานได้ดี:

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

1

ฉันพบสาเหตุของปัญหานี้ (เราได้ทั้ง "การเพิ่มหน้าต่างล้มเหลว" และตัวอธิบายไฟล์รั่วไหลเนื่องจาก mvds บอกว่า)

มีข้อบกพร่องในBitmapFactory.decodeFileDescriptor()Android 4.4 มันเกิดขึ้นเฉพาะเมื่อinPurgeableและinInputShareableของมีการกำหนดให้BitmapOptions trueสิ่งนี้ทำให้เกิดปัญหามากมายในหลาย ๆ ที่ที่มีการโต้ตอบกับไฟล์

MediaStore.Images.Thumbnails.getThumbnail()โปรดทราบว่าวิธีการที่เรียกว่ายังมาจาก

Universal Image Loaderได้รับผลกระทบจากปัญหานี้ PicassoและGlideดูเหมือนจะไม่ได้รับผลกระทบ https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

โค้ดหนึ่งบรรทัดนี้ในเมธอด writeToParcel (ปลายทาง dest, แฟล็ก int) ช่วยให้ฉันกำจัด TransactionTooLargeException ได้

dest=Parcel.obtain(); 

หลังจากรหัสนี้เท่านั้นฉันกำลังเขียนข้อมูลทั้งหมดไปยังวัตถุพัสดุเช่น dest.writeInt () ฯลฯ


1

ลองใช้EventBusหรือContentProviderชอบวิธีแก้ปัญหา

หากคุณอยู่ในกระบวนการเดียวกัน (โดยปกติกิจกรรมทั้งหมดของคุณจะเป็น) ลองใช้EventBusทำให้การแลกเปลี่ยนข้อมูลในกระบวนการไม่ต้องการบัฟเฟอร์ค่อนข้างดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับข้อมูลของคุณมีขนาดใหญ่เกินไป (คุณสามารถใช้วิธีการโทรเพื่อส่งผ่านข้อมูลและ EventBus ซ่อนสิ่งที่น่าเกลียด) นี่คือรายละเอียด:

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

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

ContentProviderหากทั้งสองฝ่ายแสดงเจตจำนงที่ไม่ได้อยู่ในกระบวนการเดียวกันลองบ้าง


ดูที่ธุรกรรมTooLargeException

ธุรกรรม Binder ล้มเหลวเนื่องจากมีขนาดใหญ่เกินไป

ระหว่างการเรียกโพรซีเดอร์แบบรีโมตอาร์กิวเมนต์และค่าส่งคืนของการโทรจะถูกถ่ายโอนเป็นวัตถุพัสดุที่เก็บไว้ในบัฟเฟอร์ธุรกรรม Binder หากอาร์กิวเมนต์หรือค่าส่งคืนมีขนาดใหญ่เกินกว่าที่จะพอดีกับบัฟเฟอร์ของธุรกรรมการโทรจะล้มเหลวและ TransactionTooLargeException จะถูกส่งออกไป


1

ฉันได้รับ TransactionTooLargeException จากข้อผิดพลาดของ Stackoverflow ในการทดสอบ Android Espresso ฉันพบข้อผิดพลาดการติดตามสแต็กของข้อผิดพลาด stackoverflow ในบันทึกเมื่อฉันลบตัวกรอง Logcat สำหรับแอป

ฉันเดาว่าเอสเพรสโซ่เป็นสาเหตุของ TransactionTooLargeException เมื่อพยายามจัดการกับข้อยกเว้นสแต็คขนาดใหญ่มาก


1

หนึ่งสามารถใช้:

android:largeHeap="true"

ในรายการ Android ภายใต้แท็กแอปพลิเคชัน

นี่เป็นการแก้ไขปัญหาในกรณีของฉัน!


ในกรณีของฉัน (เพราะการโทรonSaveInstantStateในกิจกรรม / ชิ้นส่วนและบันทึกรายการขนาดใหญ่) มันไม่ได้ช่วย
CoolMind

1
นี่ถือเป็นการปฏิบัติที่ไม่ดีเพราะคุณไม่ได้จัดการกับสาเหตุที่มีการบันทึกข้อมูลจำนวนมากตั้งแต่แรก สำหรับอุปกรณ์ที่ใหม่กว่าจะทำให้แอปขัดข้องและขีด จำกัด น้อยกว่ามาก (256KB) ตรวจสอบสาเหตุที่คุณต้องเก็บล็อตก่อนและลด
Tim Kist

1

ฉันยังประสบปัญหานี้สำหรับข้อมูลบิตแมปที่ส่งผ่านจากกิจกรรมหนึ่งไปยังอีกกิจกรรมหนึ่ง แต่ฉันแก้ปัญหาด้วยการทำให้ข้อมูลของฉันเป็นข้อมูลแบบสแตติกและทำงานได้สมบูรณ์แบบสำหรับฉัน

ในกิจกรรมแรก:

public static Bitmap bitmap_image;

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

และในกิจกรรมที่สอง:

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

1

สิ่งนี้เกิดขึ้นในแอพของฉันเพราะฉันส่งรายการผลการค้นหาในอาร์กิวเมนต์ของแฟรกเมนต์กำหนดรายการนั้นให้กับคุณสมบัติของแฟรกเมนต์ - ซึ่งจริงๆแล้วเป็นการอ้างอิงไปยังตำแหน่งเดียวกันในหน่วยความจำที่ชี้โดยอาร์กิวเมนต์ของแฟรกเมนต์ - จากนั้นเพิ่ม รายการใหม่เป็นรายการซึ่งเปลี่ยนขนาดของอาร์กิวเมนต์ของแฟรกเมนต์ด้วย เมื่อกิจกรรมถูกระงับคลาสแฟรกเมนต์พื้นฐานพยายามบันทึกอาร์กิวเมนต์ของแฟรกเมนต์ใน onSaveInstanceState ซึ่งจะล้มเหลวหากอาร์กิวเมนต์มีขนาดใหญ่กว่า 1MB ตัวอย่างเช่น:

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

ทางออกที่ง่ายที่สุดในกรณีนี้คือการกำหนดสำเนาของรายการไปยังคุณสมบัติของแฟรกเมนต์แทนที่จะกำหนดการอ้างอิง:

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

ทางออกที่ดียิ่งขึ้นคือการไม่ส่งผ่านข้อมูลจำนวนมากในการขัดแย้ง

ผมอาจจะไม่ได้พบนี้โดยความช่วยเหลือของคำตอบนี้และTooLargeTool


1

ฉันยังมีชีวิตอยู่ TransactionTooLargeException ประการแรกฉันได้ทำงานทำความเข้าใจกับสิ่งที่มันเกิดขึ้น ฉันรู้เหตุผลว่าทำไมมันเกิดขึ้น เราทุกคนรู้เพราะเนื้อหาที่มีขนาดใหญ่ ปัญหาของฉันเป็นเช่นนั้นและฉันแก้ไข บางทีวิธีนี้อาจมีประโยชน์สำหรับใครก็ได้ ฉันมีแอพที่รับเนื้อหาจาก API ฉันได้รับผลลัพธ์จาก API ในหน้าจอแรกและส่งไปที่หน้าจอที่สอง ฉันสามารถส่งเนื้อหานี้ไปที่หน้าจอที่สองได้สำเร็จ หลังจากหน้าจอที่สองหากฉันต้องการไปที่หน้าจอที่สามข้อยกเว้นนี้จะเกิดขึ้น หน้าจอของฉันแต่ละอันถูกสร้างขึ้นจาก Fragment ฉันสังเกตเห็นว่าเมื่อฉันออกจากหน้าจอที่สอง มันบันทึกเนื้อหามัด หากเนื้อหานี้มีขนาดใหญ่เกินไปข้อยกเว้นนี้จะเกิดขึ้น โซลูชันของฉันคือหลังจากที่ฉันได้รับเนื้อหาจากชุดข้อมูลฉันล้างมัน

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

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

ในขณะนี้ถูกต้อง (อัปยศให้ฉันฉันเข้าใจเหมือนกันหลังจากปี) ชิ้นส่วนจะทำอย่างไรถ้ามันสร้างขึ้นใหม่ (การหมุนหน้าจอ)?
CoolMind

0

วิธีแก้ปัญหาคือให้แอปเขียน ArrayList (หรือวัตถุใดก็ตามที่ทำให้เกิดปัญหา) ไปยังระบบไฟล์แล้วส่งการอ้างอิงไปยังไฟล์นั้น (เช่นชื่อไฟล์ / พา ธ ) ผ่านทาง Intent ไปยัง IntentService แล้วปล่อยให้ IntentService ดึงเนื้อหาไฟล์และแปลงกลับเป็น ArrayList

เมื่อ IntentService ได้ทำกับไฟล์แล้วควรลบหรือส่งคำแนะนำกลับไปที่แอพผ่าน Local Broadcast เพื่อลบไฟล์ที่สร้างขึ้น (ส่งกลับการอ้างอิงไฟล์เดียวกันที่ให้มา)

สำหรับข้อมูลเพิ่มเติมดูคำตอบของฉันสำหรับปัญหาที่เกี่ยวข้องนี้


0

ในฐานะที่เป็น Intents, ผู้ให้บริการเนื้อหา, Messenger, บริการระบบทั้งหมดเช่น Telephone, Vibrator ฯลฯ ใช้ประโยชน์จากผู้ให้บริการโครงสร้างพื้นฐาน IPC โดย Binder นอกจากนี้การเรียกคืนระยะเวลากิจกรรมยังใช้โครงสร้างพื้นฐานนี้

1MB เป็นขีด จำกัด โดยรวมของการทำธุรกรรมเครื่องผูกทั้งหมดที่ดำเนินการในระบบในช่วงเวลาหนึ่ง

ในกรณีที่มีการทำธุรกรรมเกิดขึ้นมากมายเมื่อมีการส่งเจตนามันอาจล้มเหลวแม้ว่าข้อมูลเพิ่มเติมจะไม่ใหญ่ http://codetheory.in/an-overview-of-android-binder-framework/


0

ด้วยสถานที่มากมายที่TransactionTooLargeExceptionสามารถเกิดขึ้นได้ - นี่คือสิ่งใหม่สำหรับ Android 8 - ความล้มเหลวเมื่อมีคนเริ่มพิมพ์ EditText หากเนื้อหามีขนาดใหญ่เกินไป

มันเกี่ยวข้องกับAutoFillManager (ใหม่ใน API 26) และรหัสต่อไปนี้ในStartSessionLocked():

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

หากฉันเข้าใจถูกต้องสิ่งนี้จะเรียกใช้บริการป้อนอัตโนมัติ - ผ่าน AutofillManagerClient ภายในเครื่องผูก และเมื่อ EditText มีเนื้อหามากมายดูเหมือนว่าจะทำให้เกิด TTLE

บางสิ่งอาจลดลง (หรือทำตามที่ฉันทดสอบอยู่แล้ว): เพิ่มandroid:importantForAutofill="noExcludeDescendants"ในการประกาศเค้าโครง xml ของ EditText หรือในรหัส:

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

วิธีแก้ปัญหาอันยิ่งใหญ่และน่ากลัวตัวที่สองอาจจะแทนที่performClick()และonWindowFocusChanged()วิธีการตรวจจับข้อผิดพลาดในคลาสย่อย TextEdit เอง แต่ฉันไม่คิดว่ามันฉลาดจริง ๆ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.