Який найкращий спосіб обміну даними між видами діяльності?


239

У мене є одна діяльність, яка є основною діяльністю, що використовується в додатку, і вона має ряд змінних. У мене є ще два види діяльності, які я хотів би мати можливість використовувати дані з першого заняття. Тепер я знаю, що можу зробити щось подібне:

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

Однак я хочу поділитися великою кількістю змінних, а деякі можуть бути досить великими, тому я не хочу створювати їх копії, як вище.

Чи існує спосіб безпосередньо отримати та зміни змінних без використання методів get і set? Я пам'ятаю, як читав статтю на сайті Google Dev, в якій говорилося, що це не рекомендується для роботи на Android.


2
Станом на Android 2.3 (Пряники), оптимізація отримання / набору здійснюється автоматично Dalvik; це актуально лише в тому випадку, якщо ви орієнтовані на більш старі версії Android.
StellarVortex

Зауважте, що приклад не копіює рядкові дані. Він швидше створює посилання на один і той же об’єкт рядка.
Код-учень

Важко повірити, чому немає можливості розпочати діяльність з іншої діяльності та передати будь-який складний об’єкт від першого до другого? Без серіалізації, збереження об'єкта та всіх цих зусиль. Це отвір безпеки або яка інша причина проти простої передачі посилання на об'єкт, якщо обидві дії в одному додатку? (Я розумію, що це по-іншому, якщо вони є в різних додатках)
Droidum


LiveData - найкраще, найсвіжіше рішення. Перевірте мою відповідь нижче.
Амір

Відповіді:


476

Ось компіляція найпоширеніших способів досягнення цього :

  • Надіслати дані всередині наміру
  • Статичні поля
  • HashMap of WeakReferences
  • Зберігаються об'єкти (sqlite, параметри спільного доступу, файл тощо)

TL; DR : Є два способи обміну даними: передача даних у додатки намірів або збереження їх десь в іншому місці. Якщо дані є примітивами, Strings або визначені користувачем об’єкти: надішліть їх як частину додаткових додатків (визначені користувачем об'єкти повинні реалізувати 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");

Використовуйте цей метод, якщо ви передаєте примітивні дані або Strings . Ви також можете передавати об'єкти, які реалізує 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();

Використовуйте додаток одиночний

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 void setData(String data) {DataHolder.data = data;}
}

Із розпочатої діяльності:

String data = DataHolder.getData();

HashMap of 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, і це чудово працює. Тепер, коли сказав, що ОП використовував рядок у прикладі, для цього наміри є ідеальними та бажаними.
Чарлі Коллінз

10
Виявлено, що серіалізація має серйозні проблеми щодо продуктивності моделі Android. Ось чому вони представили Parcelable. Прочитайте Parcelable замість Serializable у наведеній вище відповіді.
Субін Себастьян

3
Це робиться setResultметодом. Також у цьому випадку вторинна діяльність повинна бути використана startActivityForResultметодом.
Крістіан

2
Чудовий підсумок! Що стосується проблеми знищення одиночних клавіш, існує просте рішення для додатків з великою кількістю діяльності та об’єктів: Використовуйте підклас у Activityвсьому, і onCreate()перевірте будь-яке статичне поле сингтона, яке ви заповнюєте при запуску програми. Якщо це поле порожнє, поверніться до запускової діяльності, використовуючи FLAG_ACTIVITY_CLEAR_TASKабо BroadcastReceiverдля вбивства інших дій.
Янош

1
Я б не рекомендував зберігати дані в класі додатків. Детальніше читайте тут: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

Що ви можете використовувати:

  1. передача даних між видами діяльності (як сказав Крістіан)
  2. використання класу з великою кількістю статичних змінних (тому ви можете викликати їх без екземпляра класу та без використання getter / setter)
  3. Використання бази даних
  4. Спільні налаштування

Що ви виберете, залежить від ваших потреб. Ймовірно, ви будете використовувати більше ніж один спосіб, коли у вас буде "багато"


