Nougat中的TransactionTooLargeException异常

6

异常

 05-12 15:42:45.791 11043-11043/ E/UncaughtException: java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 631792 bytes
                                                                       at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3776)
                                                                       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:6123)
                                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
                                                                    Caused by: android.os.TransactionTooLargeException: data parcel size 631792 bytes
                                                                       at android.os.BinderProxy.transactNative(Native Method)
                                                                       at android.os.BinderProxy.transact(Binder.java:615)
                                                                       at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3700)
                                                                       at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768)
                                                                       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:6123) 
                                                                       at java.lang.reflect.Method.invoke(Native Method) 
                                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:75705-12 15:42:47.247 11043-11043/ E/AndroidRuntime: FATAL EXCEPTION: main
                                                                Process: , PID: 11043
                                                                java.lang.RuntimeException: android.os.TransactionTooLargeException: data parcel size 631792 bytes
                                                                    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3776)
                                                                    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:6123)
                                                                    at java.lang.reflect.Method.invoke(Native Method)
                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
                                                                 Caused by: android.os.TransactionTooLargeException: data parcel size 631792 bytes
                                                                    at android.os.BinderProxy.transactNative(Native Method)
                                                                    at android.os.BinderProxy.transact(Binder.java:615)
                                                                    at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3700)
                                                                    at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768)
                                                                    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:6123) 
                                                                    at java.lang.reflect.Method.invoke(Native Method) 
                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) 
                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757

Fragment OnItemClick:-

 listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                                long id) {

            Bundle bundle = new Bundle();
            ProdModel mProdModel = prodList.get(position);
            bundle.putSerializable("object", mProdModel);
            Intent intent = new Intent(getActivity(), DetailActivity.class);
            intent.putExtra("bundle", bundle);
            startActivityForResult(intent, 1);

        }

    });

在 DetailActivity 中,

Bundle bundle = getIntent().getBundleExtra("bundle");
    if (bundle != null) {
        ProdModel model = (ProdModel) bundle.getSerializable("object");
    }

DetailActivity清单文件

  <activity
        android:name="com.mass.mysample.DetailActivity"
        android:screenOrientation="portrait" />

使用Picasso进行图像加载
 Picasso.with(this).load(model.getImage())
             .placeholder(R.drawable.logo_without)
             .fit().into(productimage);

模型类

public class ProdModel implements Serializable {
private String seller_id;
String name;
private String image;
private float price;
private float specialprice;
private String entity_id;
private String productNumQuantity;
private String storetitle;
private String description;
private String discount;
private String max_price;
private String store_name;
private String StoreUrl;

public String getProductNumQuantity() {
    return productNumQuantity;
}

public void setProductNumQuantity(String productNumQuantity) {
    this.productNumQuantity = productNumQuantity;
}

public String getDiscount() {
    return discount;
}

public void setDiscount(String discount) {
    this.discount = discount;
}

public String getMax_price() {
    return max_price;
}

public void setMax_price(String max_price) {
    this.max_price = max_price;
}

public String getStore_name() {
    return store_name;
}

public void setStore_name(String store_name) {
    this.store_name = store_name;
}

public String getStoreUrl() {
    return StoreUrl;
}

public void setStoreUrl(String storeUrl) {
    StoreUrl = storeUrl;
}

public String getStoretitle() {
    return storetitle;
}

public void setStoretitle(String storetitle) {
    this.storetitle = storetitle;
}

public String getSeller_id() {
    return seller_id;
}

public void setSeller_id(String seller_id) {
    this.seller_id = seller_id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getImage() {
    return image;
}

public void setImage(String image) {
    this.image = image;
}

public String getEntity_id() {
    return entity_id;
}

public void setEntity_id(String entity_id) {
    this.entity_id = entity_id;
}

public String getDescription() {
    return description;
}

public void setDescription(String description) {
    this.description = description;
}

public float getPrice() {
    return price;
}

public void setPrice(float price) {
    this.price = price;
}

public float getSpecialPrice() {
    return specialprice;
}

public void setSpecialPrice(float specialprice) {
    this.specialprice = specialprice;
}

}

从TabActivity到fragment的数据流:

1.I have a TabActivity with ViewPager and Fragment. 
2.In Fragment, I have a ListView.
3.The Data to fragment is passed from view pager adapter by set arguments.

我尝试过的解决方案

1.A POJO class that implements Serialization. 
2.I use Picasso to load the image from image URL. 
3.This exception is thrown when OnItemClick on ListView in fragment passes data to DetailActivity to show all passed data.
4.I get this exception after the DetailActivity load's image and other data then app suddenly crashes.
5.I pass data in Intent was all POJO Object with Id, Name, Image URL, Price etc.,

注意:应用程序仅在NOUGAT版本中崩溃。

最终,我实施的解决方法是

将targetSdkVersion从25更改为23

更改后,我的应用程序也不会在Nougat中崩溃。

我想知道这是否是正确的解决方案,或者是否有任何变通方法。

请指导正确的方式。

提前致谢。

片段

public class ProdFragment extends Fragment {
ListView listview;
SharedPreferences spref;
Boolean isInternetPresent = false;
ConnectionDetector cd;

private String toBeDisplayed,CatID;
private static final String TAG = ProdFragment.class.getSimpleName();
String totalProductCart;
private ArrayList<Root_SubCatModel> subCatList;
private ArrayList<ProdModel> prodList;
ProdAdapter adapter;

public ProdFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    spref = getActivity().getSharedPreferences(getResources().getString(R.string.myPref),
            Context.MODE_PRIVATE);
    Bundle bundle = getArguments();
    if (bundle != null) {
        int tabPosition = bundle.getInt("Tab_Position");
        toBeDisplayed = bundle.getString("Tab_ToBeDisplayed");
        CatID = bundle.getString("CategoryId");
        subCatList = (ArrayList<Root_SubCatModel>) bundle.getSerializable("Tab_Data");
    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View rootView = inflater.inflate(R.layout.prod_listview, container, false);

    cd = new ConnectionDetector(getActivity());
    listview = (ListView) rootView.findViewById(R.id.prodlist);
    if (subCatList != null) {
        adapter = new ProdAdapter(getActivity(),
                CatID,
                toBeDisplayed,
                R.layout.list_product,
                subCatList,
                (ProductCartCountListener)getActivity());
        listview.setAdapter(adapter);
        adapter.notifyDataSetChanged();

        prodList = new ArrayList<>();
        for(int i = 0; i<subCatList.size();i++){
            if (subCatList.get(i).getProd() != null) {
                if(toBeDisplayed.equals("SubCategory")){
                    if (CatID.equals(subCatList.get(i).getCategory_id())) {
                        this.prodList = subCatList.get(i).getProd();
                    }
                }else if(toBeDisplayed.equals("Products")) {
                    this.prodList = subCatList.get(i).getProd();
                }
            }
        }

    }

    listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                                long id) {

            Bundle bundle = new Bundle();
            bundle.putSerializable("object", prodList.get(position));
            Intent intent = new Intent(getActivity(), DetailActivity.class);
            intent.putExtra("bundle", bundle);
            startActivityForResult(intent,1);
        }

    });

    return rootView;
}

