Phải làm gì trên TransactionTooLargeException


239

Tôi có một TransactionTooLargeException. Không thể tái sản xuất. Trong các tài liệu nó nói

Giao dịch Binder không thành công vì quá lớn.

Trong cuộc gọi thủ tục từ xa, các đối số và giá trị trả về của cuộc gọi được chuyển dưới dạng đối tượng Parcel được lưu trữ trong bộ đệm giao dịch Binder. Nếu các đối số hoặc giá trị trả về quá lớn để phù hợp với bộ đệm giao dịch, thì cuộc gọi sẽ thất bại và TransactionTooLargeException sẽ bị ném.

...

Có hai kết quả có thể xảy ra khi một cuộc gọi thủ tục từ xa ném ra TransactionTooLargeException. Khách hàng không thể gửi yêu cầu của mình đến dịch vụ (rất có thể nếu các đối số quá lớn để phù hợp với bộ đệm giao dịch) hoặc dịch vụ không thể gửi phản hồi của nó cho khách hàng (rất có thể nếu giá trị trả về là quá lớn để phù hợp với bộ đệm giao dịch).

...

Vì vậy, ở đâu đó tôi đang vượt qua hoặc nhận được các đối số vượt quá giới hạn không xác định. Ở đâu?

Stacktrace không hiển thị bất cứ điều gì hữu ích:

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)

Nó dường như có liên quan với quan điểm? Làm thế nào điều này có liên quan đến cuộc gọi thủ tục từ xa?

Có thể quan trọng: Phiên bản Android: 4.0.3, Thiết bị: HTC One X


Không. Nhưng tôi đã không nhận được nó một lần nữa. Có trình theo dõi lỗi trong ứng dụng trực tiếp và chỉ nhận được nó một lần trong khoảng 3 tuần. Ít nhất nó dường như không xảy ra thường xuyên. Có lẽ đáng để mở một vấn đề tại Android mặc dù ...
Ixx

Tôi không có câu trả lời nhưng điều này đáng tin cậy khiến Galaxy S2 của tôi khó thiết lập lại.
Timmmm

Tôi vừa có điều này xảy ra trong một trong những ứng dụng của tôi ngày hôm nay. Điều này cũng chỉ xảy ra một lần và với Galaxy S3. Thật thú vị khi điều này dường như chỉ bắt gặp với các thiết bị mạnh hơn.
danwms

ngoại lệ đó đã được thêm vào API 15, developer.android.com/reference/android/os/NH Và tôi đã sao chép nó trong MapView trong khi cuộn quanh bản đồ. cho đến khi gc viết rằng tôi không còn ký ức. (Tôi mất vài phút)
meh

Bộ đệm giao dịch được giới hạn ở mức 1MB trên tất cả các thiết bị và bộ đệm này cũ hơn mọi giao dịch. Vì vậy, thiết bị càng mạnh thì càng có nhiều giao dịch có thể thực hiện đồng thời, tất cả đều tiêu thụ cùng một bộ đệm 1MB. Điều đó nói rằng, câu trả lời của bạn không phải là một câu trả lời mà là một nhận xét.
3c71

Câu trả lời:


158

Tôi đã gặp phải vấn đề này và tôi thấy rằng khi có một lượng lớn dữ liệu được trao đổi giữa một dịch vụ và một ứng dụng, (Điều này liên quan đến việc chuyển nhiều hình thu nhỏ). Trên thực tế, kích thước dữ liệu khoảng 500kb và kích thước bộ đệm giao dịch IPC được đặt thành 1024KB. Tôi không chắc tại sao nó vượt quá bộ đệm giao dịch.

Điều này cũng có thể xảy ra, khi bạn truyền nhiều dữ liệu thông qua các mục đích bổ sung

Khi bạn nhận được ngoại lệ này trong ứng dụng của mình, vui lòng phân tích mã của bạn.

  1. Bạn đang trao đổi nhiều dữ liệu giữa các dịch vụ và ứng dụng của bạn?
  2. Sử dụng ý định để chia sẻ dữ liệu khổng lồ, (ví dụ: người dùng chọn số lượng lớn tệp từ chia sẻ báo chí chia sẻ, URI của các tệp đã chọn sẽ được chuyển bằng cách sử dụng ý định)
  3. nhận tệp bitmap từ dịch vụ
  4. chờ đợi Android phản hồi lại với dữ liệu khổng lồ (ví dụ: getInstalledAppluggest () khi người dùng cài đặt nhiều ứng dụng)
  5. sử dụng applicationBatch () với nhiều hoạt động đang chờ xử lý

