44

我有一个奇怪的问题。我在网上四处寻找,但没有找到答案。我仍然是android编程的初学者。让我们开始吧:

我想做的就是用一些数据调用第二个活动。它适用于小数据,但如果数据变大,第二个活动将不会显示并且第一个活动完成。这是我的调用方法的代码:

Intent intent = new Intent(ActivitySearch.this,ActivityResults.class);
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("data", searchList);
intent.putExtras(bundle);
startActivity(intent);

接收数据的部分并不重要。即使我不尝试读取包,也不会调用该活动。我已经用以下几行测试了这个:

@Override
public void onCreate(Bundle savedInstanceState) {
Log.d("DEBUG","ActivityResult::onCreate()");
super.onCreate(savedInstanceState);

OnCreate()永远不会被调用。

也许你的一个人有一个想法......谢谢你的帮助!

编辑:至少我忘记了:这只发生在 ICS 下。该应用程序就像姜饼和果冻的魅力一样。

编辑2:Logcat

10-10 14:49:46.951: D/OpenGLRenderer(21696): Flushing caches (mode 0)
10-10 14:49:47.011: V/ActivityThread(22429): com.example.amazonsearch white listed for hwui
10-10 14:49:50.821: W/IInputConnectionWrapper(21696): showStatusIcon on inactive InputConnection
4

7 回答 7

55

您可能会收到TransactionTooLargeException

正如 google android guide所建议的,您可以使用静态字段或单例在活动之间共享数据。

他们推荐它“用于在短时间内共享复杂的非持久性用户定义对象”

从您的代码看来,这正是您所需要的。

因此,您在 ActivitySearch.class 中的代码可能如下所示:

ActivityResults.data = searchList;
Intent intent = new Intent(ActivitySearch.this,ActivityResults.class);
startActivity(intent);

然后,您可以在 ActivityResults 活动启动后从任何位置访问 ActivityResults.data。

对于需要在用户会话之间共享的数据,不建议使用静态字段,因为当应用程序在后台运行时(如果框架需要释放资源),应用程序进程可能会被 android 框架杀死并重新启动。在这种情况下,所有静态字段都将重新初始化。

于 2012-10-11T20:43:03.210 回答
33

我更喜欢传递大数据的方式是使用enums。这种方法的一些优点:

  • 无需创建单例,您始终拥有枚举的单个实例;
  • 数据被正确封装;
  • 引用在活动收到后立即删除

这是一个例子:

package com.jyvee.arguments;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

public class SomeActivity extends Activity {

    // Names for the arguments we pass to the
    // activity when we create it
    private final static String ARG_STRING = "ARG_STRING";
    private final static String ARG_INT = "ARG_INT";

    private String stringField;
    private int intField;
    private List<Object> arrayField;

    private enum DataHolder {
        INSTANCE;

        private List<Object> mObjectList;

        public static boolean hasData() {
            return INSTANCE.mObjectList != null;
        }

        public static void setData(final List<Object> objectList) {
            INSTANCE.mObjectList = objectList;
        }

        public static List<Object> getData() {
            final List<Object> retList = INSTANCE.mObjectList;
            INSTANCE.mObjectList = null;
            return retList;
        }
    }

    @Override
    protected void onCreate(final Bundle savedState) {
        super.onCreate(savedState);

        // Get the activity intent if there is a one
        final Intent intent = getIntent();

        // And retrieve arguments if there are any
        if (intent.hasExtra(ARG_STRING)) {
            stringField = intent.getExtras().getString(ARG_STRING);
        }
        if (intent.hasExtra(ARG_INT)) {
            intField = intent.getExtras().getInt(ARG_INT);
        }
        // And we retrieve large data from enum
        if (DataHolder.hasData()) {
            arrayField = DataHolder.getData();
        }

        // Now stringField, intField fields are available
        // within the class and can be accessed directly
    }

    /**
     * /** A static method for starting activity with supplied arguments
     * 
     * @param contextA
     *            context that starts this activity
     * @param stringArg
     *            A string argument to pass to the new activity
     * @param intArg
     *            An int argument to pass to the new activity
     * @param objectList
     *            An object list argument to pass to the new activity
     */
    public static void startActivity(final Context context, final String stringArg, 
            final int intArg, final List<Object> objectList) {

        // Initialize a new intent
        final Intent intent = new Intent(context, SomeActivity.class);

        // To speed things up :)
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);

        // And add arguments to the Intent
        intent.putExtra(ARG_STRING, stringArg);
        intent.putExtra(ARG_INT, intArg);

        // Now we put the large data into our enum instead of using Intent extras
        DataHolder.setData(objectList);

        context.startActivity(intent);
    }
}

更多信息在这里

于 2013-02-05T11:27:39.317 回答
3

如果您将大量信息从一个Activity传递到另一个,则可能会使应用程序变慢。

或者,使用全局类来存储变量,您可以使用它轻松获取或设置任何值。这可以在全局文件中声明。

请参阅此链接: http ://androidresearch.wordpress.com/2012/03/22/defining-global-variables-in-android/

于 2012-10-10T12:49:57.460 回答
3

据我记得,直到 API-8(Froyo),在通过意图传递可打包对象时存在一些限制(如 1MB)。但是,您可以简单地将可打包数据写入文件,然后通过 bundle 将文件路径发送到您的下一个活动。稍后,编写您的第二个活动以从文件中读取数据并随后将其删除。

于 2012-10-10T12:50:29.557 回答
2

TransactionTooLargeException最近遇到了这个问题,并正在寻找一种可能的解决方案来避免它。最后,我找不到任何可行的方法。在大多数答案中,您会发现人们推荐了不同的方法来避免此异常,但没有合适的示例。

