【问题标题】:I cant pass too large arraylist of objects between 2 activities?我不能在 2 个活动之间传递太大的对象数组列表?
【发布时间】:2016-03-31 08:57:06
【问题描述】:

我正在尝试在 2 个活动抛出意图之间传递自定义对象的数组列表,但我收到了这个错误,我不知道如何解决它。如果有人可以帮助我,我将不胜感激!提前致谢。

第一个活动中的方法:

i.putParcelableArrayListExtra("key", (ArrayList<? extends Parcelable>) result);
startActivity(i);

进入第二个活动的方法:

Intent i = getIntent();
ArrayList<ItemObjects> list = i.getParcelableArrayListExtra("key");

错误日志:

12-25 09:11:49.546 17742-17742/com.example.baha.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.baha.myapplication, PID: 17742
java.lang.RuntimeException: Failure from system
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1514)
    at android.app.Activity.startActivityForResult(Activity.java:3917)
    at android.app.Activity.startActivityForResult(Activity.java:3877)
    at android.app.Activity.startActivity(Activity.java:4200)
    at android.app.Activity.startActivity(Activity.java:4168)
    at com.example.baha.myapplication.splash$GetMarkets.onPostExecute(splash.java:127)
    at com.example.baha.myapplication.splash$GetMarkets.onPostExecute(splash.java:62)
    at android.os.AsyncTask.finish(AsyncTask.java:651)
    at android.os.AsyncTask.-wrap1(AsyncTask.java)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: android.os.TransactionTooLargeException: data parcel size 12404332 bytes
    at android.os.BinderProxy.transactNative(Native Method)
    at android.os.BinderProxy.transact(Binder.java:503)
    at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2657)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1507)
    at android.app.Activity.startActivityForResult(Activity.java:3917) 
    at android.app.Activity.startActivityForResult(Activity.java:3877) 
    at android.app.Activity.startActivity(Activity.java:4200) 
    at android.app.Activity.startActivity(Activity.java:4168) 
    at com.example.baha.myapplication.splash$GetMarkets.onPostExecute(splash.java:127) 
    at com.example.baha.myapplication.splash$GetMarkets.onPostExecute(splash.java:62) 
    at android.os.AsyncTask.finish(AsyncTask.java:651) 
    at android.os.AsyncTask.-wrap1(AsyncTask.java) 
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:148) 
    at android.app.ActivityThread.main(ActivityThread.java:5417) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

【问题讨论】:

标签: java android android-intent arraylist


【解决方案1】:

您不能,否则它会使您的应用程序冻结。将数据存储在您的 Application 派生对象中,以便它们随时存在。

【讨论】:

  • 这实际上是一个比接受的答案更好的答案,尽管提供的信息量可能会更好。
  • 没什么。只需添加更多细节;)
  • 如果我错了,请纠正我,但 static 对象不是在应用程序的整个生命周期中存储在 RAM 中吗?这意味着您将永久占用 12Mb 的设备 RAM,这听起来不是一个好主意considering the cap size。我可能会考虑在需要时对这些对象进行延迟初始化,而不是将它们全部存储在 static ArrayList 中。
  • 为什么,当您不需要该数据时,您将变量设置为 null 并且数据将不再被引用,因此将被垃圾收集
【解决方案2】:

这在使用 Intent Extra 传递大量数据时经常发生。

您的选择:

  1. 使用变量定义一个可以从其他类访问的公共变量:

你会有数组,但我的例子有整数:

现在,您想从另一个类访问这些整数,对吧?现在,您正在通过额外的意图将 int 发送到其他活动。相反,您可以创建 MyClass 的对象并以这种方式访问​​它们:

  1. 使用数组数据创建一个共享首选项对象,并从其他类访问数据。

    SharedPreferences prefs = this.getSharedPreferences("com.example.app", Context.MODE_PRIVATE);

然后编辑:

prefs.edit().putInt(INT).apply();

【讨论】:

  • 不清楚 MyClass 将自己存储在哪里。将 1 MB 的数据写入共享首选项(只是一个 xml 文件)也是有问题的。
  • @AlexanderKulyakhtin 实际上,MyClass 是带有您的数组的类。现在,在你的另一个类中,创建一个MyClass 的对象并访问成员变量。让我知道它是否有效!
  • @AlexanderKulyakhtin 太棒了!你选择了哪种方法?
  • @RuchirBaronia 第一个,我公开了。
  • 创建新对象不会保留旧值。除非字段是静态的。
【解决方案3】:

参考:http://developer.android.com/reference/android/os/TransactionTooLargeException.html

