나는 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로 설정되어 있습니다. 왜 트랜잭션 버퍼를 초과했는지 잘 모르겠습니다.
인 텐트 엑스트라를 통해 많은 양의 데이터를 전달할 때도 발생할 수 있습니다.
응용 프로그램에서이 예외가 발생하면 코드를 분석하십시오.
- 서비스와 응용 프로그램간에 많은 데이터를 교환하고 있습니까?
- 인 텐트를 사용하여 대용량 데이터 공유 (예 : 사용자가 갤러리 공유 프레스 공유에서 많은 수의 파일을 선택하면 선택한 파일의 URI가 인 텐트를 사용하여 전송 됨)
- 서비스에서 비트 맵 파일 받기
- 사용자가 많은 응용 프로그램을 설치할 때 getInstalledApplications ()와 같이 Android가 큰 데이터로 응답을 기다리는 경우
- 많은 작업이 보류중인 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/zero
1024 번 열어서 이것을 확인 하여 위의 예외 및 일부 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);