public void showAlertDialog(Context context, String title, String message, Boolean status) {
    AlertDialog alertDialog = new AlertDialog.Builder(context).create();

    // Setting Dialog Title
    alertDialog.setTitle(title);

    // Setting Dialog Message
    alertDialog.setMessage(message);

    // Setting alert dialog icon
    //alertDialog.setIcon((status) ? R.drawable.success : R.drawable.fail);

    // Setting OK Button
    alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
        }
    });

    // Showing Alert Message
    alertDialog.show();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1) {
        if (resultCode == Activity.RESULT_OK) {
            Bundle b = data.getExtras();
            if (b != null) {
                ProdModel myobj = (ProdModel) b.getSerializable("Cart_Quantity");
                if (myobj != null) {
                    if (prodList != null) {
                        for (ProdModel P : prodList) {
                            if (P.getEntity_id().equals(myobj.getEntity_id())) {
                                P.setProductNumQuantity(myobj.getProductNumQuantity());
                            }
                        }
                    }
                    Log.d(TAG,"Product_Id : " + myobj.getEntity_id()
                            + ", Product_Cart_Count : " + myobj.getProductNumQuantity());
                }
            }
        } else if (resultCode == 0) {
            Log.d(TAG,"RESULT CANCELLED");
        }
    }
    adapter.notifyDataSetChanged();
    String totalProductCart = spref.getString("Cart_Count_Tool", "0");
    Activity activity = getActivity();
    if(activity instanceof TabActivity){
        TabActivity myActivity = (TabActivity) activity;
        Toolbar toolbar = (Toolbar) myActivity.findViewById(R.id.back_toolbar);
        ImageView cart_imageview = (ImageView) toolbar.findViewById(R.id.cart_imageview);
        cart_imageview.setImageDrawable(myActivity.buildCounterDrawable(
                Integer.parseInt(totalProductCart)));
    }
    Log.d(TAG,"RESULT NOTIFIED");
}

public void refreshData(String productId, boolean isAddAsyncTaskComplete,
                        boolean isAddAsyncTaskLimitReached, boolean isDeleteAsyncTaskComplete){
    adapter.setQuantityCount(prodList, productId, isAddAsyncTaskComplete,
            isAddAsyncTaskLimitReached, isDeleteAsyncTaskComplete);

}

TabActivity布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/storeimage"
        android:textAlignment="center"
        android:elevation="2dp"
        android:minHeight="?attr/actionBarSize"
        app:tabMode="scrollable"
        app:tabTextColor="@color/color_dark_blue"
        app:tabSelectedTextColor="@color/color_orange"/>
</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 </android.support.design.widget.CoordinatorLayout>