Cách xử lý khi bạn gặp ngoại lệ này

Nếu có thể, hãy chia hoạt động lớn thành các phần nhỏ, ví dụ, thay vì gọi áp dụng ApplBatch () với 1000 thao tác, hãy gọi nó với 100 thao tác.

Không trao đổi dữ liệu lớn (> 1MB) giữa các dịch vụ và ứng dụng

Tôi không biết làm thế nào để làm điều này, nhưng, Đừng truy vấn Android, có thể trả về dữ liệu khổng lồ :-)


1
Giả sử tôi đang kiểm tra .apk của tôi đã được cài đặt hay chưa? tại thời điểm cài đặt ... Tôi nhận được ngoại lệ tương tự trong khi kiểm tra gói com.test.installedornot.My .apk có kích thước lớn hơn 9MB thì trong trường hợp đó tôi sẽ quản lý ngoại lệ này như thế nào?
DJhon

15
Tôi nhận được ngoại lệ này chính xác trong một cuộc gọi đến getInstalledApplications. Có thể làm gì để giải quyết điều này?
Stan

1
@Stan Api này là phổ biến và được sử dụng rộng rãi xung quanh ứng dụng Android. Ngoại lệ này thực sự làm tôi lo lắng bằng cách nào đó khi tôi sử dụng api này.
hòa bình

7
Tôi có thể xác nhận kết luận của bạn về giới hạn ở khoảng 500KB nhưng đó là thiết bị cụ thể, trên một số thiết bị bạn có thể chuyển gần như toàn bộ 1MB. Tôi cũng có ngoại lệ này, vì vậy tôi đã thực hiện một số điều tra và viết một bài đăng có thể thú vị cho những người gặp vấn đề này. nemanjakovacevic.net/blog/english/2015/03/24/ từ
Nemanja Kovacevic

11
Nếu bạn cảm thấy khó theo dõi chính xác trạng thái nào gây ra sự cố của mình thì bạn có thể thấy TooLargeTool hữu ích.
Max Spencer

48

Nếu bạn cần điều tra Parcel nào gây ra sự cố của mình, bạn nên xem xét dùng thử TooLargeTool .

(Tôi thấy đây là nhận xét từ @Max Spencer dưới câu trả lời được chấp nhận và nó rất hữu ích trong trường hợp của tôi.)


9
Giải pháp được đánh giá thấp nhất. Công cụ này giúp bạn thu hẹp các Hoạt động vi phạm
Kedar Paranjape

Công cụ này đã giúp giải quyết vấn đề của tôi. Dễ dàng cài đặt và sử dụng.
Carlos

công cụ này dành cho kotlin: / có sự thay thế nào cho java không?
maxwellnewage

2
@maxwellnewage: có vẻ như phiên bản mới nhất (0.2.1, 0.2.0 cũng vậy) hiện không hoạt động trong các ứng dụng chỉ dành cho Java. Tôi đã phải sử dụng phiên bản 0.1.6 và nó đã hoạt động tốt sau đó
heisenberg

Sử dụng công cụ này tôi phát hiện ra rằng tôi đang sử dụng các bó kích thước khổng lồ trong các mảnh vỡ của mình. Những gì tôi đã làm là trích xuất đối số từ gói và xóa gói bằng cách sử dụngbundle.clear()
EJ Chathuranga

41

Đây không phải là một câu trả lời dứt khoát, nhưng nó có thể làm sáng tỏ nguyên nhân của a TransactionTooLargeExceptionvà giúp xác định vấn đề.

Mặc dù hầu hết các câu trả lời đề cập đến một lượng lớn dữ liệu được truyền, tôi thấy ngoại lệ này được ném ngẫu nhiên sau khi cuộn và phóng to và liên tục mở menu spinner ActionBar. Sự cố xảy ra khi chạm vào thanh hành động. (đây là một ứng dụng bản đồ tùy chỉnh)

Dữ liệu duy nhất được truyền xung quanh dường như được chạm từ "Bộ điều phối đầu vào" cho ứng dụng. Tôi nghĩ rằng điều này không thể hợp lý với số tiền gần 1 mb trong "Bộ đệm giao dịch".

Ứng dụng của tôi đang chạy trên thiết bị lõi tứ 1.6 GHz và sử dụng 3 luồng để truyền tải dữ liệu, giữ một lõi miễn phí cho luồng UI. Hơn nữa, ứng dụng sử dụng android: LargeHeap, còn lại 10 mb heap không sử dụng và còn lại 100 mb để phát triển heap. Vì vậy, tôi sẽ không nói đó là một vấn đề tài nguyên.

