Qual é a melhor maneira de compartilhar dados entre atividades?


239

Eu tenho uma atividade que é a principal atividade usada em todo o aplicativo e possui várias variáveis. Eu tenho duas outras atividades que eu gostaria de poder usar os dados da primeira atividade. Agora eu sei que posso fazer algo assim:

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

No entanto, quero compartilhar muitas variáveis ​​e algumas podem ser bastante grandes, por isso não quero criar cópias delas como acima.

Existe uma maneira de obter e alterar diretamente as variáveis ​​sem usar os métodos get e set? Lembro-me de ler um artigo no site de desenvolvimento do Google dizendo que isso não é recomendado para o desempenho no Android.


2
A partir do Android 2.3 (Gingerbread), a otimização de get / set é realizada automaticamente pela Dalvik; isso é relevante apenas se você estiver segmentando versões mais antigas do Android.
StellarVortex

Observe que o exemplo não copia os dados da sequência. Em vez disso, ele cria uma referência ao mesmo objeto de string.
Code-Apprentice

É difícil de acreditar, por que não há possibilidade de iniciar uma atividade de outra atividade e passar qualquer objeto complexo da primeira para a segunda? Sem serialização, salvando o objeto e todo esse esforço. Isso é uma falha de segurança ou que outro motivo é simplesmente passar a referência do objeto se as duas atividades estiverem no mesmo aplicativo? (Eu entendo que é diferente se eles estão em diferentes apps)
Droidum


LiveData é a melhor e mais recente solução. Verifique minha resposta abaixo.
Amir Uval

Respostas:


476

Aqui está uma compilação das maneiras mais comuns de conseguir isso :

  • Enviar dados dentro da intenção
  • Campos estáticos
  • HashMap de WeakReferences
  • Persistir objetos (sqlite, compartilhar preferências, arquivo etc.)

TL; DR : existem duas maneiras de compartilhar dados: passar dados nos extras da intenção ou salvá-los em outro lugar. Se os dados forem primitivos, Strings ou objetos definidos pelo usuário: envie-os como parte dos extras de intenção (os objetos definidos pelo usuário devem ser implementados Parcelable). Se passar objetos complexos, salve uma instância em um singleton em outro lugar e acesse-os a partir da atividade iniciada.

Alguns exemplos de como e por que implementar cada abordagem:

Enviar dados dentro das intenções

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

Na segunda atividade:

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

Use este método se estiver passando dados primitivos ou Strings . Você também pode passar objetos que implementam Serializable.

Embora tentador, você deve pensar duas vezes antes de usar Serializable: é propenso a erros e terrivelmente lento. Então, em geral: fique longe,Serializable se possível. Se você deseja transmitir objetos complexos definidos pelo usuário, dê uma olhada na Parcelableinterface . É mais difícil de implementar, mas possui ganhos de velocidade consideráveis ​​em comparação com Serializable.

Compartilhe dados sem persistir no disco

É possível compartilhar dados entre as atividades salvando-os na memória, pois, na maioria dos casos, as duas atividades são executadas no mesmo processo.

Nota: às vezes, quando o usuário sai de sua atividade (sem sair dela), o Android pode decidir matar seu aplicativo. Nesse cenário, experimentei casos em que o Android tenta iniciar a última atividade usando a intenção fornecida antes da morte do aplicativo. Nesses casos, os dados armazenados em um singleton (seu ou Application) desaparecerão e poderão ocorrer coisas ruins. Para evitar esses casos, você persiste objetos no disco ou verifica os dados antes de usá-los para garantir sua validade.

Use uma classe singleton

Tenha uma classe para armazenar os dados:

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

Da atividade lançada:

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

Usar aplicativo singleton

O aplicativo singleton é uma instância android.app.Applicationcriada quando o aplicativo é iniciado. Você pode fornecer um personalizado estendendo 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;}
}

Antes de iniciar a atividade:

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

Em seguida, a partir da atividade lançada:

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

Campos estáticos

A ideia é basicamente a mesma que o singleton, mas nesse caso você fornece acesso estático aos dados:

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

Da atividade lançada:

String data = DataHolder.getData();

