私はTransactionTooLargeException
を得ました。再現できません。文書にはそれが言う
バインダートランザクションが大きすぎるため失敗しました。
リモートプロシージャコールの間、コールの引数と戻り値は、バインダトランザクションバッファに格納されたParcelオブジェクトとして転送されます。引数または戻り値が大きすぎてトランザクションバッファに収まらない場合、呼び出しは失敗し、TransactionTooLargeExceptionがスローされます。
...
リモートプロシージャコールがTransactionTooLargeExceptionをスローしたときに考えられる結果は2つあります。クライアントが要求をサービスに送信できなかったか(ほとんどの場合、引数が大きすぎてトランザクションバッファに収まらなかった場合)、またはサービスが応答をクライアントに返送できませんでした(戻り値がトランザクションバッファに収まるには大きすぎます。
...
それで、どこかで、私はある未知の限界を超える議論を渡したり受けたりしています。どこ?
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、Device:HTC One X
私はこの問題に遭遇しました、そして、サービスとアプリケーションの間で大量のデータが交換されるとき、私はそれがわかりました(これはたくさんのサムネイルを転送することを含みます)。実際のデータサイズは約500 KBで、IPCトランザクションバッファサイズは1024 KBに設定されています。トランザクションバッファを超えた理由はわかりません。
これは、インテントエクストラを介して大量のデータを渡すときにも発生する可能性があります。
アプリケーションでこの例外が発生したら、コードを分析してください。
この例外が発生したときの対処方法
可能であれば、例えば1000回の操作でapplyBatch()を呼び出すのではなく、大きい操作を小さいチャンクに分割し、それぞれ100回で呼び出します。
サービスとアプリケーション間で巨大なデータ(> 1MB)を交換しないでください。
私はこれを行う方法を知りませんが、巨大なデータを返すことができます、Androidに問い合わせないでください:-)
これは決定的な答えではありませんが、TransactionTooLargeException
の原因を明らかにし、問題の特定に役立つ可能性があります。
ほとんどの回答は大量のデータ転送に関するものですが、スクロールとズームを頻繁に行ってActionBarスピナーメニューを繰り返し開くと、この例外が偶然にスローされます。アクションバーをタップするとクラッシュします。 (これはカスタムマッピングアプリです)
渡される唯一のデータは、「Input Dispatcher」からアプリに触れるようです。私はこれが「トランザクションバッファ」の1MB近くに相当することができないと思う。
私のアプリは、クアッドコアの1.6 GHzデバイス上で実行されており、UIスレッド用に1つのコアを解放したまま、重い移動に3つのスレッドを使用しています。さらに、このアプリはAndroid:largeHeapを使用し、10 MBの未使用ヒープが残っており、100 MBの空き領域が残っています。だから私はそれがリソースの問題ではないと思います。
クラッシュの直前には、必ず次の行があります。
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のソースコードを掘り下げると、次の行が見つかります。
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
と命名するべきでした。
現時点で私は問題を解決しませんでした、しかし私が有用な何かを見つけたら私はこの答えを更新します。
pdate:私のコードがいくつかのファイルディスクリプタをリークしたことが判明しましたが、その数はlinuxで最大化されていて(通常1024)、これが例外を引き起こしたようです。結局のところ、それはリソースの問題でした。私は/dev/zero
を1024回開くことでこれを検証しました。その結果、上記の例外を含むUI関連のアクションではあらゆる種類の奇妙な例外、さらにはSIGSEGVがいくつか発生しました。明らかにファイル/ソケットを開くことができないことは、Android全体で非常にきれいに扱われ/報告されたものではありません。
TransactionTooLargeException
は、約4ヶ月間私たちを悩ませてきました。そしてついにこの問題を解決しました!
何が起こっていたのかというと、FragmentStatePagerAdapter
にViewPager
を使っています。ユーザーはページングして100以上のフラグメントを作成します(その読み取りアプリケーションです)。
destroyItem()
でフラグメントを適切に管理していますが、FragmentStatePagerAdapter
のAndroidの実装ではバグがあります、そこでそれは以下のリストへの参照を保ちました:
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
にダンプされ、OSが100個以上の項目を好まない場合に発生します。
そのため、私たちに対する修正はsaveState()
メソッドをオーバーライドし、not"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;
}
どのParcelがクラッシュの原因となっているのかを調査する必要がある場合は、 TooLargeTool を試すことを検討してください。
(私はこれを@Max Spencerからのコメントとして受け入れられた回答の下で見つけ、私の場合は役に立ちました。)
TransactionTooLargeExceptionがなぜ現れるのかという答えを求めてがっかりした人のためにインスタンスの状態で保存したものを確認してみてください。
コンパイル時/ 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)
どうしますか?
ローカルデータベースにデータを保存し、このデータを取得するために使用できるインスタンス状態のIDのみを保持します。
この例外は通常、アプリがバックグラウンドに送信されているときにスローされます。
そのため、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;
}
}
それから私のメインのアクティビティでは、保存されたインスタンスサイクルを完全に回避し、データフラグメントに対する責任を延期します。 Fragments自体にこれを使用する必要はありません。それらの状態はActivityの状態に自動的に追加されます。
@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.com/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);
デバイスの機能やアプリに関係なく、トランザクションバッファは1 MBに制限されていることを理解することが重要です。このバッファは、ユーザが行うすべてのAPI呼び出しで使用され、アプリケーションが現在実行しているすべてのトランザクション間で共有されます。
私はそれがパーセルやそのような(Parcel.obtain())
のような特定のオブジェクトも持っていると思うので、すべてのobtain()
を常にrecycle()
とマッチさせることが重要です。
このエラーは、返されたデータが1 MB未満の場合でも、大量のデータを返すAPI呼び出しで簡単に発生する可能性があります(他のトランザクションがまだ実行中の場合)。
たとえば、PackageManager.getInstalledApplication()
呼び出しは、インストールされているすべてのアプリのリストを返します。特定のフラグを追加すると、多くの余分なデータを取得することができます。これを実行すると失敗する可能性が高いので、余分なデータを取得しないで、アプリごとに取得することをお勧めします。
ただし、それでも呼び出しは失敗する可能性があるので、呼び出しをcatch
で囲み、必要に応じて再試行できることが重要です。
私の知る限りでは、このような問題を回避する方法はありません。再試行して、できるだけ少ない情報を取得するようにすること以外はありません。
私もSamsung S3でこの例外を受けました。私は2つの根本的な原因を疑います、
DDMSを使用して、アプリを再生しながらヒープを確認すると、どのsetcontentviewが問題を引き起こしているのかがわかります。
私は問題2を取り除くためにすべてのフォルダにまたがってすべてのdrawableをコピーしました。
問題は解決しました。
これをあなたのアクティビティに追加する
@Override
protected void onSaveInstanceState(Bundle oldInstanceState) {
super.onSaveInstanceState(oldInstanceState);
oldInstanceState.clear();
}
それは私にとってもうまくいく
したがって、私たちにとっては、AIDLインターフェイスを介して大きすぎるオブジェクトをリモートサービスに送信しようとしていました。トランザクションサイズは1MBを超えることはできません。要求は512KBの個別のチャンクに分割され、インタフェースを介して一度に1つずつ送信されます。私は知っているがちょっと - 残忍な解決策 - そのAndroid :(
最近、私はAndroidのContacts Providerを使って作業しているときにも興味深いケースに遭遇しました。
内部の連絡先データベースから連絡先の写真を読み込む必要がありました。システムアーキテクチャによると、このデータはすべて連絡先プロバイダーへのクエリによって配信されます。
それは別のアプリケーションとして動作するので - あらゆる種類のデータ転送はBinderメカニズムを使用して実行されるので、Binder bufferがここで効果を発揮します。
私の主な間違いは、クローズしなかった BLOBデータを含むCursor
がContacts Providerから取得されたため、プロバイダに割り当てられたメモリが増え、大量の!!!FAILED BINDER TRANSACTION!!!
メッセージが表示されるまでBinderバッファが膨らんだこと私のLogCatの出力。
したがって、主な考え方は、外部のコンテンツプロバイダを使用してそれらからCursor
sを取得した場合は、それらを使用し終わったら必ず閉じることです。
あなたはonSaveInstanceStateメソッドからあなたの古いInstanceStateをクリアしました、そして、それはうまくいくでしょう。私のviewpagerにFragmentStatePagerAdapterを使っているので、InstanceStateをクリアするために親のアクティビティにOverrideメソッドを続けます。
@Override
protected void onSaveInstanceState(Bundle InstanceState) {
super.onSaveInstanceState(InstanceState);
InstanceState.clear();
}
私はここからこの解決策を見つけました NougatのAndroid.os.TransactionTooLargeException
私にとっては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);
}
}
私の場合は、ネイティブライブラリがSIGSEGVでクラッシュした後にTransactionTooLargeExceptionが二次クラッシュとして発生します。ネイティブライブラリのクラッシュは報告されていないので、TransactionTooLargeExceptionを受け取るだけです。
私はIntentを介してビットマップを送信しようとしたときにそれが起こると同時に私はアプリケーションを折り畳んだときに同じ問題に直面しました。
この記事の内容 ここにリンクの説明を入力 アクティビティが停止処理中の場合に発生します。つまり、アクティビティが保存状態のバンドルをシステムOSに送信して安全な状態に保とうとしています後で(設定変更またはプロセスの停止後に)復元したが、送信した1つ以上のバンドルが大きすぎた。
私は自分のアクティビティでonSaveInstanceStateをオーバーライドすることでハックを介して解決しました:
@Override
protected void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
}
そしてコメントスーパーを呼び出します。それは汚いハックですが、それは完璧に機能しています。ビットマップはクラッシュせずに正常に送信されました。これが誰かに役立つことを願っています。
大きなサイズのIntentオブジェクトデータに入れないでください。私の場合は、String 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);
大きなContentValues []をbulkInsertしようとしたとき、私はこれを私のsyncadapterで得ました。私はそれを次のように修正することにしました:
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));
}
また、あるアクティビティから別のアクティビティへのビットマップデータの受け渡しに関してこの問題に直面していましたが、データを静的データとして作成することで解決策を作成しました。
最初の活動では:
public static Bitmap bitmap_image;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
bitmap_image=mybitmap;
}
そして2番目の活動では:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bitmap mybitmap=first.bitmap_image;
}
私はこれの根本的な原因を見つけました(mvdsが言うように「ウィンドウの追加に失敗しました」とファイルディスクリプタリークの両方を得ました)。
Android 4.4のBitmapFactory.decodeFileDescriptor()
に bug があります。 inPurgeable
のinInputShareable
およびBitmapOptions
がtrue
に設定されている場合にのみ発生します。これは、ファイルとやり取りする多くの場所で多くの問題を引き起こします。
このメソッドはMediaStore.Images.Thumbnails.getThumbnail()
からも呼び出されることに注意してください。
Universal Image Loaderはこの問題の影響を受けます。 ピカソとグライドは影響を受けないようです。 https://github.com/nostra13/Android-Universal-Image-Loader/issues/102
私はAndroid EspressoテストのStackoverflowエラーからTransactionTooLargeExceptionを受け取りました。私は自分のアプリのLogcatフィルタを外したときにログにstackoverflowエラースタックトレースを見つけました。
本当に大きな例外スタックトレースを処理しようとすると、EspressoがTransactionTooLargeExceptionを引き起こしたと思います。
使用することができます:
Android:largeHeap="true"
android Manifestではapplicationタグの下にあります。
これで私の場合は問題が解決しました。
私にとっては、このエラーは発表者から来ていました。私はonResumeにコメントをし、onStartに同じコードを書き、それは私のために働きました。
@Override
public void onStart() {
super.onStart();
Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
if (goal != null && goal.getValue() > 0) {
mCurrentValue = (int) goal.getValue();
notifyPropertyChanged(BR.currentValue);
mIsButtonEnabled.set(true);
}
}
/* @Override
public void onResume() {
super.onResume();
Goal goal = Session.getInstance(getContext()).getGoalForType(mMeasureType);
if (goal != null && goal.getValue() > 0) {
mCurrentValue = (int) goal.getValue();
notifyPropertyChanged(BR.currentValue);
mIsButtonEnabled.set(true);
}
}*/
TransactionTooLargeExceptionも生きました。最初に、それがどこで発生するのかを理解するために取り組んできました。私はそれが起こる理由を知っています。私たちは皆、大規模なコンテンツのおかげで知っています。私の問題はそのようなもので、私は解決しました。たぶん、この解決法は誰にとっても役に立つでしょう。私はAPIからコンテンツを取得するアプリを持っています。最初の画面でAPIから結果を取得して、それを2番目の画面に送信しています。このコンテンツを2番目の画面に正常に送信できます。 2番目の画面の後、3番目の画面に移動したい場合、この例外が発生します。私の画面はそれぞれFragmentから作成されています。私は2番目の画面から出るときに気づきました。バンドルの内容を保存します。このコンテンツが大きすぎると、この例外が発生します。私の解決策は、バンドルからコンテンツを取得した後、それをクリアすることです。
class SecondFragment : BaseFragment() {
lateinit var myContent: MyContent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myContent = arguments?.getParcelable("mycontent")
arguments?.clear()
}
これは、検索結果のリストをフラグメントの引数で渡し、そのリストをフラグメントのプロパティ(実際にはフラグメントの引数が指すメモリ内の同じ場所への参照)に割り当てることでした。リストに新しい項目が追加され、それによってフラグメントの引数のサイズも変更されました。アクティビティが中断されると、基本フラグメントクラスはフラグメントの引数を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 の助けがなければこれを見つけることができなかったでしょう。
私は、放送受信機にビットマップオブジェクトを送っている(放送を送っている)間、同じ問題を抱えていました。
intent.putExtra("image", bitmapImage);
それで、それらをビットマップとして送る代わりに。ビットマップをバイト配列に変換しました。驚いたことにそれはうまくいった!なぜAndroidはビットマップで巨大なデータを転送することを許可していないがバイト配列を介して同じことを許可しているのだろうか。
intent.putExtra("imageInByteArray", convertBitmapToByteArray(bitmapImage));
受信機側でバイト配列をビットマップに変換し直すと問題が解決しました。
WriteToParcel(Parcel dest、int flags)メソッドのこの1行のコードは、TransactionTooLargeExceptionを取り除くのに役立ちました。
dest=Parcel.obtain();
このコードのあと、私はすべてのデータを区画オブジェクトに書きます。すなわちdest.writeInt()などです。
インテントとして、コンテンツプロバイダー、メッセンジャー、電話、バイブレーターなどのようなすべてのシステムサービスはBinderによってIPCインフラストラクチャプロバイダーを利用します。さらに活動ライフサイクルコールバックもこのインフラストラクチャを使用します。
1MBは、特定の瞬間にシステムで実行されるすべてのバインダートランザクションに対する全体的な制限です。
インテントが送信されたときに大量のトランザクションが発生している場合は、余分なデータが大きくなくても失敗する可能性があります。 http://codetheory.in/an-overview-of-Android-binder-framework/
Activity
には、Fragment
が含まれます。Activity
が明確な履歴フラグメントのないsportFragmentManager.fragments
である場合、フラグメントコンテンツを完全に再作成する必要がある場合
val fragments = sportFragmentManager.fragments
val transaction = sportFragmentManager.beginTransaction()
for (frag in fragments){
transaction.remove(frag)
}
transaction.commitAllowingStateLoss()
わずかな時間でフラグメントが再作成され、Activity
が発生します(デバッグ使用 tooLargeTool )
W/ActivityStopInfo: Bundle stats:
W/ActivityStopInfo: Android:viewHierarchyState [size=2304]
W/ActivityStopInfo: Android:views [size=2256]
W/ActivityStopInfo: Android:support:fragments [size=519072]
W/ActivityStopInfo: PersistableBundle stats:
W/ActivityStopInfo: [null]
解決策は、アプリがファイルシステムにArrayList(または問題の原因となっているオブジェクト)を書き込み、Intentを介してそのファイルへの参照(filename/pathなど)をIntentServiceに渡し、IntentServiceを許可することです。ファイルの内容を取得してArrayListに変換します。
IntentServiceがファイルを処理したら、それを削除するか、Local Broadcastを介して指示をアプリに渡して、作成したファイルを削除します(提供されたのと同じファイル参照を渡します)。
詳細については、 この関連問題に対する私の回答 を参照してください。
@Vaiden 回答 助けてくれました。なぜ短いリストがこの例外を引き起こすのか理解できませんでした。私はたくさんのフラグメントとリストを持ったViewPager
をいくつか持っています。そのため、別のアクティビティを開始したりプログラムを停止したり(画面をオフにしたり)するたびに、(通常はXiaomiで)この例外をキャッチしました。
私はすべてのフラグメントがそれらのonSaveInstanceState()
イベントを呼び出し、そして最後にホスティングアクティビティがonSaveInstanceState()
をonStop()
の前に呼び出すことを発見しました。その後、クラッシュが起こりました。それで、私は多くの短いリスト(サイズが10-100 Kb)がTransactionTooLargeException
例外を発生させることができることを理解しました。
データベースにデータを書き込むことでこの問題を解決してから、それらのid
sによって項目を取得することができます。このようにしてid
sのリストをActivity/Fragmentに渡すことができます。
しかし、あなたが一時的な速い方法を望むならば、これをしてください。
1)アクティビティの再作成中に存続するインスタンス保持フラグメントを作成します。
class SavedInstanceFragment : Fragment() {
// This map will hold bundles from different sources (tag -> bundle).
private lateinit var bundleMap: HashMap<String, Bundle?>
// This method is only called once for this fragment.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
bundleMap = HashMap()
}
fun pushData(key: String, bundle: Bundle): SavedInstanceFragment {
if (bundleMap[key] == null) {
bundleMap[key] = bundle
} else {
bundleMap[key]!!.putAll(bundle)
}
return this
}
fun popData(key: String): Bundle? {
val data = bundleMap[key]
bundleMap[key] = null
return data
}
companion object {
private const val TAG = "SavedInstanceFragment"
// Create the fragment with this method in `onCreate()` of an activity.
// Then you can get this fragment with this method again.
fun getInstance(fragmentManager: FragmentManager): SavedInstanceFragment {
var out = fragmentManager.findFragmentByTag(TAG) as SavedInstanceFragment?
if (out == null) {
out = SavedInstanceFragment()
fragmentManager.beginTransaction().add(out, TAG).commit()
}
return out
}
}
}
2)問題の断片を保持しているあなたのアクティビティのonCreate()
で、この断片を作成してください。それは活動のレクリエーションを生き残るでしょう。この操作は非同期であるため、他のフラグメントをFragmentManager
に追加する前に作成する必要があります。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity)
if (savedInstanceState == null) {
SavedInstanceFragment.getInstance(supportFragmentManager)
}
...
}
3)すべての問題の断片にこれらの行を追加します。 FragmentManager
の中で見つけることができるように、getInstance(activity?.supportFragmentManager!!)
を使って保持インスタンスのフラグメントに至るところにアクセスする必要があります。 getInstance()
のパラメータとしてchildFragmentManager
を使用すると、別のフラグメントを作成してクラッシュします。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bundle = SavedInstanceFragment.getInstance(activity?.supportFragmentManager!!).popData(TAG)
(bundle ?: arguments)?.run { // I access 'bundle' or 'arguments', depending if it is not first or first call of 'onCreate()'.
token = getString(ARG_TOKEN)!!
id = getInt(ARG_ID)
items = getParcelableArrayList(ARG_ITEMS)
}
}
override fun onSaveInstanceState(outState: Bundle) {
// Create a copy of savedInstanceState and Push to the retain-instance fragment.
val bundle = (outState.clone() as Bundle).apply {
putString(ARG_TOKEN, token)
putInt(ARG_ID, id)
putParcelableArrayList(ARG_ITEMS, items)
}
SavedInstanceFragment.getInstance(activity?.supportFragmentManager!!).pushData(TAG, bundle)
super.onSaveInstanceState(outState)
}
companion object {
const val TAG = "YourFragment"
private const val ARG_TOKEN = "token"
private const val ARG_ID = "id"
private const val ARG_ITEMS = "items"
fun newInstance(token: String, id: Int, items: ArrayList<Item>?) =
YourFragment().apply {
arguments = Bundle().apply {
putString(ARG_TOKEN, token)
putInt(ARG_ID, id)
putParcelableArrayList(ARG_ITEMS, items)
}
}
}
永続フラグメントの代わりに、シングルトンを使用できます。 ライブラリについての詳細は https://medium.com/@mdmasudparvez/Android-os-transactiontoolargeexception-on-nougat-solved-3b6e30597345 も参照してください、解決策。
私にとってはTransactionTooLargeException私がインテントを介してあるアクティビティから別のアクティビティに大きなビットマップ imageを送信しようとしたときに発生しました。 アプリケーションのグローバル変数 を使ってこの問題を解決しました。
たとえば、大きなビットマップイメージをアクティビティAからアクティビティBに送信する場合は、そのビットマップイメージをグローバル変数に格納します。
((Global) this.getApplication()).setBitmap(bitmap);
次にアクティビティBを開始し、グローバル変数から読み取ります。
Bitmap bitmap = ((Global) this.getApplication()).getBitmap();
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 */ }
Intentの両側が同じプロセスにない場合は、ややContentProvider
を試してください。
TransactionTooLargeException を参照してください。
バインダートランザクションが大きすぎるため失敗しました。
リモートプロシージャコールの間、コールの引数と戻り値は、バインダトランザクションバッファに格納されたParcelオブジェクトとして転送されます。引数または戻り値が大きすぎてトランザクションバッファに収まらない場合、呼び出しは失敗し、TransactionTooLargeExceptionがスローされます。
私が私のアプリでWebView
を扱っているとき、それは起こります。私はそれがaddView
とUIリソースに関連していると思います。私のアプリでは、以下のようにWebViewActivity
にいくつかのコードを追加します。
@Override
protected void onDestroy() {
if (mWebView != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}
プロジェクトでBitmapをBase64に変換して解析可能なオブジェクトに保存した場合は、以下のコードでビットマップのサイズを変更する必要があります。
pngをjpegに置き換え、品質を100から75または60に置き換えます。
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, byteArrayOutputStream)
この解決策は私のために働く
TransactionTooLargeExceptionが発生する可能性が非常に多くの場所で - ここにAndroid 8でもう1つ新しい - 誰かが単にEditTextに入力し始めたときのクラッシュ内容が大きすぎます。
これはStartSessionLocked()
の AutoFillManager (API 26の新機能)と 次の コードに関連しています。
mSessionId = mService.startSession(mContext.getActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, mContext.getOpPackageName());
私が正しく理解していれば、これは自動入力サービスを呼び出します - AutofillManagerClientをバインダー内で渡します。そして、EditTextにたくさんのコンテンツがあるとき、それはTTLEを引き起こすようです。
いくつかのことがそれを軽減するかもしれません(あるいは私がとにかくテストしていたようにしました):EditTextのxmlレイアウト宣言にAndroid:importantForAutofill="noExcludeDescendants"
を追加します。またはコードで:
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);
}
2番目のひどい、ひどい回避策は、TextEditサブクラス自体のエラーを捕捉するためにperformClick()
およびonWindowFocusChanged()
メソッドをオーバーライドすることかもしれません。しかし、私はそれが本当に賢いとは思わない...