Sự cố luôn luôn xảy ra ngay trước những dòng này:

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

Việc này không được in theo thứ tự đó, nhưng (theo như tôi đã kiểm tra) xảy ra trên cùng một mili giây.

Và chính dấu vết ngăn xếp, cho rõ ràng, giống như trong câu hỏi:

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

Đi sâu vào mã nguồn của android người ta tìm thấy những dòng này:

khung / cơ sở / lõi / 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);

Đối với tôi có vẻ như tôi có thể nhấn vào tính năng không có giấy tờ này, trong đó giao dịch không thành công vì các lý do khác ngoài Giao dịch là Quá lớn. Họ nên đặt tên cho nó TransactionTooLargeOrAnotherReasonException.

Tại thời điểm này tôi không giải quyết được vấn đề, nhưng nếu tôi thấy điều gì hữu ích, tôi sẽ cập nhật câu trả lời này.

cập nhật: hóa ra mã của tôi bị rò rỉ một số mô tả tệp, số lượng được tối đa hóa trong linux (thường là 1024) và điều này dường như đã kích hoạt ngoại lệ. Vì vậy, nó là một vấn đề tài nguyên sau khi tất cả. Tôi đã xác minh điều này bằng cách mở /dev/zero1024 lần, dẫn đến tất cả các loại ngoại lệ kỳ lạ trong các hành động liên quan đến giao diện người dùng, bao gồm ngoại lệ ở trên và thậm chí một số SIGSEGV. Rõ ràng việc không mở được tệp / ổ cắm không phải là thứ được xử lý / báo cáo rất rõ ràng trên Android.


36

Điều TransactionTooLargeExceptionnày đã gây khó chịu cho chúng tôi khoảng 4 tháng nay và cuối cùng chúng tôi đã giải quyết được vấn đề!

Chuyện gì đang xảy ra được chúng ta đang sử dụng một FragmentStatePagerAdaptertrong một ViewPager. Người dùng sẽ chuyển qua và tạo ra hơn 100 phân đoạn (đây là một ứng dụng đọc).

Mặc dù chúng tôi quản lý các đoạn đúng cách destroyItem(), nhưng trong quá trình triển khai Android FragmentStatePagerAdaptercó một lỗi, trong đó nó giữ một tham chiếu đến danh sách sau:

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

Và khi Android FragmentStatePagerAdaptercố gắng lưu trạng thái, nó sẽ gọi hàm

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

Như bạn có thể thấy, ngay cả khi bạn quản lý đúng các đoạn trong FragmentStatePagerAdapterlớp con, lớp cơ sở vẫn sẽ lưu trữ một đoạn Fragment.SavedStatecho mỗi đoạn được tạo. Điều TransactionTooLargeExceptionnày sẽ xảy ra khi mảng đó được đổ vào a parcelableArrayvà HĐH sẽ không thích nó hơn 100 mục.

Do đó, cách khắc phục cho chúng tôi là ghi đè saveState()phương thức và không lưu trữ bất cứ thứ gì "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;
}

câu trả lời tốt nhất cho tôi Cảm ơn bạn rất nhiều
pavel

Trong tất cả các khả năng cho điều này, đối với tôi đây dường như chỉ là lựa chọn. Những gì được lưu trữ trong trạng thái sẽ cho phép nó có được kích thước này? Nếu nhà nước cần thiết đến mức có mã để lưu nó, làm thế nào để không gây ra vấn đề khác? Cảm ơn
Kenny

Câu hỏi tương tự như @Kenny
Ông 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

Tôi chỉ để 3 trạng thái cuối cùng, kiểm tra mã ở trên.
Ramy Sabry

20

Đối với những người thất vọng cay đắng khi tìm kiếm câu trả lời về lý do tại sao Giao dịch ngoại lệ bị lỗi, hãy thử kiểm tra xem bạn lưu bao nhiêu thông tin trong trạng thái.

Trên compile / targetSdkVersion <= 23, chúng tôi chỉ có cảnh báo nội bộ về kích thước lớn của trạng thái đã lưu, nhưng không có gì bị lỗi:

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)

Nhưng trên compile / targetSdkVersion> = 24, chúng tôi đã gặp sự cố RuntimeException thực sự trong trường hợp này:

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) 

Phải làm sao?

