What to do on TransactionTooLargeException

前端 未结 30 3331
盖世英雄少女心
盖世英雄少女心 2020-11-22 03:08

I got a TransactionTooLargeException. Not reproducible. In the docs it says

The Binder transaction failed because it was too large.

D

相关标签:
30条回答
  • 2020-11-22 03:38

    When I am dealing with the WebView in my app it happens. I think it's related to addView and UI resources. In my app I add some code in WebViewActivity like this below then it runs ok:

    @Override
    protected void onDestroy() {
        if (mWebView != null) {
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);  
            mWebView.removeAllViews();  
            mWebView.destroy();
        }
        super.onDestroy();
    }
    
    0 讨论(0)
  • 2020-11-22 03:39

    It is important to understand that the transaction buffer is limited to 1 MB, regardless of device capabilities or app. This buffer is used with every API calls you make and is shared amongst all transactions an app is currently running.

    I believe it also holds some specific object like parcels and such (Parcel.obtain()), so it's important to always match every obtain() with a recycle().

    This error can easily happen on API calls returning a lot of data, even though the returned data is less than 1 MB (if other transactions are still running).

    For example, the PackageManager.getInstalledApplication() call returns a list of all apps installed. Adding specific flags allows to retrieve a lot of extra data. Doing so is likely to fail, so it's recommended not to retrieve any extra data and retrieve those on a per-app basis.

    However the call may still fail, so it's important to surround it with a catch and be able to retry if necessary.

    As far as I know, there's no work-around to such issue except retrying and making sure to retrieve as little information as possible.

    0 讨论(0)
  • 2020-11-22 03:39

    For me it was also the FragmentStatePagerAdapter, however overriding saveState() did not work. Here's how I fixed it:

    When calling the FragmentStatePagerAdapter constructor, keep a separate list of fragments within the class, and add a method to remove the fragments:

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

    Then in the Activity, save the ViewPager position and call adapter.removeFragments() in the overridden onSaveInstanceState() method:

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

    Lastly, in the overridden onResume() method, re-instantiate the adapter if it isn't null. (If it's null, then the Activity is being opened for the first time or after the app has been killed off by Android, in which onCreate will do the adapter creation.)

    @Override
    public void onResume() {
        super.onResume();
        if (adapter != null) {
            adapter = new PagerAdapter(frags);
            mViewPager.setAdapter(adapter);
            mViewPager.setCurrentItem(currentTabPosition);
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:42

    Also i was facing this issue for Bitmap data passing from one activity to another but i make a solution by making my data as static data and this is working perfect for me

    In activity first :

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

    and in second activity :

     @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
       Bitmap mybitmap=first.bitmap_image;
    }
    
    0 讨论(0)
  • 2020-11-22 03:42

    A solution would be for the app to write the ArrayList (or whatever object is causing the problem) to the file system, then pass a reference to that file (e.g., filename/path) via the Intent to the IntentService and then let the IntentService retrieve the file contents and convert it back to an ArrayList.

    When the IntentService has done with the file, it should either delete it or pass the instruction back to the app via a Local Broadcast to delete the file that it created (passing back the same file reference that was supplied to it).

    For more info see my answer to this related problem.

    0 讨论(0)
  • 2020-11-22 03:43

    This was happening in my app because I was passing a list of search results in a fragment's arguments, assigning that list to a property of the fragment - which is actually a reference to the same location in memory pointed to by the fragment's arguments - then adding new items to the list, which also changed the size of the fragment's arguments. When the activity is suspended, the base fragment class tries to save the fragment's arguments in onSaveInstanceState, which crashes if the arguments are larger than 1MB. For example:

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

    The easiest solution in this case was to assign a copy of the list to the fragment's property instead of assigning a reference:

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

    An even better solution would be to not pass around so much data in the arguments.

    I probably never would have found this without the help of this answer and TooLargeTool.

    0 讨论(0)
提交回复
热议问题