列表视图

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
    android:id="@+id/prodlist"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</FrameLayout>

这是完整的堆栈跟踪吗?我记得在一个项目中出现过这个异常,顶部的堆栈跟踪显示“事务太大”,但是向下滚动时,实际上是由于我意外地进行了递归调用导致的StackOverflowException。 - Arthur Attout
1
你能发布一下你的 onSaveInstanceState() 实现吗? - azizbekian
1
看起来这是在TabActivity中的Fragment尝试保存其状态时发生的。它的状态是640KB,太大了。你能发布Fragment的相关代码吗?还有你的布局文件。这个问题与“Serializable vs. Parcelable”无关,也与Bundle中的ProdModel无关。 - David Wasser
1
您还没有发布足够的Fragment代码。问题不在DetailActivity,而是在TabActivity中的Fragment中。 - David Wasser
1
private ArrayList<Root_SubCatModel> subCatList; 是什么?您正在使用包含此数据的 Bundle 调用 setArguments() 以供 Fragment 使用。您传递给 setArguments() 的所有数据都会自动保存,以便在 Fragment 销毁和创建时保持不变。我猜这就是您的问题所在。您可以将这些数据保存在 Activity 中,而 Fragment 在需要时可以从 Activity 获取数据,而不是这样做。 - David Wasser
显示剩余16条评论
4个回答

24
您在调用 setArguments() 方法时向您的 Fragment 传递了过多的数据。这样,您的 Fragment 可以正常工作,但是当它尝试保存其实例状态时会导致事务缓冲区溢出。如果您的目标是 Android 7.0(API 24 或更高版本),这将抛出一个 RuntimeException。为了保留向后兼容性并避免破坏现有应用程序,只有当您的目标是 API 24 或更高版本时才使用新的行为。如果您的目标是 API < 24,则会捕获并静默忽略事务缓冲区溢出异常。这意味着您的数据不会被持久保存,这可能会引起注意(或者不注意)。
您的代码存在问题。在调用 setArguments() 方法时,您不应该向 Fragment 传递大量数据。您可以将数据保留在您的 Activity 中。当 Fragment 想要访问数据时,它随时都可以执行以下操作:
// Get the owning Activity
MyActivity activity = (MyActivity)getActivity();
// Get the data from the Activity
List<Data> data = activity.getData();

在你的Activity中编写一个getData()方法,该方法返回对Fragment所需数据的引用。

通过这种方式,数据保存在Activity中,Fragment需要时可以随时访问它。


2
谢谢David。你节省了我的时间。 - Rabindra Acharya
我们能否直接使用 MyActivity.variable 和 MyActivity.anything() 而不需要 MyActivity activity = (MyActivity)getActivity(); 来访问变量和方法呢? - user1090751
@user1090751 如果变量和方法被定义为“静态”,那么你才能这样做。虽然这是可能的,但违反了面向对象设计的基本原则,通常不建议这样做。 - David Wasser
除了违反原则,性能问题怎么样? - user1090751
@user1090751 性能差别几乎为零。你可能无法测量,肯定也不会注意到它。 - David Wasser

7
Android 7.0(Nogat)包含许多系统和API行为变更。关于TransactionTooLarge异常:
在Android 7.0中,许多平台API现在开始检查跨Binder事务发送的大型负载,系统现在将TransactionTooLargeExceptions重新抛出为RuntimeExceptions,而不是静默记录或抑制它们。一个常见的例子是在Activity.onSaveInstanceState()中存储过多的数据,这会导致当您的应用程序定位到Android 7.0时,ActivityThread.StopInfo抛出RuntimeException。
可能的解决方案: 1.在全局缓存中保存所需对象,并仅传递密钥到DetailsActivity以检索对象。

我认为你的解决方案不是最佳实践。我们应该将对象保存在某个数据库/文件中,并传递一个键给新的活动,以便稍后检索它。 - Tin Tran
仅当需要在应用程序启动或应用程序被终止后再次使用时,将其保存在数据库/文件中。 如果仅需要在单击某个按钮以显示详细信息,则将其保存在内存中,并在应用程序进入后台时清除它。 - abhishesh
我遇到了这个异常 - android.os.TransactionTooLargeException: 数据包大小为1100920字节。 我的代码是关于插入联系人 - ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); ContentProviderResult[] res = context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);如何解决它? - Maulik Patel

2
关于文档这个异常,当值过大无法适应事务缓冲区时会抛出。对于一个大型项目,在不同的地方调用多个带有发送意图的操作可能是一个复杂的问题。
您的示例展示了这个问题,即使只有单个对象也是如此。您应该彻底改变传输行为,例如通过修剪对象仅包含重要信息来解决这个问题。 意图额外数据应仅包含轻量级信息!
在您的示例中,您有一个可能会产生此问题的字段。可能您正在使用Base64编码的图像。具有特定于Android 23的消息文本的异常。请查看文档。
public String getImage() {
    return image;
}

public void setImage(String image) {
    this.image = image;
}

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接