Jaki jest najlepszy sposób udostępniania danych między działaniami?

239

Mam jedno działanie, które jest głównym działaniem używanym w całej aplikacji i ma wiele zmiennych. Mam dwie inne aktywności, które chciałbym móc wykorzystać dane z pierwszej aktywności. Teraz wiem, że mogę zrobić coś takiego:

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

Jednak chcę udostępnić wiele zmiennych, a niektóre mogą być dość duże, więc nie chcę tworzyć ich kopii jak wyżej.

Czy istnieje sposób na bezpośrednie uzyskanie i zmianę zmiennych bez użycia metod get and set? Pamiętam, jak czytałem artykuł na stronie deweloperów Google, który powiedział, że nie jest to zalecane ze względu na wydajność na Androidzie.

Szalony dureń
źródło
2
Od Androida 2.3 (Gingerbread) optymalizacja get / set jest wykonywana automatycznie przez Dalvik; jest to istotne tylko wtedy, gdy kierujesz reklamy na starsze wersje Androida.
StellarVortex
Zauważ, że przykład nie kopiuje danych ciągu. Raczej tworzy odwołanie do tego samego obiektu ciągu.
Code-Apprentice
Trudno w to uwierzyć, dlaczego nie ma możliwości rozpoczęcia działania od innego działania i przekazania dowolnego złożonego obiektu od pierwszego do drugiego? Bez serializacji, zapisywania obiektu i całego tego wysiłku. Czy jest to luka w zabezpieczeniach, czy z innego powodu nie można po prostu przekazać odwołania do obiektu, jeśli obie czynności są w tej samej aplikacji? (Rozumiem, że jest inaczej, jeśli są w różnych aplikacjach)
Droidum
Proszę zobaczyć ten stackoverflow.com/questions/56521969/…
Levon Petrosyan
LiveData to najlepsze, najnowsze rozwiązanie. Sprawdź moją odpowiedź poniżej.
Amir Uval,

Odpowiedzi:

476

Oto zestawienie najczęstszych sposobów osiągnięcia tego celu :

  • Wysyłaj dane w sposób zamierzony
  • Pola statyczne
  • HashMap of WeakReferences
  • Zachowaj obiekty (sqlite, preferencje udostępniania, plik itp.)

TL; DR : istnieją dwa sposoby udostępniania danych: przekazywanie danych do dodatków intencji lub zapisywanie ich gdzie indziej. Jeśli dane są prymitywne, ciągi lub obiekty zdefiniowane przez użytkownika: wyślij je jako część zamierzonych dodatków (obiekty zdefiniowane przez użytkownika muszą zostać zaimplementowane Parcelable). Jeśli przekazujesz złożone obiekty, zapisz instancję w singletonie w innym miejscu i uzyskaj do nich dostęp z uruchomionej aktywności.

Kilka przykładów tego, jak i dlaczego wdrożyć każde podejście:

Wysyłaj dane wewnątrz intencji

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

W sprawie drugiej aktywności:

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

Użyj tej metody, jeśli przekazujesz prymitywne dane lub ciągi . Możesz także przekazywać obiekty, które implementują Serializable.

Choć kuszące, powinieneś pomyśleć dwa razy przed użyciem Serializable: jest podatne na błędy i strasznie powolne. Więc ogólnie: trzymaj się z daleka,Serializable jeśli to możliwe. Jeśli chcesz przekazać złożone obiekty zdefiniowane przez użytkownika, spójrz na Parcelableinterfejs . Jest trudniejszy do wdrożenia, ale w porównaniu do niego ma znaczny wzrost prędkości Serializable.

Udostępniaj dane bez utrwalania się na dysku

Możliwe jest współdzielenie danych między działaniami poprzez zapisanie ich w pamięci, biorąc pod uwagę, że w większości przypadków oba działania przebiegają w tym samym procesie.

