Каков наилучший способ обмена данными между действиями?


239

У меня есть одно действие, которое является основным действием, используемым в приложении, и оно имеет ряд переменных. У меня есть два других занятия, которые я хотел бы использовать, используя данные первого занятия. Теперь я знаю, что могу сделать что-то вроде этого:

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

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

Есть ли способ напрямую получить и изменить переменные без использования методов get и set? Я помню, как читал статью на сайте разработчиков Google, в которой говорилось, что это не рекомендуется для производительности на Android.


2
Начиная с Android 2.3 (Gingerbread), оптимизация get / set выполняется автоматически Dalvik; это актуально, только если вы ориентируетесь на более старые версии Android.
StellarVortex

Обратите внимание, что пример не копирует строковые данные. Скорее он создает ссылку на тот же строковый объект.
Код-ученик

Трудно поверить, почему нет возможности начать действие с другого вида деятельности и передать какой-либо сложный объект с первого на второй? Без сериализации, сохранения объекта и всех этих усилий. Это дыра в безопасности или какая другая причина против простой передачи ссылки на объект, если оба действия находятся в одном приложении? (Я понимаю, что по-разному, если они в разных приложениях)
Droidum

Пожалуйста, смотрите это stackoverflow.com/questions/56521969/…
Левон Петросян

LiveData - лучшее, самое последнее решение. Проверьте мой ответ ниже.
Амир Увал

Ответы:


476

Вот подборка самых распространенных способов добиться этого :

  • Отправить данные внутри намерения
  • Статические поля
  • HashMap из WeakReferences
  • Сохранять объекты (sqlite, общие настройки, файлы и т. Д.)

TL; DR : существует два способа обмена данными: передача данных в дополнениях к цели или сохранение их в другом месте. Если данные являются примитивами, строками или объектами, определенными пользователем: отправьте их как часть дополнений к намерениям (объекты, определенные пользователем, должны быть реализованы Parcelable). При передаче сложных объектов сохраняйте экземпляр в одном месте где-нибудь еще и получайте к ним доступ из запущенного действия.

Некоторые примеры того, как и зачем реализовывать каждый подход:

Отправить данные в намерениях

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

На втором занятии:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

Используйте этот метод, если вы передаете примитивные данные или строки . Вы также можете передавать объекты, которые реализует Serializable.

Несмотря на соблазн, вы должны подумать дважды перед использованием Serializable: это подвержено ошибкам и ужасно медленно. Итак, в целом: держитесь подальше,Serializable если это возможно. Если вы хотите передать сложные пользовательские объекты, взгляните на Parcelableинтерфейс . Его сложнее реализовать, но он значительно выиграл в скорости Serializable.

Обмен данными без сохранения на диске

Можно обмениваться данными между действиями, сохраняя их в памяти, учитывая, что в большинстве случаев обе операции выполняются в одном и том же процессе.

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

Используйте одноэлементный класс

Есть класс для хранения данных:

public class DataHolder {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}

  private static final DataHolder holder = new DataHolder();
  public static DataHolder getInstance() {return holder;}
}

Из запущенной деятельности:

String data = DataHolder.getInstance().getData();

Используйте приложение синглтон

Синглтон приложения - это экземпляр, android.app.Applicationкоторый создается при запуске приложения. Вы можете предоставить пользовательский, расширив Application:

import android.app.Application;
public class MyApplication extends Application {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}
}

Перед началом деятельности:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

Тогда из запущенной деятельности:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

Статические поля

Идея в основном такая же, как у синглтона, но в этом случае вы предоставляете статический доступ к данным:

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static void setData(String data) {DataHolder.data = data;}
}

Из запущенной деятельности:

String data = DataHolder.getData();

HashMap из WeakReferences

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

public class DataHolder {
  Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();

  void save(String id, Object object) {
    data.put(id, new WeakReference<Object>(object));
  }

  Object retrieve(String id) {
    WeakReference<Object> objectWeakReference = data.get(id);
    return objectWeakReference.get();
  }
}

Перед началом деятельности:

DataHolder.getInstance().save(someId, someObject);

Из запущенной деятельности:

DataHolder.getInstance().retrieve(someId);

Вы можете или не можете передавать идентификатор объекта, используя дополнительные функции намерения. Все зависит от вашей конкретной проблемы.

Сохранять объекты на диске

Идея состоит в том, чтобы сохранить данные на диске перед запуском другого действия.

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

Недостатки: это громоздко и требует больше времени для реализации. Требуется больше кода и, следовательно, больше шансов на появление ошибок. Это также будет намного медленнее.

Вот некоторые из способов сохранения объектов:


11
Я бы сказал, что это не «нормальный» способ для больших / более сложных данных. Намного проще использовать статический синглтон или объект Application, и он прекрасно работает. Теперь, когда сказано, что OP действительно использовал строку в примере, для этого Intent идеален и предпочтителен.
Чарли Коллинз

