TransactionTooLargeException에서 수행 할 작업 또는 반환 값이

나는 TransactionTooLargeException. 재현 할 수 없습니다. 문서에서 그것은 말합니다

바인더 트랜잭션이 너무 커서 실패했습니다.

원격 프로 시저 호출 동안, 인수 및 호출의 리턴 값은 바인더 트랜잭션 버퍼에 저장된 Parcel 오브젝트로 전송됩니다. 인수 또는 반환 값이 너무 커서 트랜잭션 버퍼에 맞지 않으면 호출이 실패하고 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)

조회수와 관련이있는 것 같습니다. 이것은 원격 프로 시저 호출과 어떤 관련이 있습니까?

어쩌면 중요한 : 안드로이드 버전 : 4.0.3, 기기 : HTC One X



답변

이 문제가 발생하여 서비스와 응용 프로그램간에 엄청난 양의 데이터가 교환되는 경우 많은 썸네일을 전송합니다. 실제로 데이터 크기는 약 500kb이고 IPC 트랜잭션 버퍼 크기는 1024KB로 설정되어 있습니다. 왜 트랜잭션 버퍼를 초과했는지 잘 모르겠습니다.

인 텐트 엑스트라를 통해 많은 양의 데이터를 전달할 때도 발생할 수 있습니다.

응용 프로그램에서이 예외가 발생하면 코드를 분석하십시오.

  1. 서비스와 응용 프로그램간에 많은 데이터를 교환하고 있습니까?
  2. 인 텐트를 사용하여 대용량 데이터 공유 (예 : 사용자가 갤러리 공유 프레스 공유에서 많은 수의 파일을 선택하면 선택한 파일의 URI가 인 텐트를 사용하여 전송 됨)
  3. 서비스에서 비트 맵 파일 받기
  4. 사용자가 많은 응용 프로그램을 설치할 때 getInstalledApplications ()와 같이 Android가 큰 데이터로 응답을 기다리는 경우
  5. 많은 작업이 보류중인 applyBatch () 사용

이 예외가 발생했을 때 처리하는 방법

가능한 경우 큰 작업을 작은 청크로 분할합니다 (예 : 1000 개의 작업으로 applyBatch ()를 호출하는 대신 100 개씩 호출).

서비스와 응용 프로그램간에 거대한 데이터 (> 1MB)를 교환하지 마십시오

나는 이것을하는 방법을 모른다. 그러나 거대한 데이터를 반환 할 수있는 안드로이드를 쿼리하지 마라 🙂


답변

충돌을 일으키는 Parcel을 조사해야하는 경우 TooLargeTool 시도를 고려해야 합니다.

(이 답변을 @Max Spencer의 의견으로 받아 들였으며 제 경우 도움이되었습니다.)


답변

이것은 명확한 대답은 아니지만 TransactionTooLargeException문제 의 원인을 밝히고 문제를 정확히 찾아내는 데 도움이 될 수 있습니다.

대부분의 답변은 전송 된 많은 양의 데이터를 언급하지만 ActionBar 스피너 메뉴를 많이 스크롤하고 확대 / 축소하고 반복적으로 열면이 예외가 우연히 발생합니다. 작업 표시 줄을 탭하면 충돌이 발생합니다. (이것은 커스텀 매핑 앱입니다)

전달되는 유일한 데이터는 “입력 디스패처”에서 앱으로 터치하는 것 같습니다. 나는 이것이 “Transaction Buffer”에서 1MB 근처에 합리적으로 있다고 생각하지 않습니다.

내 응용 프로그램은 쿼드 코어 1.6GHz 장치에서 실행 중이며 무거운 스레드를 처리하기 위해 3 개의 스레드를 사용하므로 하나의 코어를 UI 스레드에 사용할 수 없습니다. 또한 응용 프로그램은 android : largeHeap을 사용하고 사용되지 않는 힙이 10MB 남고 힙을 늘리기 위해 100MB의 여유 공간이 있습니다. 그래서 나는 그것이 자원 문제라고 말하지 않을 것입니다.

충돌은 항상 다음 줄 바로 앞에옵니다.

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

안드로이드의 소스 코드를 살펴보면 다음 줄을 찾습니다.

frameworks / base / core / jni / android_util_Binder.cpp :

case FAILED_TRANSACTION:
    ALOGE("!!! FAILED BINDER TRANSACTION !!!");
    // TransactionTooLargeException is a checked exception, only throw from certain methods.
    // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
    //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
    //        for other reasons also, such as if the transaction is malformed or
    //        refers to an FD that has been closed.  We should change the driver
    //        to enable us to distinguish these cases in the future.
    jniThrowException(env, canThrowRemoteException
            ? "android/os/TransactionTooLargeException"
                    : "java/lang/RuntimeException", NULL);

나에게 그것은 문서화되지 않은 기능을 치고있는 것처럼 들린다. 거래는 TooLarge가 아닌 다른 이유로 트랜잭션이 실패합니다. 그들은 그것을 이름을 붙여야했다 TransactionTooLargeOrAnotherReasonException.

현재로서는 문제를 해결하지 못했지만 유용한 정보가 있으면이 답변을 업데이트하겠습니다.

업데이트 : 내 코드에서 일부 파일 설명자가 유출되었으며 그 수는 Linux에서 최대화되어 (일반적으로 1024) 예외가 발생했습니다. 결국 자원 문제였습니다. /dev/zero1024 번 열어서 이것을 확인 하여 위의 예외 및 일부 SIGSEGV를 포함하여 UI 관련 작업에서 모든 종류의 이상한 예외가 발생했습니다. 분명히 파일 / 소켓을 열지 못하는 것은 Android 전체에서 매우 깨끗하게 처리 /보고되는 것이 아닙니다.


답변

TransactionTooLargeException이제 약 4 개월 동안 우리를 괴롭혀 왔으며, 우리는 마침내 문제를 해결했습니다!

우리는에서를 사용하고 FragmentStatePagerAdapter있었습니다 ViewPager. 사용자는 페이지를 넘겨 100 개 이상의 조각 (읽기 응용 프로그램)을 만듭니다.

우리는에서 조각을 올바르게 관리하지만 destroyItem()Android의 구현 FragmentStatePagerAdapter에는 다음 목록에 대한 참조를 유지하는 버그가 있습니다.

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

그리고 안드로이드가 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상기 OS가없는 것 등 그 항목 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;
}


답변

TransactionTooLargeException이 발생하는 이유에 대한 답변을 몹시 실망한 사람들을 위해 인스턴스 상태에서 저장하는 정보의 양을 확인하십시오.

compile / 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)

그러나 compile / 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) 

무엇을해야합니까?

로컬 데이터베이스에 데이터를 저장하고이 데이터를 검색하는 데 사용할 수있는 인스턴스 상태에서 ID 만 유지하십시오.


답변

이 예외는 일반적으로 앱이 백그라운드로 전송 될 때 발생합니다.

그래서 나는 onSavedInstanceStae라이프 사이클 을 완전히 피하기 위해 data Fragment 방법을 사용하기로 결정했습니다 . 내 솔루션은 복잡한 인스턴스 상태를 처리하고 최대한 빨리 메모리를 해제합니다.

먼저 데이터를 저장할 간단한 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/


답변

이 문제의 구체적인 원인은 하나도 없습니다. 저에게 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);