Lưu dữ liệu trong cơ sở dữ liệu cục bộ và chỉ giữ trạng thái id mà bạn có thể sử dụng để truy xuất dữ liệu này.


Có thể giữ nó làm tham số toàn cầu và sử dụng nó sau này không?
Jitendra ramoliya

@Jitendraramoliya Vâng, bạn có thể. Đó chính xác là những gì tôi muốn nói.
Yazon2006

13

Ngoại lệ này thường được ném khi ứng dụng đang được gửi đến nền.

Vì vậy, tôi đã quyết định sử dụng phương pháp Phân đoạn dữ liệu để tránh hoàn toàn onSavedInstanceStaevòng đời. Giải pháp của tôi cũng xử lý các trạng thái phức tạp và giải phóng bộ nhớ càng sớm càng tốt.

Đầu tiên tôi đã tạo một Fargment đơn giản để lưu trữ dữ liệu:

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

Sau đó, trên Hoạt động chính của tôi, tôi hoàn toàn phá vỡ chu trình cá thể đã lưu và trì hoãn trách nhiệm đối với Phân đoạn dữ liệu của tôi. Không cần sử dụng điều này trên các Mảnh vỡ, sice trạng thái của chúng được tự động thêm vào trạng thái Hoạt động):

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

Những gì còn lại chỉ đơn giản là để bật phiên bản đã lưu:

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

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

Chi tiết đầy đủ: http://www.devsbedevin.net/avoiding-transactiontoolargeexception-on-android-nougat-and-up/


1
Điều gì xảy ra nếu hoạt động bị hủy khi ứng dụng ở chế độ nền và bạn cố gắng tiến hành hoạt động?
Thảm họa tổng thể

@MasterDisaster trong trường hợp đó không có trạng thái nào được lưu vì quá trình giữ đoạn thể hiện đã chết.
Vaiden

Quyền, vì vậy trường hợp này chỉ hoạt động với thay đổi cấu hình.
Thảm họa tổng thể

Nó hoạt động bất cứ khi nào hệ điều hành kích hoạt onSavedState(), xảy ra trong nhiều trường hợp. Thay đổi cấu hình là một. Chuyển đổi ứng dụng và đi đến nền là một cái khác. Và có nhiều hơn nữa.
Vaiden

1
Tôi nghĩ giải pháp này nên được mở rộng để lưu dữ liệu từ các nguồn khác nhau. Có lẽ là HashMap với các thẻ là khóa và gói dưới dạng giá trị ..
CoolMind

11

Không có một nguyên nhân cụ thể nào cho vấn đề này. Đối với tôi, trong lớp Fragment của tôi, tôi đã làm điều này:

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

thay vì điều này:

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

9

Điều quan trọng là phải hiểu rằng bộ đệm giao dịch được giới hạn ở 1 MB, bất kể khả năng hay ứng dụng của thiết bị. Bộ đệm này được sử dụng với mọi lệnh gọi API bạn thực hiện và được chia sẻ giữa tất cả các giao dịch mà ứng dụng hiện đang chạy.

Tôi tin rằng nó cũng chứa một số đối tượng cụ thể như bưu kiện và (Parcel.obtain())vì vậy, điều quan trọng là luôn luôn khớp mọi thứ obtain()với a recycle().

Lỗi này có thể dễ dàng xảy ra đối với các lệnh gọi API trả về rất nhiều dữ liệu, mặc dù dữ liệu được trả về ít hơn 1 MB (nếu các giao dịch khác vẫn đang chạy).

Ví dụ: PackageManager.getInstalledApplication()cuộc gọi trả về danh sách tất cả các ứng dụng được cài đặt. Thêm cờ cụ thể cho phép lấy nhiều dữ liệu bổ sung. Làm như vậy có khả năng thất bại, do đó, không nên truy xuất bất kỳ dữ liệu bổ sung nào và truy xuất dữ liệu đó trên cơ sở mỗi ứng dụng.

Tuy nhiên, cuộc gọi vẫn có thể thất bại, vì vậy điều quan trọng là phải bao quanh nó catchvà có thể thử lại nếu cần.

Theo như tôi biết, không có vấn đề nào liên quan đến vấn đề như vậy ngoại trừ thử lại và đảm bảo lấy được càng ít thông tin càng tốt.


Cảm ơn bạn đã thông tin, bộ đệm đó được chia sẻ giữa tất cả các giao dịch trong ứng dụng!
Artem Mostyaev