Binder 事务缓冲区有一个有限的固定大小,目前为 1Mb, 该进程的所有正在进行的事务共享。 因此,当有很多 即使大多数个别交易仍在进行中 大小适中。

【讨论】:

    【解决方案4】:

    您不能使用意图传递太大的对象。如果你想在多个活动中使用这些数据,你可以定义一个如下的帮助类:

    public class MyData {
        private static MyData sInstance = new MyData();
        public static MyData get() { return sIntance; }
    
        private ArrayList<> data;
        public List<> getData() { return data; }
        public void loadData() { 
            // load data
        } 
    }
    

    你可以在一个activity中加载数据,然后当你跳转到另一个activity时,你可以简单地使用:

    ArrayList<> data = MyData.get().getData();
    

    【讨论】:

      【解决方案5】:

      提供的所有答案似乎质量都不高。所以我再提供一个。

      问题

      您遇到的异常是由于您尝试通过 Bundle 传输的数据量太大。

      注意:我在我的网站上写了一篇关于此主题的博文,其中包含更详细的信息。你可以找到它 这里:http://neotechsoftware.com/blog/android-intent-size-limit

      解决方案

      您无法通过标准方式(使用 Bundle 或 Parcel)转移您的对象,因为它太大了。在这些情况下经常使用两种解决方案:

      • 基于文件的临时存储和传输
      • 基于内存的存储和传输

      基于文件

      您可以将自定义对象数组存储在文件中,然后再将其读回。我不建议这样做,因为它通常比内存存储慢。然而,一种常用的技术是在适用的情况下使用某种数据库。然而,使用数据库有一个主要缺点,存储在数据库中的对象通常是临时对象,因此您需要在阅读完它们后将其删除。如果出现问题,您的数据库可能会开始变得混乱。所以你需要某种干净的程序。避免这种情况的最佳方法可能是将数据库文件(或实际上用于此的任何文件)写入缓存目录。

      内存中 1:(应用程序实例)

      您最好的选择(也可能是首选的 Android 方式)是将数组存储在 Application 实例中。为此,您需要创建一个类并使其扩展Application。在此类中,您将创建一个公共字段来写入和读取数组。

      public class App extends Application {
      
          public ArrayList<YourObject> objects;
      
      }
      

      您可以通过获取Application 实例来读取和写入该字段。

      App app = (App) getApplicationContext();
      app.objects = yourArrayList;
      

      不要忘记在manifest.xml 文件中注册您的扩展Application 类! 为什么会这样:因为Application 实例不会在不同的活动之间被销毁,它有一个独立于 UI 的生命周期。

      内存中 2(单例)

      另一种选择是使用单例模式或创建具有静态字段的类。下面的示例演示了单例模式。

      public class DataContainer {
      
          private static DataContainer instance = null;
      
          public static DataContainer getInstance(){
              /**
               * Synchronizing this method is not needed if you only use it in
               * Activity life-cycle methods, like onCreate(). But if you use
               * the singleton outside the UI-thread you must synchronize this
               * method (preferably using the Double-checked locking pattern).
               */
              return instance != null?instance: (instance = new DataContainer());
          }
      
          public ArrayList<YourObject> objects;
      
      }
      
      
      DataContainer.getInstance().objects = yourArray;
      

      关于内存对象存储的非常重要的注意事项:假设您的应用程序在后台,并且您将一些对象存储在 Application 实例(或单例容器)中以便稍后恢复它.如果 Android 系统决定终止您的应用程序,因为它需要为另一个应用程序释放一些内存,那么您的对象将被销毁。现在重要的部分是,如果用户返回到您的应用程序,Android 系统会重新创建已销毁的 Activity 并将非空的“已保存实例状态”Bundle 传递给 onCreate() 方法。此时您可能会假设(因为Bundle 不为空)您的内存中存储的对象存在,但这不是真的! 结论:始终检查存储的对象是否非空!

      【讨论】:

      • 嗨,我有一个疑问。目前,我必须加载一些文件并将它们存储在 ArrayList 中。因此,在我的 SplashScreen 中,我启动了一个 JobIntentService 来执行加载,然后在加载完成后,我将 ArrayList 存储到我的应用程序类中存在的静态 ArrayList 中。现在假设 Android 操作系统决定终止我的应用程序/活动,应用程序中的 ArrayList 也会被清除吗?如果是,您的解决方案会观察到相同的行为。我打算使用的解决方案是否与您的解决方案具有相同的效果?请帮忙。
      猜你喜欢
      • 1970-01-01
      • 2012-10-30
      • 1970-01-01
      • 1970-01-01
      • 2013-07-22
      • 1970-01-01
      • 1970-01-01
      • 2017-07-03
      • 1970-01-01
      相关资源
      最近更新 更多