1
Зверніть увагу, що
статистика

@EpicPandaForce звичайно, а також коли пристрій було вимкнено.
WarrenFaith

1
Але якщо пристрій вимкнено, додаток перезавантажується з MAINдії. Після смерті процесу ви перезапустите будь-яку діяльність, яка була відкрита останньою, що може бути сторінкою деталей десь у програмі.
EpicPandaForce

@EpicPandaForce, здається, ти пропустив мою іронію, навіть коли ти чіткий Cpt.
WarrenFaith

16

Виконайте те, що Google наказує вам робити! тут: http://developer.android.com/resources/faq/framework.html#3

  • Примітивні типи даних
  • Нестійкі об’єкти
  • Клас одиночки - мій улюблений: D
  • Публічне статичне поле / метод
  • Хеш-карта слабких посилань на об'єкти
  • Постійні об'єкти (Налаштування програми, Файли, ContentProviders, SQLite DB)

1
Посилання Google для стійких об’єктів: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk

Тільки антипатерні, клас Singleton - це перша модель дизайну, клас зі статичним полем / методом поводиться дуже схоже на одиночні кнопки і створювати базу даних для збереження об'єктів Busniss іноді не дуже добре, звичайно, це не ваша вина, і ви перераховуєте можливі способи отримати це, але мені цікаво, чому Google ускладнив таку дуже дурну штуку, проблеми з роботою або що таке ?? !!!! чи я не розумію спосіб Android ?? !!!!
La VloZ Merrill