Uwaga: czasami, gdy użytkownik opuści Twoją aktywność (bez wychodzenia z niej), Android może zdecydować o zabiciu Twojej aplikacji. W takim scenariuszu napotkałem przypadki, w których Android próbuje uruchomić ostatnią aktywność, wykorzystując zamiar podany przed śmiercią aplikacji. W takich przypadkach dane przechowywane w singletonie (twoje lub Application) znikną i mogą się zdarzyć złe rzeczy. Aby uniknąć takich przypadków, należy utrwalić obiekty na dysku lub sprawdzić dane przed ich użyciem, aby upewnić się, że są prawidłowe.

Użyj klasy singleton

Poproś klasę, aby przechowała dane:

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

Z rozpoczętej działalności:

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

Użyj singletonu aplikacji

Singleton aplikacji to instancja android.app.Applicationtworzona podczas uruchamiania aplikacji. Możesz podać niestandardowy, rozszerzając 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;}
}

Przed rozpoczęciem działania:

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

Następnie z rozpoczętej działalności:

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

Pola statyczne

Pomysł jest zasadniczo taki sam jak singleton, ale w tym przypadku zapewniasz statyczny dostęp do danych:

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

Z rozpoczętej działalności:

String data = DataHolder.getData();

HashMap of WeakReferences

Ten sam pomysł, ale zezwalający śmieciarzowi na usuwanie niepowiązanych obiektów (np. Gdy użytkownik zakończy działanie):

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

Przed rozpoczęciem działania:

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

Z rozpoczętej działalności:

DataHolder.getInstance().retrieve(someId);

Możliwe, że nie musisz podawać identyfikatora obiektu przy użyciu dodatków intencji. Wszystko zależy od konkretnego problemu.

Utrwalaj obiekty na dysku

Chodzi o to, aby zapisać dane na dysku przed uruchomieniem innej czynności.

Zalety: możesz uruchomić działanie z innych miejsc, a jeśli dane są już utrwalone, powinno działać dobrze.

Wady: uciążliwe i zajmuje więcej czasu. Wymaga więcej kodu, a tym samym większej szansy na wprowadzenie błędów. Będzie też znacznie wolniej.

Niektóre sposoby utrwalania obiektów obejmują:

Cristian
źródło
11
Twierdziłbym, że nie jest to „normalny” sposób dla większych / bardziej złożonych danych. O wiele łatwiej jest używać statycznego singletona lub obiektu Application i działa świetnie. Teraz, gdy powiedziano, że OP użył w tym przykładzie łańcucha, Intent jest doskonały i preferowany.
Charlie Collins
10
Okazało się, że Serializable ma poważne problemy z wydajnością w modelu procesu Android. Dlatego wprowadzili paczkę. Przeczytaj paczkę zamiast serializacji w powyższej odpowiedzi.
Subin Sebastian,
3
Odbywa się to za pomocą setResultmetody. Ponadto w takim przypadku działanie drugorzędne należy wywołać za pomocą startActivityForResultmetody.
Cristian,
2
Świetne podsumowanie! Jeśli chodzi o problem niszczenia singletonów, istnieje proste rozwiązanie dla aplikacji z dużą ilością działań i obiektów: użyj podklasy obejmującej Activitycałe i onCreate()sprawdzając każde pole statyczne singletonu, które wypełniasz podczas uruchamiania aplikacji. Jeśli to pole jest puste, wróć do działania początkowego, używając przycisku FLAG_ACTIVITY_CLEAR_TASKlub, BroadcastReceiveraby zabić inne działania.
Janosch,
1
Nie polecam zapisywania danych w klasie aplikacji. Przeczytaj więcej tutaj: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan
22

Czego możesz użyć:

  1. przekazywanie danych między czynnościami (jak powiedział Cristian)
  2. za pomocą klasy z dużą ilością zmiennych statycznych (więc możesz wywoływać je bez instancji klasy i bez użycia gettera / settera)
  3. Korzystanie z bazy danych
  4. Wspólne preferencje

To, co wybierzesz, zależy od twoich potrzeb. Prawdopodobnie skorzystasz z więcej niż jednego sposobu, gdy masz „dużo”