HashMap de WeakReferences

A mesma ideia, mas permitindo que o coletor de lixo remova objetos não referenciados (por exemplo, quando o usuário sai da atividade):

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

Antes de iniciar a atividade:

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

Da atividade lançada:

DataHolder.getInstance().retrieve(someId);

Você pode ou não ter que passar a identificação do objeto usando os extras da intenção. Tudo depende do seu problema específico.

Persistir objetos no disco

A idéia é salvar os dados no disco antes de iniciar a outra atividade.

Vantagens: você pode iniciar a atividade de outros lugares e, se os dados já persistirem, devem funcionar bem.

Desvantagens: é complicado e leva mais tempo para ser implementado. Requer mais código e, portanto, mais chance de introduzir bugs. Também será muito mais lento.

Algumas das maneiras de persistir objetos incluem:


11
Eu argumentaria que esse não é o caminho "normal" para dados maiores / mais complexos. É muito mais fácil usar um singleton estático ou o objeto Application e funciona muito bem. Agora que o OP disse que usou uma String no exemplo, por isso, Intent é perfeito e preferido.
Charlie Collins

10
Serializable descobriu ter sérios problemas de desempenho no modelo de processo do Android. É por isso que eles introduziram o Parcelable. Leia Parcelável em vez de Serializável na resposta acima.
Subin Sebastian

3
Isso é feito através do setResultmétodo Além disso, nesse caso, a atividade secundária deve ser chamada usando o startActivityForResultmétodo
Cristian

2
Ótimo resumo! Com relação ao problema de destruição de singletons, existe uma solução simples para aplicativos com muitas atividades e objetos: Use uma subclasse de Activitytodo e, em sua onCreate()verificação, qualquer campo estático do singleton que você preenche ao iniciar o aplicativo. Se esse campo estiver vazio, volte à atividade inicial usando FLAG_ACTIVITY_CLEAR_TASKou a BroadcastReceiverpara eliminar outras atividades.
Janosch

1
Eu não recomendaria salvar dados na classe do aplicativo. Leia mais aqui: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

O que você pode usar:

  1. passando dados entre atividades (como disse Cristian)
  2. usando uma classe com muitas variáveis ​​estáticas (para que você possa chamá-las sem uma instância da classe e sem usar getter / setter)
  3. Usando um banco de dados
  4. Preferências Compartilhadas

O que você escolhe depende de suas necessidades. Provavelmente você usará mais de uma maneira quando tiver "muitas"


1
Por favor note que a estática são apuradas em processo morte
EpicPandaForce

@EpicPandaForce, é claro, e também quando o dispositivo foi desligado.
WarrenFaith

1
Mas se o dispositivo estiver desligado, o aplicativo reiniciará da MAINação. Após a morte do processo, você reinicia qualquer atividade aberta pela última vez, que pode ser uma página de detalhes em algum lugar profundo do aplicativo.
EpicPandaForce

@EpicPandaForce parece que você perdeu minha ironia, mesmo que você seja Cpt Obvious.
WarrenFaith

16

Faça o que o google ordena que você faça! aqui: http://developer.android.com/resources/faq/framework.html#3

  • Tipos de dados primitivos
  • Objetos não persistentes
  • Classe Singleton - o meu favorito: D
  • Um campo / método estático público
  • Um HashMap de WeakReferences to Objects
  • Objetos Persistentes (Preferências de Aplicativos, Arquivos, ContentProviders, SQLite DB)

1
Google Fazer a ligação para objetos persistentes: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk

Apenas antipadrões, a classe Singleton é o primeiro padrão de design, uma classe com campo / método estático se comporta muito como singletons e criar banco de dados para objetos persistentes de busniss às ​​vezes não é uma coisa boa, claro que não é sua culpa e você está listando possível maneiras de consegui-lo, mas eu me pergunto por que o Google complicou uma coisa tão estúpida, problemas de desempenho ou o quê ?? !!!! ou eu não estou entendendo a maneira Android ?? !!!!
La VloZ Merrill