посилання розірвано :(
Shayan_Aryan

14

"Однак я хочу поділитися великою кількістю змінних, а деякі можуть бути досить великими, тому я не хочу створювати їх копії, як вище".

Це не робить копію (особливо це стосується String , але навіть об'єкти передаються за значенням посилання, а не самого об'єкта, а подібні геттери - прекрасні у використанні - можливо, краще використовувати, ніж інші засоби, тому що вони є загальними і добре зрозуміла). Старіші "міфи про ефективність", такі як використання геттерів та сетерів, все ще мають деяке значення, але також були оновлені в документах .

Але якщо ви не хочете цього робити, ви також можете просто зробити загальні або захищені змінні в GlobalState та отримати доступ до них безпосередньо. І ви можете зробити статичний синглтон, як вказує об'єкт Application JavaDoc :

Зазвичай немає необхідності в підкласі Application. У більшості випадків статичні одинарні можуть надавати однакові функціональні можливості більш модульним способом. Якщо вашому синглтону потрібен глобальний контекст (наприклад, для реєстрації широкомовних приймачів), функція для його отримання може бути надана Контексту, який внутрішньо використовує Context.getApplicationContext () під час першої побудови сингтона.

Використання даних про наміри , як і інші відповіді, тут відзначають - це ще один спосіб передачі даних, але він зазвичай використовується для менших даних та простих типів. Ви можете передавати більші / складніші дані, але це більше, ніж просто статичне одиночне зображення. Об'єкт Application все ще є моїм улюбленим для обміну більшими / складнішими непостійними даними між компонентами додатків 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 "Як я можу передавати дані між видами діяльності / сервісами в рамках однієї програми?", А про клас програми не згадується.
Джеррі101

Мені подобається використовувати об’єкт Application, дотримуючись прецеденту, який ви встановили в "Android в дії". Але багатьом потенційним роботодавцям це не подобається, коли вони бачать це в проблемах з кодом. Вони можуть помилятися, але вони кидають свою вагу. BTW: це посилання Framework.html # 3 більше не працює.
Метт Дж.

7

Ви можете розширити клас програми та позначити будь-які об’єкти, які ви хочете там, вони потім будуть доступні в будь-якій точці вашої програми


Я б не рекомендував зберігати дані в класі додатків. Детальніше читайте тут: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

4

Існує новий і кращий спосіб обміну даними між видами діяльності, і це LiveData . Зокрема, зверніть увагу на цю цитату зі сторінки розробника Android:

Той факт, що об’єкти LiveData усвідомлюють життєвий цикл, означає, що ви можете ділитися ними між різними видами діяльності, фрагментами та службами. Щоб приклад був простим, ви можете реалізувати клас LiveData як сингл

Наслідок цього величезний - будь-які дані моделі можуть передаватися в загальний однотонний клас всередині LiveDataобгортки. Її можна вводити від діяльності у відповідну ViewModelдля того, щоб це було визначено. І вам більше не потрібно турбуватися про слабкі посилання, щоб запобігти витоку пам'яті.


2

Використання хешмапу слабкого довідкового підходу, описаного вище, та в http://developer.android.com/guide/faq/framework.html мені здається проблематичним. Як відтворюються цілі записи, а не лише значення карти? Яку сферу ви виділяєте? Оскільки рамка контролює життєвий цикл діяльності, наявність одного з заходів, що беруть участь, вона ризикує помилками під час виконання часу, коли власник заздалегідь буде знищений своїми клієнтами. Якщо Додатком належить його, деяка Активність повинна явно видалити запис, щоб уникнути хешмапу, щоб він тримався за записами з дійсним ключем та потенційно зібраним зібраним слабким посиланням. Крім того, що повинен робити клієнт, коли значення, повернене для ключа, є нульовим?

Мені здається, що кращим вибором є WeakHashMap, який належить Додатку або в межах одиночного. Доступ до значення на карті здійснюється за допомогою ключового об'єкта, і коли немає чітких посилань на ключ (тобто всі дії виконуються з ключем і на що він відображається), GC може відновити запис карти.


2

Існують різні способи обміну даними між видами діяльності

1: передача даних між видами діяльності за допомогою наміру

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

2: Використовуючи статичне ключове слово, визначте змінну як загальнодоступну статичну та використовуйте будь-яке місце в проекті

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

1

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

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

Або упакуйте ваші дані в серіалізаційний або розбірний і приєднайте їх до пакету та передайте пакет між видами діяльності.

Це може бути зовсім не те, що ви шукаєте, але ви також можете спробувати використовувати SharedPreferences або загальну перевагу.

У будь-якому випадку повідомте мені, що ви вирішили.



1

Якщо ви маєте намір зателефонувати на інші дії з поточної активності, слід скористатись Інтенціями . Ваша увага може бути меншою за збереження даних, ніж для обміну ними за потребою.

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


1

Усі вищезазначені відповіді чудові ... Я лише додав, що ніхто ще не згадував про збереження даних через діяльність, і це використовувати вбудовану базу даних для Android SQLite для збереження відповідних даних ... Насправді ви можете розмістити свої databaseHelper у стані програми та зателефонуйте за потребою впродовж усього активує. Або просто зробіть клас помічника та виконайте дзвінки БД, коли це потрібно ... Просто додайте ще один шар, який слід врахувати ... Але всіх інших відповідей було б достатньо а також .. Дійсно просто вподобання


1

Обмін даними між прикладами активів, що передають електронний лист після входу

"електронна пошта" - це ім'я, яке може бути використане для посилання на значення для запитуваної діяльності

1 Код на сторінці входу

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

2 код на домашній сторінці

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

1

І якщо ви хочете працювати з об'єктом даних, це два реалізації дуже важливі:

Серіалізабельність проти парцеляції

  • Serializable - це маркерний інтерфейс, який передбачає, що користувач не може перевести дані відповідно до своїх вимог. Тож коли об’єкт реалізує Serializable Java автоматично його серіалізує.
  • Роздільна здатність - це власний протокол серіалізації для Android. У програмі Parcelable розробники пишуть спеціальний код для маршалінгу та нерозподілу даних. Таким чином, це створює менше об'єктів для сміття порівняно з серіалізацією
  • Продуктивність Parcelable дуже висока в порівнянні з Serializable через його власну реалізацію Настійно рекомендується використовувати Parcelable імплантацію при серіалізації об'єктів в android.

public class User implements Parcelable

дізнайтеся більше тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.