1
Nếu đây là trường hợp thì tại sao tôi chỉ nhận được trên điện thoại Android 4.4 và không có nơi nào khác. Tôi nghĩ rằng đó là một lỗi trong 4.4 mà tôi không thể hiểu tại sao.
JPM

9

Tôi cũng có ngoại lệ này trên Samsung S3. Tôi nghi ngờ 2 nguyên nhân gốc rễ,

  1. bạn có bitmap tải và chiếm quá nhiều bộ nhớ, sử dụng thu nhỏ
  2. Bạn có một số drawable bị thiếu trong các thư mục drawable-_dpi, android sẽ tìm chúng trong drawable và thay đổi kích thước chúng, làm cho setContentView của bạn đột nhiên nhảy lên và sử dụng rất nhiều bộ nhớ.

Sử dụng DDMS và xem heap của bạn khi bạn chơi ứng dụng của mình, điều đó sẽ cho bạn một số dấu hiệu cho thấy setcontentview đang tạo ra vấn đề.

Tôi đã sao chép tất cả các drawable trên tất cả các thư mục để thoát khỏi vấn đề 2.

Vấn đề đã được giải quyết.


Các ngoại lệ bộ nhớ / bitmap thường trông khác nhau. Tôi đã thấy rất nhiều người trong số họ thử nghiệm với Android 2.x - 4.x và các ngoại lệ luôn có vẻ khác nhau. Nhưng, ai biết được, có lẽ điều này cũng liên quan nhưng cụ thể với các phiên bản 4.x.
Ixx

10
Đó chỉ là một ngoại lệ khủng khiếp, liên quan đến thông tin, vì nó không đưa ra bất kỳ manh mối nào về vấn đề đến từ đâu.
Ixx

Tôi nghĩ rằng vài ngày đã qua, những phát hiện của bạn là gì?
Denny

8

Thêm phần này vào Hoạt động của bạn

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

Nó làm việc cho tôi hy vọng nó cũng sẽ giúp bạn


7
Tôi nghĩ rằng, đó là gợi ý có hại nhất. Tại sao chúng ta nên xóa dữ liệu mà chúng ta muốn khôi phục onCreate()?
CoolMind

2
Ý nghĩa của mã này là bạn cuối cùng KHÔNG lưu trạng thái cá thể của bạn ...
Justin

4

