Проблема: передача больших данных во второе действие

У меня странная проблема. Я искал в Интернете, но не нашел ответа. Я все еще новичок в программировании 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. Приложение работает как шарм с пряниками и фройо.

Edit2: 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

8 ответов

Решение

Вы, вероятно, получаете TransactionTooLargeException

Как подсказывает руководство Google для Android, вы можете использовать статические поля или одиночные столбцы для обмена данными между действиями.

Они рекомендуют его "Для совместного использования сложных непостоянных пользовательских объектов на короткое время"

Из вашего кода кажется, что это именно то, что вам нужно.

Таким образом, ваш код в ActivitySearch.class может выглядеть примерно так:

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

После этого вы можете получить доступ к ActivityResults.data из любого места действия ActivityResults после его начала.

Для данных, которые должны быть разделены между сеансами пользователя, не рекомендуется использовать статические поля, поскольку процесс приложения может быть уничтожен и перезапущен платформой Android, когда приложение работает в фоновом режиме (если платформе нужно освободить ресурсы). В этом случае все статические поля будут повторно инициализированы.

Я предпочитаю передавать большие данные, используя перечисления. Некоторые преимущества этого подхода:

  • Нет необходимости создавать синглтоны, у вас всегда есть один экземпляр вашего enum;
  • Данные правильно инкапсулированы;
  • Ссылка удаляется сразу после получения активности

Вот пример:

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

Больше информации здесь.

Я столкнулся с этим TransactionTooLargeExceptionпроблема недавно и искал возможное решение, чтобы избежать ее. Наконец, я не смог найти ничего, что могло бы работать. В большинстве ответов вы найдете людей, рекомендующих разные способы избежать этого исключения, но надлежащего примера не было.

Вот что я сделал, чтобы исправить эту проблему, это может быть не идеальным решением в некоторых местах, а также имеет некоторые ограничения.

Шаг 1 - Напишите класс, который преобразует пакет в строку и сохранит его в общих предпочтениях.

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 bundle = new ActivityBridge(this).getData(getIntent());

Примечание. Это решение очищает сохраненный пакет после чтения и не возвращает пакет, если действие создается заново. Это обходной путь, и мы будем очень благодарны за любые предложения или проблемы.

Если вы передаете большую информацию из одного в другое действие, то это может замедлить работу приложения

но используйте Global Class для хранения переменных, используя которые вы можете легко получить или установить любые значения

что объявлено в Глобальном файле

смотрите эту ссылку:

http://androidresearch.wordpress.com/2012/03/22/defining-global-variables-in-android/

Насколько я помню, вплоть до API-8 (Froyo) были некоторые ограничения (например, 1 МБ) при передаче разлагаемых объектов через намерения. Тем не менее, вы можете просто записать свои данные для продажи в файл и отправить путь к файлу для вашего следующего действия через пакет. Позже, код вашей второй деятельности, чтобы прочитать данные из файла и затем удалить их.

Я решил записать полезную нагрузку в файл, а затем прочитать ее в дочернем действии, потому что:

  • может выжить, когда приложение будет убито ОС
    • (например, после перехода к новому действию приложение надолго остается в фоновом режиме)
    • (например, включены Параметры разработчика> Не сохранять действия)
  • нет необходимости в глобальных / статических переменных

этот класс ниже () позволяет сохранять и загружать " Payload"в файл и из файла. DataReference сам по себе Serializable чтобы его можно было записать в Bundle или Intent быть переданным в новый 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
    }
}

Я понятия не имею, почему это не работает с большими данными, но если вы не можете найти способ исправить это, я предлагаю вам использовать пользовательское глобальное приложение, как здесь. (Также проверьте правильный ответ, чтобы он работал)

Проблема: передача больших данных во второе действие. Как было предложено в руководстве по Google Android, для обмена данными между действиями можно использовать статические поля или синглтоны. Вы можете передавать данные между действиями в приложении 3 способами. Намерение; SharedPreferences; Заявка; передача данных намеренно имеет некоторый предел. Для большого количества данных вы можете использовать совместное использование данных на уровне приложения, и, сохраняя его в SharedPreference, вы увеличиваете размер вашего приложения.

Как передавать большие данные между действиями в Android? Этот пример демонстрирует, как передавать большие данные между действиями в Android Шаг 1 - Создайте новый проект в Android Studio, перейдите в раздел Отправка данных между действиями в Android. Намерения Android - это объекты, используемые для запуска действий. из других действий Android. Одним из наиболее распространенных способов использования намерений является открытие нового действия в вашем приложении. Часто вам может потребоваться передать информацию в новое действие.

Как вы передаете большие данные между действиями и защитой, +1, используйте библиотеку базы данных ORM android. Как Realm, DBFlow или другой ORM. Откуда берутся изображения? Передача изображений намеренно означает их хранение в данных. Передача данных между фрагментами. Начиная с Fragment 1.3.0-alpha04, каждый FragmentManager реализует FragmentResultOwner . Это означает, что FragmentManager может действовать как центральное хранилище результатов фрагментов.

Другие вопросы по тегам