10
В Serializable обнаружены серьезные проблемы с производительностью в модели процессов Android. Вот почему они представили Parcelable. Прочитайте Parcelable вместо Serializable в ответе выше.
Субин Себастьян

3
Это делается с помощью setResultметода. Кроме того, в этом случае вторичная активность должна вызываться с использованием startActivityForResultметода.
Кристиан

2
Отличное резюме! Что касается проблемы уничтожения синглетонов, существует простое решение для приложений с большим количеством действий и объектов: используйте подкласс Activityвсех и onCreate()проверяйте любое статическое поле синглета, которое вы заполняете при запуске приложения. Если это поле пустое, вернитесь к началу действия, используя FLAG_ACTIVITY_CLEAR_TASKили, BroadcastReceiverчтобы убить другие действия.
Janosch

1
Я бы не рекомендовал сохранять данные в классе приложения. Узнайте больше здесь: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

Что вы можете использовать:

  1. передача данных между действиями (как сказал Кристиан)
  2. использование класса с большим количеством статических переменных (так что вы можете вызывать их без экземпляра класса и без использования getter / setter)
  3. Использование базы данных
  4. Общие настройки

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


1
Обратите внимание, что статика очищается при смерти процесса
EpicPandaForce

@EpicPandaForce конечно, а также когда устройство было выключено.
WarrenFaith

1
Но если устройство выключено, приложение перезапускается с MAINдействия. После завершения процесса вы перезапускаете все действия, которые были открыты последними, что может быть страницей с подробностями где-то глубоко в приложении.
EpicPandaForce

@EpicPandaForce, кажется, вы пропустили мою иронию, даже если вы - очевидный Cpt.
WarrenFaith

16

Делайте то, что Google приказывает вам делать! здесь: http://developer.android.com/resources/faq/framework.html#3

  • Примитивные типы данных
  • Непостоянные объекты
  • Синглтон класс - мой любимый: D
  • Открытое статическое поле / метод
  • Хэш-карта слабых ссылок на объекты
  • Постоянные объекты (настройки приложения, файлы, контент-провайдеры, БД SQLite)

1
Ссылка Google для постоянных объектов: developer.android.com/guide/topics/data/data-storage.html
Никоминск,

Только антипаттерны, класс Singleton является первым шаблоном проектирования, класс со статическим полем / методом ведет себя подобно синглетонам, и создание базы данных для сохранения объектов busniss иногда не является хорошей вещью, конечно, это не ваша ошибка, и вы перечисляете возможность способы добиться этого, но мне интересно, почему Google усложнил такую ​​очень глупую вещь, проблемы с производительностью или что ?? ?? !!!! или я не понимаю способ Android ?? !!!!
La VloZ Merrill