Vì vậy, đối với chúng tôi, chúng tôi đã cố gắng gửi một đối tượng quá lớn thông qua giao diện AIDL của chúng tôi đến một dịch vụ từ xa. Kích thước giao dịch không thể vượt quá 1MB. Yêu cầu được chia thành các phần riêng biệt 512KB và được gửi từng lần thông qua giao diện. Một giải pháp tàn bạo mà tôi biết nhưng hey - Android của nó :(


4

Bạn đã xóa InstanceState cũ của mình khỏi phương thức onSaveInstanceState và nó sẽ hoạt động tốt. Tôi đang sử dụng FragmentStatePagerAdOG cho chế độ xem của mình vì vậy tôi giữ phương thức Ghi đè bên dưới vào hoạt động chính của mình để xóa InstanceState.

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

Tôi đã tìm thấy giải pháp này từ đây android.os.TransactionTooLargeException trên Nougat


Cảm ơn bạn rất nhiều.
Andrain

Cảm ơn câu trả lời của bạn giúp tôi
Jatin Patel

3

Gần đây tôi cũng đã gặp một trường hợp thú vị khi làm việc với Nhà cung cấp danh bạ của Android .

Tôi cần tải ảnh danh bạ từ cơ sở dữ liệu danh bạ nội bộ và theo kiến ​​trúc hệ thống, tất cả dữ liệu này được gửi bằng truy vấn đến Nhà cung cấp danh bạ.

Vì nó hoạt động như một ứng dụng riêng biệt - tất cả các loại truyền dữ liệu được thực hiện bằng cách sử dụng cơ chế Binder và do đó, bộ đệm Binder được sử dụng ở đây.

Sai lầm chính của tôi là tôi đã không đóng các Cursordữ liệu blob nhận từ Danh bạ nhà cung cấp, do đó bộ nhớ được phân bổ cho các nhà cung cấp tăng và điều này thổi phồng Binder đệm cho đến khi tôi nhận được tấn của !!!FAILED BINDER TRANSACTION!!!thông điệp về sản lượng LogCat tôi.

Vì vậy, ý tưởng chính là khi bạn làm việc với Nhà cung cấp nội dung bên ngoài và nhận được Cursortừ họ, hãy luôn đóng nó khi bạn hoàn thành để làm việc với họ.


3

Tôi đã đối mặt với cùng một vấn đề khi tôi cố gắng gửi bitmap qua Intent và đồng thời khi nó xảy ra, tôi đã gấp ứng dụng.

Cách nó được mô tả trong bài viết này, nhập mô tả liên kết vào đây xảy ra khi một Hoạt động đang trong quá trình dừng, điều đó có nghĩa là Hoạt động đó đang cố gắng gửi Gói trạng thái đã lưu của nó đến HĐH hệ thống để giữ an toàn cho việc khôi phục sau này (sau khi thay đổi cấu hình hoặc xử lý cái chết) nhưng một hoặc nhiều Gói mà nó gửi quá lớn.

Tôi đã giải quyết nó thông qua hack bằng cách ghi đè onSaveInstanceState trong Hoạt động của tôi:

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

và bình luận gọi siêu. Đó là một hack bẩn nhưng nó đang hoạt động hoàn hảo. Bitmap đã được gửi thành công mà không gặp sự cố. Hy vọng điều này sẽ giúp được ai đó.


2

Trong trường hợp của tôi, tôi nhận được TransactionTooLargeException là một sự cố thứ cấp sau khi thư viện gốc bị sập với SIGSEGV. Sự cố thư viện riêng không được báo cáo vì vậy tôi chỉ nhận được TransactionTooLargeException.


2

Tôi đã nhận được điều này trong chương trình đồng bộ hóa của mình khi cố gắng tạo ra số lượng lớn ContentValues ​​[]. Tôi quyết định sửa nó như sau:

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
Nếu cái kia thất bại thì sao? Bạn cần phải chia nhiều hơn, sử dụng một vòng lặp. Có lẽ có một cách để có được kích thước giao dịch và có được kích thước giao dịch tối đa?
nhà phát triển Android

2

Đối với tôi đó cũng là FragmentStatePagerAdapter, tuy nhiên việc ghi đè saveState()không hoạt động. Đây là cách tôi sửa nó:

Khi gọi hàm FragmentStatePagerAdaptertạo, hãy giữ một danh sách các đoạn riêng biệt trong lớp và thêm một phương thức để loại bỏ các đoạn:

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

Sau đó Activity, lưu ViewPagervị trí và gọi adapter.removeFragments()theo onSaveInstanceState()phương thức ghi đè :

private int pagerPosition;

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

Cuối cùng, trong onResume()phương thức ghi đè , khởi tạo lại bộ điều hợp nếu không null. (Nếu là vậy null, thì nó Activitysẽ được mở lần đầu tiên hoặc sau khi ứng dụng bị Android tắt, trong đó onCreatesẽ thực hiện việc tạo bộ điều hợp.)

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

1

Đảm bảo rằng bạn không đưa vào dữ liệu đối tượng Ý định có kích thước lớn. Trong trường hợp của tôi, tôi đã thêm kích thước Chuỗi 500k và sau đó bắt đầu một hoạt động khác. Nó luôn thất bại với ngoại lệ này. Tôi đã tránh chia sẻ dữ liệu giữa các hoạt động bằng cách sử dụng các biến hoạt động tĩnh - bạn không phải gửi chúng đến Ý định và sau đó rút từ đó.

Những gì tôi đã có:

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

Đây là ý tưởng rất xấu. Sử dụng các biến tĩnh theo cách đó là một mùi mã. Ngoài ra, hãy thử chạy mã này với cờ "Không giữ hoạt động" và nhấn home sau khi bạn điều hướng đến PageWebView. Sau khi bạn mở lại hoạt động của mình, bạn có thể sẽ gặp sự cố vì MainActivity sẽ chết 100% với tất cả các biến của nó.
Kyrylo Zapylaiev

1

Khi tôi đang làm việc với WebViewứng dụng của mình thì nó xảy ra. Tôi nghĩ nó liên quan đến addViewvà tài nguyên UI. Trong ứng dụng của tôi, tôi thêm một số mã WebViewActivitynhư thế này bên dưới để nó chạy ok:

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

1

Tôi đã tìm ra nguyên nhân gốc rễ của việc này (chúng tôi đã nhận được cả "việc thêm cửa sổ thất bại" và rò rỉ mô tả tệp như mvds nói).

Có một lỗi trong BitmapFactory.decodeFileDescriptor()Android 4.4. Nó chỉ xảy ra khi inPurgeableinInputShareablecủa BitmapOptionsđược thiết lập để true. Điều này gây ra nhiều vấn đề ở nhiều nơi tương tác với các tập tin.

Lưu ý rằng phương thức cũng được gọi từ MediaStore.Images.Thumbnails.getThumbnail().

Universal Image Loader bị ảnh hưởng bởi vấn đề này. PicassoGlide dường như không bị ảnh hưởng. https://github.com/nostra13/Android-Universal-Image-Loader/issues/1020


1

Chính một dòng mã này trong phương thức writeToParcel (Parcel Dest, int flags) đã giúp tôi thoát khỏi TransactionTooLargeException.

dest=Parcel.obtain(); 

Sau mã này chỉ có tôi đang viết tất cả dữ liệu cho đối tượng bưu kiện, ví dụ: Dest.writeInt (), v.v.


1

Cố gắng sử dụng EventBushoặc ContentProviderthích giải pháp.

Nếu bạn đang ở trong cùng một quy trình (thông thường tất cả các hoạt động của bạn sẽ như vậy), hãy thử sử dụng EventBus, vì trong quá trình trao đổi dữ liệu KHÔNG cần một bộ đệm, do đó bạn không cần phải lo lắng về dữ liệu của mình quá lớn. (Bạn chỉ có thể sử dụng phương thức gọi để truyền dữ liệu thực sự và EventBus ẩn những thứ xấu xí) Dưới đây là chi tiết:

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

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

Nếu hai bên của Ý định không cùng quy trình, hãy thử phần nào ContentProvider.


Xem TransactionTooLargeException

Giao dịch Binder không thành công vì quá lớn.

Trong cuộc gọi thủ tục từ xa, các đối số và giá trị trả về của cuộc gọi được chuyển dưới dạng đối tượng Parcel được lưu trữ trong bộ đệm giao dịch Binder. Nếu các đối số hoặc giá trị trả về quá lớn để phù hợp với bộ đệm giao dịch, thì cuộc gọi sẽ thất bại và TransactionTooLargeException sẽ bị ném.


1

Tôi đã nhận được một TransactionTooLargeException từ lỗi Stackoverflow trong thử nghiệm Android Espresso. Tôi đã tìm thấy dấu vết ngăn xếp lỗi stackoverflow trong nhật ký khi tôi tắt bộ lọc Logcat cho ứng dụng của mình.

Tôi đoán rằng Espresso đã gây ra Giao dịch ngoại lệ khi cố gắng xử lý một ngăn xếp ngoại lệ thực sự lớn.


1

Người ta có thể sử dụng:

android:largeHeap="true"

trong Bản kê khai Android dưới thẻ ứng dụng.

Điều này đã giải quyết vấn đề trong trường hợp của tôi!


Trong trường hợp của tôi (vì gọi onSaveInstantStatecác hoạt động / phân đoạn và lưu danh sách lớn), điều đó không có ích.
CoolMind

1
Đây được coi là thông lệ xấu vì bạn không giải quyết được lý do tại sao có nhiều dữ liệu được lưu ngay từ đầu. Trên các thiết bị mới hơn, nó bị sập ứng dụng và giới hạn nhỏ hơn nhiều (256KB). Điều tra lý do tại sao bạn lưu trữ rất nhiều đầu tiên và giảm nó.
Tim Kist

1

Ngoài ra tôi đã phải đối mặt với vấn đề này khi dữ liệu Bitmap chuyển từ hoạt động này sang hoạt động khác nhưng tôi tạo ra giải pháp bằng cách biến dữ liệu của mình thành dữ liệu tĩnh và điều này hoạt động hoàn hảo với tôi

Trong hoạt động đầu tiên:

public static Bitmap bitmap_image;

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

và trong hoạt động thứ hai:

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

1

Điều này đã xảy ra trong ứng dụng của tôi vì tôi đã chuyển một danh sách kết quả tìm kiếm trong các đối số của một đoạn, gán danh sách đó cho một thuộc tính của đoạn - thực sự là một tham chiếu đến cùng một vị trí trong bộ nhớ được chỉ ra bởi các đối số của đoạn - sau đó thêm các mục mới vào danh sách, cũng thay đổi kích thước của các đối số của đoạn. Khi hoạt động bị đình chỉ, lớp phân đoạn cơ sở cố gắng lưu các đối số của phân đoạn trong onSaveInstanceState, sẽ gặp sự cố nếu các đối số lớn hơn 1MB. Ví dụ:

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

Giải pháp đơn giản nhất trong trường hợp này là gán một bản sao của danh sách cho thuộc tính của đoạn thay vì gán tham chiếu:

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

Một giải pháp thậm chí tốt hơn là không vượt qua quá nhiều dữ liệu trong các đối số.

Tôi có lẽ sẽ không bao giờ tìm thấy điều này nếu không có sự giúp đỡ của câu trả lời nàyTooLargeTool .


1

Tôi cũng đã sống TransactionTooLargeException. Đầu tiên tôi đã làm việc để hiểu nó xảy ra ở đâu. Tôi biết lý do tại sao nó xảy ra. Mọi người trong chúng ta đều biết vì nội dung lớn. Vấn đề của tôi là như thế và tôi đã giải quyết. Có lẽ giải pháp này có thể hữu ích cho bất cứ ai. Tôi có một ứng dụng lấy nội dung từ api. Tôi nhận được kết quả từ API trong màn hình đầu tiên và gửi nó đến màn hình thứ hai. Tôi có thể gửi nội dung này đến màn hình thứ hai thành công. Sau màn hình thứ hai nếu tôi muốn đi màn hình thứ ba thì ngoại lệ này xảy ra. Mỗi màn hình của tôi được tạo ra từ Fragment. Tôi nhận thấy rằng khi tôi rời khỏi màn hình thứ hai. Nó lưu nội dung bó của nó. nếu nội dung này quá lớn thì ngoại lệ này sẽ xảy ra. Giải pháp của tôi là sau khi tôi nhận được nội dung từ gói tôi xóa nó.

class SecondFragment : BaseFragment() {

    lateinit var myContent: MyContent

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

Trong khi điều này là đúng (xấu hổ với tôi, tôi đã hiểu tương tự sau một năm), mảnh vỡ sẽ làm gì nếu nó tái tạo (xoay màn hình)?
CoolMind

0

Một giải pháp sẽ là cho ứng dụng ghi ArrayList (hoặc bất kỳ đối tượng nào gây ra sự cố) vào hệ thống tệp, sau đó chuyển tham chiếu đến tệp đó (ví dụ: tên tệp / đường dẫn) thông qua IntentService và sau đó để IntentService lấy lại nội dung tệp và chuyển đổi nó trở lại thành một ArrayList.

Khi IntentService đã thực hiện với tệp, nó sẽ xóa nó hoặc chuyển hướng dẫn trở lại ứng dụng thông qua Broadcast Broadcast để xóa tệp mà nó đã tạo (trả lại cùng một tham chiếu tệp được cung cấp cho nó).

Để biết thêm thông tin xem câu trả lời của tôi cho vấn đề liên quan này .


0

Như Ý định, Nhà cung cấp nội dung, Messenger, tất cả các dịch vụ hệ thống như Điện thoại, Máy rung, v.v đều sử dụng nhà cung cấp cơ sở hạ tầng IPC của Binder. Ngoài ra, các cuộc gọi lại vòng đời hoạt động cũng sử dụng cơ sở hạ tầng này.

1MB là giới hạn tổng thể cho tất cả các giao dịch liên kết được thực hiện trong hệ thống tại một thời điểm cụ thể.

Trong trường hợp có rất nhiều giao dịch xảy ra khi ý định được gửi, nó có thể thất bại mặc dù dữ liệu bổ sung không lớn. http://codetheory.in/an-overview-of-android-binder-framework/


0

Với rất nhiều nơi TransactionTooLargeException thể happen-- đây là thêm một mới cho Android 8 - một vụ tai nạn khi một người nào đó chỉ đơn thuần là bắt đầu gõ vào một EditText nếu nội dung quá lớn.

Nó liên quan đến AutoFillManager (mới trong API 26) và mã sau đây trong StartSessionLocked():

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

Nếu tôi hiểu chính xác, điều này sẽ gọi dịch vụ tự động điền - chuyển AutofillManagerClient trong chất kết dính. Và khi EditText có nhiều nội dung, nó dường như gây ra TTLE.

Một vài điều có thể giảm thiểu nó (hoặc đã làm như tôi đang thử nghiệm): Thêm android:importantForAutofill="noExcludeDescendants"vào khai báo bố cục xml của EditText. Hoặc trong mã:

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

Cách giải quyết khủng khiếp thứ 2 cũng có thể là ghi đè performClick()onWindowFocusChanged()các phương thức để bắt lỗi trong chính lớp con TextEdit. Nhưng tôi không nghĩ điều đó thực sự khôn ngoan ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.