这是我为解决此问题所做的工作,这在某些地方可能不是理想的解决方案,并且也有一些限制。

第 1 步 -编写一个将捆绑包转换为字符串并将其存储在 Shared Preference 中的类。

public class ActivityBridge {

    private static final String KEY_ACTIVITY_BRIDGE = "ACTIVITY_BRIDGE";
    private final Context context;
    private SharedPreferences sharedPreferences;


    public ActivityBridge(Context context) {
        this.context = context;

        sharedPreferences = context.getSharedPreferences(KEY_ACTIVITY_BRIDGE, Context.MODE_PRIVATE);
    }


    @SuppressLint("ApplySharedPref")
    public void putData(Bundle bundle, Intent intent) {
        sharedPreferences.edit()
                .putString(
                        intent.toString(),
                        Base64.encodeToString(bundleToBytes(bundle), 0)
                )
                .commit();
    }


    @SuppressLint("ApplySharedPref")
    public Bundle getData(Intent intent) {
        Bundle bundle;
        final String bundleString = sharedPreferences.getString(intent.toString(), "");

        if (TextUtils.isEmpty(bundleString)) {
            return null;
        } else {
            bundle = bytesToBundle(Base64.decode(bundleString, 0));
        }

        sharedPreferences.edit()
                .clear()
                .commit();

        return bundle;
    }


    public byte[] bundleToBytes(Bundle bundle) {
        Parcel parcel = Parcel.obtain();
        parcel.writeBundle(bundle);
        byte[] bytes = parcel.marshall();
        parcel.recycle();
        return bytes;
    }


    public Bundle bytesToBundle(byte[] bytes) {
        Parcel parcel = Parcel.obtain();
        parcel.unmarshall(bytes, 0, bytes.length);
        parcel.setDataPosition(0);
        Bundle bundle = parcel.readBundle(context.getClassLoader());
        parcel.recycle();
        return bundle;
    }
}

第 2 步 -用法

在创建意图时

         Intent intent = new Intent(ActivityA.this, ActivityB.class);

         Bundle bundle = new Bundle();
         bundle.putString("<KEY>", "<VALUE>");
         new ActivityBridge(ActivityA.this).putData(bundle, intent);

         startActivity(intent);

在提取捆绑包时

    Bundle bundle = new ActivityBridge(this).getData(getIntent());

注意:此方案读取后会清除存储的Bundle,如果重新创建Activity,则不会返回Bundle。这是一种解决方法,任何建议或问题都将受到高度赞赏。

于 2020-05-12T20:41:18.790 回答
1

我不知道为什么它不适用于大数据,但是如果您找不到任何方法来修复它,我建议您使用自定义全局应用程序,例如这里。(还要检查正确的答案以使其正常工作)

于 2012-10-10T12:53:32.950 回答
0

我选择将有效负载写入文件,然后在子活动中将其读回,因为:

  • 可以在被操作系统杀死的应用程序中幸存下来
    • (如进入新activity后,app长时间进入后台)
    • (例如,开发人员选项 > 不保留活动已打开)
  • 不需要全局/静态变量

( ) 下面的这个类DataReference允许在文件中保存和加载SerializablePayload”。DataReference本身就是这样Serializable,它可以被写入一个BundleIntent传递给一个新的Activity......

只需确保在 期间也保存它,并在...saveInstanceState期间重新加载它onCreate

/**
 * provides a means to pass around a large resource via a bundle. instead of storing the [Payload] in
 * the bundle, only a [sessionId], and [directoryName] is stored in a bundle, that can be used to
 * retrieve the actual [Payload] at a later time.
 */
class DataReference<Payload : Serializable>(
        private val directoryName: String,
        private val sessionId: String = randomAlphaNumericString()
) : Serializable {

    private fun directory(context: Context) = context.applicationContext.getDir(directoryName, Context.MODE_PRIVATE)
    private fun sessionIdFile(context: Context) = File(directory(context), "sessionId")
    private fun payloadFile(context: Context) = File(directory(context), "payload")

    /**
     * if the [sessionId] is valid, reads the [Payload] from persistent memory, and returns it to
     * the caller, or null, if no existing [Payload] exists; otherwise, if [sessionId] is invalid,
     * the [Payload] is deleted from persistent memory (if any exists), and null is returned to the
     * caller.
     */
    fun load(context: Context, payloadKClass: KClass<Payload>): Payload? {
        return if (sessionId == sessionIdFile(context).readObject(String::class)) {
            payloadFile(context).readObject(payloadKClass)
        } else {
            sessionIdFile(context).deleteFileIfExists()
            payloadFile(context).deleteFileIfExists()
            null
        }
    }

    /**
     * overwrites any existing [Payload] with the new [payload], which is only accessible from
     * [load] when the [sessionId] passed into [load] matches the [sessionId] passed into this call
     * to [save].
     */
    fun save(context: Context, payload: Payload) {

        // delete files if they exist
        sessionIdFile(context).deleteFileIfExists()
        payloadFile(context).deleteFileIfExists()

        // write the payload & session id to the file
        ObjectOutputStream(sessionIdFile(context).outputStream()).use { oos ->
            oos.writeObject(sessionId)
            oos.flush()
        }
        ObjectOutputStream(payloadFile(context).outputStream()).use { oos ->
            oos.writeObject(payload)
            oos.flush()
        }
    }

    private fun File.deleteFileIfExists(): Boolean {
        isFile && delete()
        return !exists()
    }

    private fun <T:Any> File.readObject(tKClass: KClass<T>): T? = if (isFile) {
        ObjectInputStream(inputStream()).use { ois ->
            tKClass.safeCast(ois.readObject())
        }
    } else {
        null
    }
}

于 2021-02-03T19:28:06.193 回答