Каков наилучший способ обмена данными между действиями?
У меня есть одно действие, которое является основным действием, используемым в приложении, и оно имеет ряд переменных. У меня есть два других занятия, которые я хотел бы использовать, используя данные первого занятия. Теперь я знаю, что могу сделать что-то вроде этого:
GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();
Однако я хочу поделиться множеством переменных, некоторые из которых могут быть довольно большими, поэтому я не хочу создавать их копии, как описано выше.
Есть ли способ напрямую получить и изменить переменные без использования методов get и set? Я помню, как читал статью на сайте разработчиков Google, в которой говорилось, что это не рекомендуется для производительности на Android.
14 ответов
Вот подборка наиболее распространенных способов добиться этого:
- Отправить данные внутри намерения
- Статические поля
- 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 может решить убить ваше приложение. В таком сценарии у меня были случаи, когда 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();
Используйте приложение синглтон
Приложение Singleton является экземпляром 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 String 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);
Вы можете или не должны передавать идентификатор объекта, используя дополнительные функции намерения. Все зависит от вашей конкретной проблемы.
Сохранять объекты на диске
Идея состоит в том, чтобы сохранить данные на диске перед запуском другого действия.
Преимущества: вы можете запустить действие из других мест и, если данные уже сохранены, оно должно работать просто отлично.
Недостатки: это громоздко и требует больше времени для реализации. Требуется больше кода и, следовательно, больше шансов на появление ошибок. Это также будет намного медленнее.
Вот некоторые из способов сохранения объектов:
- Сохраните их в общих настройках
- Сохраните их в базу данных sqlite
- Сохраните их в файл (я бы избежал этого)
Что вы можете использовать:
- передача данных между действиями (как сказал Кристиан)
- использование класса с большим количеством статических переменных (так что вы можете вызывать их без экземпляра класса и без использования getter/setter)
- Использование базы данных
- Общие настройки
То, что вы выбираете, зависит от ваших потребностей. Вероятно, вы будете использовать более одного способа, когда у вас есть "много"
Делайте то, что Google приказывает вам сделать! здесь: http://developer.android.com/resources/faq/framework.html
- Примитивные типы данных
- Непостоянные объекты
- Синглтон класс - мой любимый:D
- Открытое статическое поле / метод
- Хэш-карта слабых ссылок на объекты
- Постоянные объекты (настройки приложения, файлы, контент-провайдеры, БД SQLite)
"Однако я хочу поделиться многими переменными, и некоторые из них могут быть довольно большими, поэтому я не хочу создавать их копии, как описано выше".
Это не делает копию (особенно со String, но даже объекты передаются по значению ссылки, а не самого объекта, и подобный метод получателя подходит для использования - возможно, лучше использовать, чем другие средства, потому что они распространены и понятно хорошо). Старые "мифы о производительности", такие как не использование геттеров и сеттеров, все еще имеют некоторую ценность, но также были обновлены в документах.
Но если вы не хотите этого делать, вы также можете просто сделать переменные общедоступными или защищенными в GlobalState и получить к ним доступ напрямую. И вы можете сделать статический синглтон, как указывает объект приложения JavaDoc:
Обычно нет необходимости создавать подкласс Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), функции для его получения может быть задан контекст, который внутренне использует Context.getApplicationContext() при первом создании синглтона.
Использование данных Intent, как отмечают другие ответы, - это еще один способ передачи данных, но обычно он используется для небольших данных и простых типов. Вы можете передавать большие / более сложные данные, но они более сложны, чем просто использование статического одиночного кода. Тем не менее, объект Application по-прежнему является моим любимым средством совместного использования больших / более сложных непостоянных данных между компонентами приложения Android (поскольку в приложении Android он имеет четко определенный жизненный цикл).
Кроме того, как отметили другие, если данные становятся очень сложными и должны быть постоянными, вы можете использовать SQLite или файловую систему.
Существует новый и лучший способ обмена данными между действиями, это LiveData. Обратите внимание, в частности, на эту цитату со страницы разработчика Android:
Тот факт, что объекты LiveData учитывают жизненный цикл, означает, что вы можете делиться ими между несколькими действиями, фрагментами и службами. Для простоты примера вы можете реализовать класс LiveData как одиночный
Смысл этого огромен - любые данные модели могут быть разделены в общем одноэлементном классе внутри LiveData
обертка. Это может быть введено из деятельности в их соответствующие ViewModel
ради проверяемости. И вам больше не нужно беспокоиться о слабых ссылках, чтобы предотвратить утечки памяти.
Вы можете расширить класс Application и тег на любые объекты, которые вы хотите там, они будут доступны в любом месте вашего приложения
Существуют различные способы обмена данными между действиями.
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 и обычные объекты.
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();
Использование хэш-карты подхода со слабыми ссылками, описанного выше и в http://developer.android.com/guide/faq/framework.html кажется мне проблематичным. Как восстанавливаются целые записи, а не только значение карты? В какой сфере вы выделяете это? Поскольку инфраструктура контролирует жизненный цикл Деятельности, владение одним из участвующих Мероприятий может привести к ошибкам во время выполнения, когда владелец уничтожается заранее перед своими клиентами. Если приложению принадлежит его, некоторые действия должны явно удалить запись, чтобы хеш-карта не удерживала записи с действительным ключом и потенциально собранной слабой ссылкой. Кроме того, что должен делать клиент, когда значение, возвращаемое для ключа, является нулевым?
Мне кажется, что WeakHashMap, принадлежащий приложению или в пределах одного экземпляра, является лучшим выбором. Доступ к значению на карте осуществляется через ключевой объект, и когда нет строгих ссылок на ключ (т. Е. Все действия выполняются с ключом и тем, на что он отображается), GC может восстановить запись карты.
Пример обмена данными между активами. Пример передачи электронного письма после входа в систему.
"email" - это имя, которое можно использовать для ссылки на значение запрашиваемой активности.
1 код на странице входа
Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
openLoginActivity.putExtra("email", getEmail);
2 код на главной странице
Bundle extras = getIntent().getExtras();
accountEmail = extras.getString("email");
У меня есть несколько идей, но я не знаю, являются ли они тем, что вы ищете.
Вы можете использовать службу, которая содержит все данные, а затем просто привязать свои действия к службе для извлечения данных.
Или упакуйте свои данные в сериализуемый или пакетный и прикрепите их к пакету и передайте пакет между действиями.
Это может быть совсем не то, что вы ищете, но вы также можете попробовать использовать SharedPreferences или предпочтение в целом.
В любом случае, дайте мне знать, что вы решите.
Все вышеупомянутые ответы великолепны... Я просто добавляю еще один, о котором никто еще не упомянул, о сохранении данных посредством действий, а именно об использовании встроенной базы данных SQLite для Android для сохранения соответствующих данных... Фактически вы можете разместить databaseHelper в состоянии приложения и вызывает его по мере необходимости во время активации. Или просто создайте вспомогательный класс и, при необходимости, выполняйте вызовы БД... Просто добавьте еще один слой для рассмотрения... Но всех остальных ответов будет достаточно как хорошо.. на самом деле просто предпочтение
И если вы хотите работать с объектом данных, эти две реализации очень важны:
Сериализуемый против Parcelable
- Serializable - это маркерный интерфейс, который подразумевает, что пользователь не может упорядочить данные в соответствии со своими требованиями. Поэтому, когда объект реализует Serializable, Java автоматически сериализует его.
- Parcelable - это собственный протокол сериализации для Android. В Parcelable разработчики пишут собственный код для маршалинга и демаршалинга. Таким образом, он создает меньше мусорных объектов по сравнению с сериализацией
- Производительность Parcelable очень высока по сравнению с Serializable из-за его пользовательской реализации. Настоятельно рекомендуется использовать Parcelable implantation при сериализации объектов в Android.
public class User implements Parcelable
проверьте больше здесь
Если вы собираетесь вызывать другие действия из текущего действия, вы должны использовать Intents. Вы можете сосредоточиться не столько на сохранении данных, сколько на обмене по мере необходимости.
Однако, если вам действительно нужно сохранить эти значения, вы можете сохранить их в каком-либо структурированном текстовом файле или базе данных в локальном хранилище. Файл свойств, файл XML или файл JSON могут хранить ваши данные и могут быть легко проанализированы при создании действия. Не забывайте также, что у вас есть SQLite на всех устройствах Android, поэтому вы можете хранить их в таблице базы данных. Вы также можете использовать Map для хранения пар ключ-значение и сериализации карты в локальное хранилище, но это может быть слишком громоздким, чтобы быть полезным для простых структур данных.
Предполагая, что вы вызываете действие два из действия одно, используя намерение.
Вы можете передать данные с помощью intent.putExtra(),
Возьмите это для справки. Отправка массивов с помощью Intent.putExtra
Надеюсь, это то, что вы хотите.