ссылка не работает :(
Shayan_Aryan

14

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

Это не делает копию (особенно со String , но даже объекты передаются по значению ссылки, а не самому объекту, и подобный метод получателя подходит для использования - возможно, лучше использовать, чем другие средства, потому что они распространены и хорошо понимал). Старые «мифы о производительности», такие как не использование геттеров и сеттеров, все еще имеют некоторую ценность, но также были обновлены в документах .

Но если вы не хотите этого делать, вы также можете просто сделать переменные общедоступными или защищенными в GlobalState и получить к ним доступ напрямую. И вы можете сделать статический синглтон, как указывает объект приложения JavaDoc :

Обычно нет необходимости создавать подкласс Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону необходим глобальный контекст (например, для регистрации широковещательных приемников), функции для его получения может быть задан контекст, который внутренне использует Context.getApplicationContext () при первом создании синглтона.

Использование данных Intent , как отмечают другие ответы, - это еще один способ передачи данных, но обычно он используется для небольших данных и простых типов. Вы можете передавать большие / более сложные данные, но они более сложны, чем просто использование статического одиночного кода. Тем не менее, объект « Приложение» все еще является моим личным фаворитом для совместного использования больших / более сложных непостоянных данных между компонентами приложения Android (поскольку в приложении Android он имеет четко определенный жизненный цикл).

Кроме того, как уже отмечали другие, если данные становятся очень сложными и должны быть постоянными, то вы также можете использовать SQLite или файловую систему.


1
На самом деле, я недавно наткнулся на это в документации: developer.android.com/guide/appendix/faq/framework.html#3 . Для «непостоянных сложных объектов» рекомендуется использовать класс Application для создания и удаления статического синглтона! Таким образом, вы получаете четко определенные приложения жизненного цикла и простоту использования статического синглтона.
Чарли Коллинз

2
этот раздел faq, кажется, теперь удален (я вижу только «непостоянные объекты» и не упоминаю класс Application). В любом случае, вы можете уточнить?
Тони Чан

1
В настоящее время он находится на сайте developer.android.com/guide/faq/framework.html#3 «Как передавать данные между операциями / службами в рамках одного приложения?», И в нем нет упоминания о классе приложения.
Jerry101

Мне нравится использовать объект Application, следуя прецеденту, установленному вами в «Android в действии». Но многим потенциальным работодателям не нравится, когда они видят это в проблемах с кодом. Они могут ошибаться, но они разбрасывают свой вес. Кстати: эта ссылка framework.html # 3 больше не работает.
Мэтт Дж.


4

Существует новый и лучший способ обмена данными между действиями, это LiveData . Обратите внимание, в частности, на эту цитату со страницы разработчика Android:

Тот факт, что объекты LiveData учитывают жизненный цикл, означает, что вы можете делиться ими между несколькими действиями, фрагментами и службами. Для простоты примера вы можете реализовать класс LiveData как одиночный

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


2

Использование хэш-карты подхода со слабыми ссылками, описанного выше, и в http://developer.android.com/guide/faq/framework.html мне кажется проблематичным. Как восстанавливаются целые записи, а не только значение карты? В какой сфере вы выделяете это? Поскольку инфраструктура контролирует жизненный цикл Деятельности, владение одним из участвующих Мероприятий может привести к ошибкам во время выполнения, когда владелец уничтожается заранее перед своими клиентами. Если приложению принадлежит его, некоторое действие должно явно удалить запись, чтобы хеш-таблица не удерживала записи с действительным ключом и потенциально собранной слабой ссылкой. Кроме того, что должен делать клиент, когда значение, возвращаемое для ключа, является нулевым?

Мне кажется, что WeakHashMap, принадлежащий приложению или в пределах одного экземпляра, является лучшим выбором. Доступ к значению на карте осуществляется через ключевой объект, и когда нет строгих ссылок на ключ (т. Е. Все действия выполняются с ключом и тем, на что он отображается), GC может восстановить запись карты.


2

Существуют различные способы обмена данными между действиями.

1: Передача данных между действиями с использованием Intent

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2: Используя ключевое слово static, определите переменную как public static и используйте любое место в проекте

      public static int sInitialValue=0;

использовать в любом месте проекта, используя classname.variableName;

3: Использование базы данных

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

4: Использование общих настроек

намного проще, чем база данных. но есть некоторые ограничения: вы не можете сохранять объекты ArrayList, List и custome.

5: Создайте установщик геттера в классе Aplication и получите доступ к любой точке проекта.

      private String data;
      public String getData() {
          return data;
      }

      public void setData(String data) {
          this.data = data;
      }

здесь установить и получить от деятельности

         ((YourApplicationClass)getApplicationContext()).setData("abc"); 

         String data=((YourApplicationClass)getApplicationContext()).getData();  

1

Ну, у меня есть несколько идей, но я не знаю, являются ли они тем, что вы ищете.

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

Или упакуйте свои данные в сериализуемый или пакетный и прикрепите их к пакету и передайте пакет между действиями.

Это может быть совсем не то, что вы ищете, но вы также можете попробовать использовать SharedPreferences или предпочтение в целом.

В любом случае, дайте мне знать, что вы решите.



1

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

Однако, если вам действительно нужно сохранить эти значения, вы можете сохранить их в каком-либо структурированном текстовом файле или базе данных в локальном хранилище. Файл свойств, файл XML или файл JSON могут хранить ваши данные и могут быть легко проанализированы при создании действия. Не забывайте также, что у вас есть SQLite на всех устройствах Android, чтобы вы могли хранить их в таблице базы данных. Вы также можете использовать Map для хранения пар ключ-значение и сериализации карты в локальное хранилище, но это может быть слишком громоздким, чтобы быть полезным для простых структур данных.


1

Все вышеупомянутые ответы великолепны ... Я просто добавляю еще один, о котором никто еще не упомянул, о сохранении данных посредством действий, а именно об использовании встроенной базы данных SQLite для Android для сохранения соответствующих данных ... Фактически вы можете разместить databaseHelper в состоянии приложения и вызывайте его по мере необходимости во время активации. Или просто создайте вспомогательный класс и, при необходимости, выполняйте вызовы БД ... Просто добавьте еще один слой для рассмотрения ... Но всех остальных ответов будет достаточно как хорошо .. на самом деле просто предпочтение


1

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

«email» - это имя, которое можно использовать для ссылки на значение запрашиваемой активности

1 код на странице входа

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
    openLoginActivity.putExtra("email", getEmail);

2 код на главной странице

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");

1

И если вы хотите работать с объектом данных, эти две реализации очень важны:

Сериализуемый против Parcelable

  • Сериализуемый - это маркерный интерфейс, который подразумевает, что пользователь не может упорядочить данные в соответствии со своими требованиями. Поэтому, когда объект реализует Serializable, Java автоматически сериализует его.
  • Parcelable - это собственный протокол сериализации для Android. В Parcelable разработчики пишут собственный код для маршалинга и демаршалинга. Таким образом, он создает меньше мусорных объектов по сравнению с сериализацией
  • Производительность Parcelable очень высока по сравнению с Serializable из-за его пользовательской реализации. Настоятельно рекомендуется использовать Parcelable implantation при сериализации объектов в Android.

public class User implements Parcelable

проверьте больше здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.