WarrenFaith
źródło
1
Należy pamiętać, że statystyki są usuwane po śmierci procesowej
EpicPandaForce
@EpicPandaForce oczywiście, a także po wyłączeniu urządzenia.
WarrenFaith,
1
Ale jeśli urządzenie zostanie wyłączone, aplikacja uruchomi się ponownie MAIN. Po śmierci procesu wznawiasz wszystkie czynności, które były ostatnio otwarte, które mogą być szczegółowymi stronami gdzieś głęboko w aplikacji.
EpicPandaForce,
@EpicPandaForce wydaje się, że tęskniłeś za moją ironią, nawet jeśli jesteś Kpt Oczywistym.
WarrenFaith
16

Rób to, co nakazuje ci Google! tutaj: http://developer.android.com/resources/faq/framework.html#3

  • Prymitywne typy danych
  • Obiekty nietrwałe
  • Singleton class - moja ulubiona: D
  • Publiczne pole / metoda statyczna
  • HashMap of WeakReferences to Objects
  • Obiekty trwałe (Preferencje aplikacji, pliki, dostawcy treści, baza danych SQLite)
Tylko Arka Gdynia
źródło
1
Google Link dla obiektów trwałych: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk
Tylko antipatterns, klasa Singleton jest pierwszym wzorcem projektowym, klasa ze statycznym polem / metodą zachowuje się bardzo podobnie do singletonów, a tworzenie bazy danych dla trwałych obiektów busniss czasami nie jest dobrą rzeczą, oczywiście nie twoja wina i jesteś na liście sposoby na osiągnięcie tego, ale zastanawiam się, dlaczego Google skomplikowało tak bardzo głupią rzecz, problemy z wydajnością czy co? !!!! czy nie rozumiem sposobu na Androida? !!!!
La VloZ Merrill,
link jest zepsuty :(
Shayan_Aryan
14

„Jednak chcę udostępnić wiele zmiennych, a niektóre mogą być dość duże, więc nie chcę tworzyć ich kopii jak powyżej”.

To nie tworzy kopii (szczególnie w przypadku String , ale nawet obiekty przechodzą przez wartość referencji, a nie sam obiekt, i podobne metody gettera są w użyciu - prawdopodobnie lepiej niż w inny sposób, ponieważ są wspólne i dobrze zrozumiane). Starsze „mity dotyczące wydajności”, takie jak nieużywanie programów pobierających i ustawiających, nadal mają pewną wartość, ale zostały również zaktualizowane w dokumentach .

Ale jeśli nie chcesz tego robić, możesz również ustawić zmienne jako publiczne lub chronione ustawić GlobalState i uzyskać do nich bezpośredni dostęp. I można utworzyć statyczny singleton, ponieważ obiekt aplikacji JavaDoc wskazuje :

Zwykle nie ma potrzeby dzielenia aplikacji. W większości sytuacji statyczne singletony mogą zapewnić tę samą funkcjonalność w bardziej modułowy sposób. Jeśli twój singleton potrzebuje globalnego kontekstu (na przykład, aby zarejestrować odbiorniki rozgłoszeniowe), funkcja do odzyskania go może otrzymać kontekst, który wewnętrznie używa Context.getApplicationContext () podczas pierwszej budowy singletona.

Za pomocą danych zamiaru , jak zauważają inne odpowiedzi tutaj, to inny sposób przekazywania danych, ale zwykle jest używany do mniejszych danych i prostych typów. Możesz przekazywać większe / bardziej złożone dane, ale jest to bardziej zaangażowane niż tylko użycie pojedynczego statycznego pojedynczego elementu. Application jest nadal moim ulubionym miejscem do udostępniania większych / bardziej złożonych nietrwałych danych między komponentami aplikacji na Androida (ponieważ ma dobrze zdefiniowany cykl życia w aplikacji na Androida).

Ponadto, jak zauważyli inni, jeśli dane stają się bardzo złożone i muszą być trwałe , możesz użyć SQLite lub systemu plików.

Charlie Collins
źródło
1
Właściwie właśnie natknąłem się na to ostatnio w dokumentacji: developer.android.com/guide/appendix/faq/framework.html#3 . W przypadku „nietrwałych złożonych obiektów” zaleca się użycie klasy Application do utworzenia i zniszczenia statycznego singletona! W ten sposób uzyskuje się dobrze zdefiniowany cykl życia aplikacji oraz łatwość użycia statycznego singletonu.
Charlie Collins,
2
ta sekcja faq wydaje się teraz usunięta (widzę tylko „nietrwałe obiekty” i brak wzmianki o klasie aplikacji). W każdym razie możesz opracować?
Tony Chan,
1
Obecnie znajduje się na stronie developer.android.com/guide/faq/framework.html#3 „Jak przekazywać dane między działaniami / usługami w ramach jednej aplikacji?”, Bez wzmianki o klasie aplikacji.
Jerry101
Lubię korzystać z obiektu Application, zgodnie z precedensem ustawionym w „Android in Action”. Ale wielu potencjalnym pracodawcom nie podoba się, gdy widzą to w wyzwaniach kodu. Mogą się mylić, ale rzucają się wokół swojego ciężaru. BTW: ten link framework.html # 3 już nie działa.
Matt J.
4

Istnieje nowy i lepszy sposób udostępniania danych między działaniami, a jest nim LiveData . Zwróć uwagę w szczególności na ten cytat ze strony programisty Androida:

Fakt, że obiekty LiveData uwzględniają cykl życia, oznacza, że ​​można je udostępniać między wieloma działaniami, fragmentami i usługami. Aby uprościć przykład, możesz zaimplementować klasę LiveData jako singleton

Konsekwencje tego są ogromne - wszelkie dane modelu mogą być współużytkowane we wspólnej klasie singletonów wewnątrz LiveDataopakowania. Może być wstrzykiwany z działań do odpowiednich ViewModeldla celów testowalności. Nie musisz się już martwić słabymi referencjami, aby zapobiec wyciekom pamięci.

Amir Uval
źródło
2

Korzystanie z mapy skrótowej podejścia do słabych odniesień, opisanej powyżej oraz w http://developer.android.com/guide/faq/framework.htmlProblematyczne wydaje mi się . W jaki sposób odzyskuje się całe wpisy, a nie tylko wartość mapy? W jakim zakresie go przydzielasz? Ponieważ struktura kontroluje cykl życia działania, posiadanie jednego z uczestniczących działań może ryzykować błędy w czasie wykonywania, gdy właściciel zostanie zniszczony przed swoimi klientami. Jeśli aplikacja jest jej właścicielem, niektóre działania muszą jawnie usunąć wpis, aby uniknąć zatrzymania hashapy względem wpisów z ważnym kluczem i potencjalnie zniekształconym zbiorem słabych referencji. Co ponadto powinien zrobić klient, gdy wartość zwrócona dla klucza jest pusta?

Wydaje mi się, że lepszym wyborem jest WeakHashMap będący własnością aplikacji lub w ramach singletonu. Dostęp do wartości na mapie jest uzyskiwany za pomocą obiektu klucza, a gdy nie istnieją silne odniesienia do klucza (tzn. Wszystkie działania są wykonywane za pomocą klucza i tego, co jest mapowane), GC może odzyskać wpis na mapie.

Mark Rosenberg
źródło
2

Istnieją różne sposoby udostępniania danych między działaniami

1: Przekazywanie danych między działaniami przy użyciu zamiaru

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

2: Używając słowa kluczowego static, zdefiniuj zmienną jako publiczną statyczną i użyj dowolnego miejsca w projekcie

      public static int sInitialValue=0;

użyj w dowolnym miejscu w projekcie, używając classname.variableName;

3: Korzystanie z bazy danych

ale jest to nieco długi proces, musisz użyć zapytania do wstawienia danych i iterować dane za pomocą kursora, gdy zajdzie taka potrzeba. Ale nie ma szansy na utratę danych bez czyszczenia pamięci podręcznej.

4: Korzystanie ze wspólnych preferencji

znacznie łatwiejsze niż baza danych. ale istnieją pewne ograniczenia, których nie można zapisać ArrayList, List i obiektów klienta.

5: Utwórz narzędzie ustawiające getter w klasie Aplication i uzyskaj dostęp do dowolnego miejsca w projekcie.

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

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

tutaj ustaw i czerp z zajęć

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

         String data=((YourApplicationClass)getApplicationContext()).getData();  
Vikas Singh
źródło
1

Mam kilka pomysłów, ale nie wiem, czy są one tym, czego szukasz.

Możesz skorzystać z usługi, która przechowuje wszystkie dane, a następnie po prostu powiązać swoje działania z usługą w celu odzyskania danych.

Lub spakuj swoje dane do postaci szeregowej lub paczkowej i dołącz je do pakietu i przekaż pakiet między działaniami.

Ten może wcale nie być tym, czego szukasz, ale możesz także spróbować użyć wspólnych preferencji lub preferencji w ogóle.

Tak czy inaczej, daj mi znać, co zdecydujesz.

Ahodder
źródło
1

Zakładając, że wywołujesz działanie dwa z działania jeden za pomocą zamiaru.
Możesz przekazać dane za pomocą intent.putExtra (),

Weź to w celach informacyjnych. Wysyłanie tablic za pomocą Intent.putExtra

Mam nadzieję, że tego właśnie chcesz.

Manish Khot
źródło
1

Jeśli zamierzasz wywoływać inne działania z bieżącej działalności, powinieneś użyć intencji . Możesz skupić się mniej na utrwalaniu danych niż na udostępnianiu ich w razie potrzeby.

Jeśli jednak naprawdę chcesz zachować te wartości, możesz utrwalić je w jakimś strukturalnym pliku tekstowym lub bazie danych w pamięci lokalnej. Plik właściwości, plik XML lub plik JSON może przechowywać dane i być łatwo analizowany podczas tworzenia działania. Nie zapomnij również, że masz SQLite na wszystkich urządzeniach z Androidem, więc możesz przechowywać je w tabeli bazy danych. Możesz także użyć mapy do przechowywania par klucz-wartość i serializacji mapy do pamięci lokalnej, ale może to być zbyt skomplikowane, aby było przydatne w przypadku prostych struktur danych.

Mike Yockey
źródło
1

Wszystkie powyższe odpowiedzi są świetne ... Właśnie dodam jedną, o której nikt jeszcze nie wspominał o utrwalaniu danych poprzez działania, a mianowicie użycie wbudowanej bazy danych SQLite dla Androida, aby zachować odpowiednie dane ... W rzeczywistości możesz umieścić swoją databaseHelper w stanie aplikacji i wywołuj go w razie potrzeby podczas aktywacji. Lub po prostu stwórz klasę pomocnika i wykonaj wywołania DB w razie potrzeby ... Wystarczy dodać kolejną warstwę do rozważenia ... Ale wszystkie pozostałe odpowiedzi byłyby wystarczające również .. Naprawdę tylko preferencje

awatara
źródło
1

Udostępnianie danych między działaniami, na przykład przekazywanie wiadomości e-mail po zalogowaniu

„e-mail” to nazwa, za pomocą której można odwoływać się do wartości żądanego działania

1 Kod na stronie logowania

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

2 kod na stronie głównej

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");
Jeff
źródło
1

A jeśli chcesz pracować z obiektem danych, te dwie implementacje są bardzo ważne:

Serializable vs Parcelable

  • Serializable to interfejs znaczników, co oznacza, że ​​użytkownik nie może zebrać danych zgodnie z ich wymaganiami. Więc kiedy obiekt implementuje Serializable Java, automatycznie go serializuje.
  • Parcelable to własny protokół serializacji Androida. W Parcelable programiści piszą niestandardowy kod do zestawiania i rozpakowywania. Tworzy więc mniej obiektów śmieci w porównaniu do serializacji
  • Wydajność Parcelable jest bardzo wysoka w porównaniu do Serializable ze względu na jego niestandardową implementację. Zaleca się stosowanie implantacji Parcelable podczas szeregowania obiektów w systemie Android.

public class User implements Parcelable

sprawdź więcej tutaj

Diego Venâncio
źródło