link está quebrado :(
Shayan_Aryan 2/16

14

"No entanto, quero compartilhar muitas variáveis ​​e algumas podem ser bastante grandes, por isso não quero criar cópias delas como acima."

Isso não faz uma cópia (especialmente com String , mas mesmo os objetos são passados ​​pelo valor da referência, não o próprio objeto, e os getter são bons de usar - sem dúvida melhor do que outros meios, porque são comuns e bem entendido). Os "mitos de desempenho" mais antigos, como não usar getters e setters, ainda têm algum valor, mas também foram atualizados nos documentos .

Mas se você não quiser fazer isso, também poderá tornar as variáveis ​​públicas ou protegidas no GlobalState e acessá-las diretamente. E você pode criar um singleton estático como o objeto Aplicativo JavaDoc indica :

Normalmente, não há necessidade de subclassificar Application. Na maioria das situações, singletons estáticos podem fornecer a mesma funcionalidade de uma maneira mais modular. Se o seu singleton precisar de um contexto global (por exemplo, para registrar receptores de transmissão), a função para recuperá-lo poderá receber um Contexto que use internamente Context.getApplicationContext () ao construir o singleton pela primeira vez.

O uso de dados de intenção , como outras respostas aqui indicadas, é outra maneira de transmitir dados, mas geralmente é usado para dados menores e tipos simples. Você pode transmitir dados maiores / mais complexos, mas é mais envolvido do que apenas usar um único estático. O objeto Application ainda é o meu favorito para compartilhar dados não persistentes maiores / mais complexos entre os componentes de aplicativos Android (porque possui um ciclo de vida bem definido em um aplicativo Android).

Além disso, como outros observaram, se os dados ficarem muito complexos e precisarem ser persistentes , você poderá usar o SQLite ou o sistema de arquivos também.


1
Na verdade, eu deparei com isso recentemente nos documentos: developer.android.com/guide/appendix/faq/framework.html#3 . Para "objetos complexos não persistentes", recomenda-se o uso da classe Application para criar e destruir um singleton estático! Dessa forma, você obtém o ciclo de vida bem definido que o Aplicativo fornece e a facilidade de uso de um singleton estático.
Charlie Collins

2
essa seção do faq parece ter sido removida agora (só vejo "objetos não persistentes" e nenhuma menção à classe Application). Enfim, você pode elaborar?
Tony Chan

1
Atualmente, ele está em developer.android.com/guide/faq/framework.html#3 "Como transmito dados entre Atividades / Serviços em um único aplicativo?", E nenhuma menção à classe Aplicativo.
Jerry101

Gostaria de usar um objeto Aplicativo, seguindo o precedente que você definiu em "Android em Ação". Mas muitos empregadores em potencial não gostam quando vêem isso em desafios de código. Eles podem estar errados, mas jogam em torno de seu peso. BTW: esse link 'framework.html # 3 não funciona mais.
Matt J.


4

Existe uma maneira nova e melhor de compartilhar dados entre atividades, e é o LiveData . Observe em particular esta citação na página do desenvolvedor do Android:

O fato de os objetos do LiveData reconhecerem o ciclo de vida significa que você pode compartilhá-los entre várias atividades, fragmentos e serviços. Para manter o exemplo simples, você pode implementar a classe LiveData como um singleton

A implicação disso é enorme - qualquer dado de modelo pode ser compartilhado em uma classe singleton comum dentro de um LiveDatawrapper. Ele pode ser injetado das atividades nos respectivos ViewModelpara fins de testabilidade. E você não precisa mais se preocupar com referências fracas para evitar vazamentos de memória.


2

Usando o hashmap da abordagem de referência fraca, descrito acima, e em http://developer.android.com/guide/faq/framework.html parece problemático para mim. Como as entradas inteiras são recuperadas, não apenas o valor do mapa? Em que escopo você o aloca? Como a estrutura está no controle do ciclo de vida da atividade, ter uma das atividades participantes próprias corre o risco de erros de tempo de execução quando o proprietário é destruído antes de seus clientes. Se o Aplicativo for o proprietário, alguma Atividade deverá remover explicitamente a entrada para evitar que o mapa de hash se segure nas entradas com uma chave válida e uma referência fraca coletada potencialmente prejudicada. Além disso, o que um cliente deve fazer quando o valor retornado para uma chave é nulo?

Parece-me que um WeakHashMap de propriedade do Aplicativo ou dentro de um singleton é uma escolha melhor. Um valor no mapa é acessado por meio de um objeto-chave e, quando não existem referências fortes à chave (ou seja, todas as atividades são feitas com a chave e para o que ele mapeia), o GC pode recuperar a entrada do mapa.


2

Existem várias maneiras de compartilhar dados entre atividades

1: Passando dados entre atividades usando Intent

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

2: Usando a palavra-chave estática, defina a variável como estática pública e use qualquer parte do projeto

      public static int sInitialValue=0;

use em qualquer lugar do projeto usando classname.variableName;

3: Usando banco de dados

mas seu processo demorado, você deve usar a consulta para inserir dados e iterar os dados usando o cursor quando necessário. Mas não há chance de perder dados sem limpar o cache.

4: Usando preferências compartilhadas

muito mais fácil que o banco de dados. mas há algumas limitações que você não pode salvar objetos ArrayList, List e custome.

5: Crie o getter setter na classe Aplication e acesse qualquer local do projeto.

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

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

aqui definir e obter de atividades

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

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

1

Bem, eu tenho algumas idéias, mas não sei se elas são o que você está procurando.

Você pode usar um serviço que armazena todos os dados e depois vincular suas atividades ao serviço para recuperação de dados.

Ou empacote seus dados em um serializável ou parcelável e anexe-os a um pacote configurável e passe o pacote entre atividades.

Esse pode não ser o que você está procurando, mas você também pode tentar usar Preferências compartilhadas ou uma preferência em geral.

De qualquer maneira, deixe-me saber o que você decide.



1

Se sua intenção é chamar outras atividades da atividade atual, você deve usar intenções . Seu foco pode ser menos em dados persistentes do que em compartilhá-los conforme a necessidade.

No entanto, se você realmente precisar persistir com esses valores, poderá persisti-los em algum tipo de arquivo de texto estruturado ou banco de dados no armazenamento local. Um arquivo de propriedades, arquivo XML ou arquivo JSON pode armazenar seus dados e ser facilmente analisados ​​durante a criação da atividade. Não esqueça também que você possui SQLite em todos os dispositivos Android, para armazená-los em uma tabela de banco de dados. Você também pode usar um mapa para armazenar pares de valores-chave e serializar o mapa para armazenamento local, mas isso pode ser muito complicado para ser útil para estruturas de dados simples.


1

Todas as respostas acima mencionadas são ótimas ... Estou apenas adicionando uma que ninguém mencionou ainda sobre persistência de dados por meio de atividades e que é usar o banco de dados SQLite Android embutido para persistir dados relevantes ... Na verdade, você pode colocar seu databaseHelper no estado do aplicativo e chame-o conforme necessário durante as atividades ativadas .. Ou apenas faça uma classe auxiliar e faça as chamadas ao banco de dados quando necessário ... Apenas adicionando outra camada para você considerar ... Mas todas as outras respostas seriam suficientes bem .. realmente apenas preferência


1

Exemplo de compartilhamento de dados entre atividades, passando um email após o login

"email" é o nome que pode ser usado para referenciar o valor da atividade que está sendo solicitada

1 Código na página de login

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

2 código na página inicial

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

1

E se você deseja trabalhar com objeto de dados, esses dois implementam muito importantes:

Serializable vs Parcelable

  • Serializable é uma interface de marcador, o que implica que o usuário não pode organizar os dados de acordo com seus requisitos. Portanto, quando o objeto implementa o Java serializável, ele será serializado automaticamente.
  • Parcelable é o próprio protocolo de serialização do Android. No Parcelable, os desenvolvedores escrevem código personalizado para empacotamento e descompactação. Portanto, ele cria menos objetos de lixo em comparação com a serialização
  • O desempenho do Parcelable é muito alto quando comparado ao Serializable devido à sua implementação personalizada. É altamente recomendável usar a implantação do Parcelable ao serializar objetos no Android.

public class User implements Parcelable

confira mais aqui

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.