Como declarar variáveis ​​globais no Android?


595

Estou criando um aplicativo que requer login. Eu criei a principal e a atividade de login.

No principal onCreatemétodo de atividade , adicionei a seguinte condição:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

O onActivityResultmétodo que é executado quando o formulário de login é finalizado se parece com o seguinte:

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

O problema é que o formulário de login às vezes aparece duas vezes (o login()método é chamado duas vezes) e também quando o teclado do telefone desliza, o formulário de login aparece novamente e acho que o problema é a variável strSessionString.

Alguém sabe como definir a variável global para evitar que o formulário de login apareça após o usuário já se autenticar com êxito?


um bom tutorial sobre como lidar com um estado de atividade usando salvou instância pacote estado quicktips.in/...
Deepak Swami

Respostas:


954

Eu escrevi essa resposta em '09, quando o Android era relativamente novo, e havia muitas áreas não bem estabelecidas no desenvolvimento do Android. Eu adicionei um longo adendo no final deste post, abordando algumas críticas e detalhando um desacordo filosófico que tenho com o uso de Singletons em vez de subclassificar Application. Leia por sua conta e risco.

RESPOSTA ORIGINAL:

O problema mais geral que você está enfrentando é como salvar o estado em várias Atividades e em todas as partes do seu aplicativo. Uma variável estática (por exemplo, um singleton) é uma maneira Java comum de conseguir isso. Descobri, no entanto, que uma maneira mais elegante no Android é associar seu estado ao contexto do aplicativo.

Como você sabe, cada Atividade também é um Contexto, que são informações sobre seu ambiente de execução no sentido mais amplo. Seu aplicativo também possui um contexto, e o Android garante que ele existirá como uma instância única no aplicativo.

A maneira de fazer isso é criar sua própria subclasse de android.app.Application e especificar essa classe na tag do aplicativo em seu manifesto. Agora, o Android criará automaticamente uma instância dessa classe e a disponibilizará para todo o aplicativo. Você pode acessá-lo contextusando qualquer Context.getApplicationContext()método ( Activitytambém fornece um método getApplication()que tem exatamente o mesmo efeito). A seguir, é apresentado um exemplo extremamente simplificado, com ressalvas a seguir:

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

Isso tem essencialmente o mesmo efeito que o uso de uma variável estática ou singleton, mas integra-se muito bem à estrutura Android existente. Observe que isso não funcionará entre processos (seu aplicativo deve ser um dos raros que possui vários processos).

Algo a ser observado no exemplo acima; suponha que tivéssemos feito algo como:

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

Agora, essa inicialização lenta (como bater no disco, bater na rede, bloquear qualquer coisa etc.) será executada toda vez que o Aplicativo for instanciado! Você pode pensar, bem, isso é apenas uma vez para o processo e eu vou ter que pagar o custo de qualquer maneira, certo? Por exemplo, como Dianne Hackborn menciona abaixo, é perfeitamente possível que seu processo seja instanciado - apenas - para manipular um evento de transmissão em segundo plano. Se o seu processamento de transmissão não precisar desse estado, você potencialmente acabou de executar uma série de operações complicadas e lentas por nada. Instanciação preguiçosa é o nome do jogo aqui. A seguir, é uma maneira um pouco mais complicada de usar o Aplicativo, que faz mais sentido para qualquer coisa, exceto o mais simples dos usos:

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

Embora eu prefira a subclassificação de aplicativos a usar singletons aqui como a solução mais elegante, prefiro que os desenvolvedores usem singletons, se realmente necessário, em vez de não pensarem nas implicações de desempenho e multithreading da associação de estado à subclasse de aplicativos.

NOTA 1: Também conforme comentado pelo anticafe, para vincular corretamente a substituição do seu aplicativo ao seu aplicativo, é necessária uma marca no arquivo de manifesto. Mais uma vez, consulte os documentos do Android para mais informações. Um exemplo:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

NOTA 2: O usuário608578 pergunta abaixo como isso funciona com o gerenciamento dos ciclos de vida do objeto nativo. Não tenho a menor velocidade para usar o código nativo com o Android nem um pouco e não estou qualificado para responder como isso iria interagir com a minha solução. Se alguém tiver uma resposta para isso, estou disposto a creditá-lo e colocar as informações neste post para obter visibilidade máxima.

TERMO ADITIVO:

Como algumas pessoas observaram, essa não é uma solução para o estado persistente , algo que talvez eu devesse ter enfatizado mais na resposta original. Ou seja, isso não pretende ser uma solução para salvar o usuário ou outras informações que devem persistir durante a vida útil do aplicativo. Portanto, considero a maioria das críticas abaixo relacionadas a aplicativos sendo mortos a qualquer momento, etc ..., discutíveis, pois tudo o que for necessário persistir no disco não deve ser armazenado por meio de uma subclasse de aplicativo. Ele deve ser uma solução para armazenar temporariamente, o estado de aplicativo recriável facilmente (se um usuário está conectado, por exemplo) e componentes que são de instância única (gerente de rede de aplicativos, por exemplo) ( NÃO singleton!) Na natureza.

Dayerman teve a gentileza de apontar uma conversa interessante com Reto Meier e Dianne Hackborn, na qual o uso de subclasses de aplicativos é desencorajado em favor dos padrões de Singleton. Somatik também apontou algo dessa natureza anteriormente, embora eu não o visse na época. Devido aos papéis de Reto e Dianne na manutenção da plataforma Android, não posso de boa fé recomendar ignorar seus conselhos. O que eles dizem, vai. Desejo discordar das opiniões expressas em relação à preferência das subclasses de Singleton em vez de Application. Na minha discordância, usarei os conceitos melhor explicados nesta explicação do StackExchange do padrão de design Singleton, para que eu não precise definir termos nesta resposta. É altamente recomendável que você escaneie o link antes de continuar. Ponto por ponto:

Dianne declara: "Não há motivo para subclassificar do Aplicativo. Não é diferente de criar um singleton ..." Essa primeira afirmação está incorreta. Existem duas razões principais para isso. 1) A classe Application fornece uma garantia vitalícia melhor para um desenvolvedor de aplicativos; é garantido que tenha a vida útil do aplicativo. Um singleton não está EXPLICITAMENTE vinculado ao tempo de vida do aplicativo (embora seja efetivamente). Isso pode não ser um problema para o desenvolvedor de aplicativos comum, mas eu diria que esse é exatamente o tipo de contrato que a API do Android deve oferecer, além de oferecer muito mais flexibilidade ao sistema Android, minimizando a vida útil dos aplicativos associados. dados. 2) A classe Application fornece ao desenvolvedor do aplicativo um único detentor de instância para state, que é muito diferente de um detentor de Estado Singleton. Para obter uma lista das diferenças, consulte o link de explicação Singleton acima.

Dianne continua: "... provavelmente será algo de que você se arrependerá no futuro, ao encontrar o objeto Application se tornando uma grande bagunça do que deveria ser uma lógica de aplicativo independente". Certamente isso não está incorreto, mas esse não é um motivo para escolher o Singleton sobre a subclasse Aplicativo. Nenhum dos argumentos de Diane fornece uma razão para que o uso de um Singleton seja melhor que uma subclasse Application, tudo o que ela tenta estabelecer é que o uso de um Singleton não é pior que uma subclasse Application, que acredito ser falsa.

Ela continua: "E isso leva mais naturalmente a como você deve gerenciar essas coisas - inicializando-as sob demanda". Isso ignora o fato de que não há motivo para que você não possa inicializar sob demanda usando também uma subclasse Application. Novamente, não há diferença.

Dianne termina com "A própria estrutura possui toneladas e toneladas de singletons para todos os poucos dados compartilhados que mantém para o aplicativo, como caches de recursos carregados, conjuntos de objetos etc. Ele funciona muito bem". Não estou argumentando que o uso de Singletons não funcione bem ou não seja uma alternativa legítima. Estou argumentando que os Singletons não fornecem um contrato tão forte com o sistema Android quanto o uso de uma subclasse Application, e além disso, o uso de Singletons geralmente aponta para um design inflexível, que não é facilmente modificado e leva a muitos problemas no caminho. IMHO, o forte contrato que a API Android oferece aos aplicativos de desenvolvedor é um dos aspectos mais atraentes e agradáveis ​​da programação com o Android, e ajudou a levar à adoção antecipada do desenvolvedor, o que levou a plataforma Android ao sucesso que ela tem hoje.

Dianne também comentou abaixo, mencionando uma desvantagem adicional no uso de subclasses de Aplicativo, eles podem incentivar ou facilitar a gravação de menos código de desempenho. Isso é muito verdadeiro e editei esta resposta para enfatizar a importância de considerar o perf aqui e adotar a abordagem correta se você estiver usando a subclassificação de aplicativos. Como Dianne afirma, é importante lembrar que sua classe Application será instanciada toda vez que seu processo for carregado (pode ser várias vezes ao mesmo tempo se seu aplicativo for executado em vários processos!), Mesmo que o processo esteja sendo carregado apenas para uma transmissão em segundo plano evento. Portanto, é importante usar a classe Application mais como um repositório para ponteiros para componentes compartilhados do seu aplicativo, e não como um local para qualquer processamento!

Deixo você com a seguinte lista de desvantagens para Singletons, roubadas do link anterior do StackExchange:

  • Incapacidade de usar classes abstratas ou de interface;
  • Incapacidade de subclasse;
  • Alto acoplamento em todo o aplicativo (difícil de modificar);
  • Difícil de testar (não pode falsificar / zombar em testes de unidade);
  • Difícil de paralelizar no caso de estado mutável (requer travamento extenso);

e adicione o meu:

  • Contrato vitalício incerto e incontrolável, inadequado para o desenvolvimento do Android (ou da maioria dos outros);

93
Obrigado Soonil - esse tipo de resposta é a razão pela qual eu amo tanto o Stack Overflow. BOM TRABALHO!
JohnnyLambada

5
Para quem quer saber como "especificar essa classe na tag do aplicativo no seu manifesto", há, até o momento, duas outras respostas para essa pergunta que descrevem como fazê-lo (use android: name), uma por ebuprofen e uma por Mike Brown.
Tyler Collier

9
Em breve, sua resposta está correta, mas você pode notar que devemos adicionar <application android: name = ". MyApp" ... /> no arquivo Android Manifest?
Anticafe

12
Deixe-me repetir mais uma vez, você não deve usar o Application for global. É inútil, não oferece benefícios sobre os singletons e pode ser ativamente prejudicial, como prejudicar o desempenho do lançamento do processo. No momento em que o Aplicativo está sendo criado, você não tem idéia para o que seu processo está sendo criado. Inicializando preguiçosamente os singletons conforme necessário, você só precisa fazer o trabalho necessário. Por exemplo, se seu processo estiver sendo iniciado para manipular uma transmissão sobre algum evento em segundo plano, não há motivo para inicializar qualquer estado global necessário à sua interface do usuário.
hackbod

14
Além disso, vamos ser bem claros aqui - todos os seus argumentos contra singletons são perfeitamente válidos, quando estamos falando de situações em que você está realmente escolhendo entre um singleton e outra abordagem que não é global; os singletons são globais, com todas as advertências sobre os globais aplicáveis. No entanto, o aplicativo também é um singleton . Você não está escapando desses problemas alternando para a subclasse de Aplicativo, um Aplicativo é exatamente o mesmo que um singleton (mas pior), apenas permitindo que você se engane ao fazer algo mais limpo. Mas você não é.
hackbod

153

Crie esta subclasse

public class MyApp extends Application {
  String foo;
}

No AndroidManifest.xml, adicione android: name

Exemplo

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

1
obrigado por isso. Eu queria saber como declará-lo no manifesto
Someone Somewhere

3
Para que funcionasse para mim, tive que remover o "." dentro de ".MyApp"
Alguém em algum lugar

3
apenas declará-la após a atividade principal, caso contrário, não poderá instalar / implantar
sami

11
só quero dizer, isso está na tag do aplicativo PRINCIPAL que já está lá ... essa não é a segunda :) teve que aprender da maneira mais difícil.
bwoogie

java.lang.IllegalAccessException: access to class is not allowed
Raptor

142

A maneira sugerida por Soonil de manter um estado para o aplicativo é boa, porém tem um ponto fraco - há casos em que o SO mata todo o processo do aplicativo. Aqui está a documentação sobre isso - Processos e ciclos de vida .

Considere um caso: seu aplicativo entra em segundo plano porque alguém está ligando para você (o aplicativo Phone está em primeiro plano agora). Nesse caso, && sob algumas outras condições (verifique o link acima para saber o que poderiam ser), o sistema operacional pode interromper o processo de inscrição, incluindo a Applicationinstância da subclasse. Como resultado, o estado está perdido. Quando você retornar posteriormente ao aplicativo, o sistema operacional restaurará sua pilha de atividades e a Applicationinstância da subclasse, independentemente do myStatecampo null.

No AFAIK, a única maneira de garantir a segurança do estado é usar qualquer tipo de persistência do estado, por exemplo, usar um privado para o arquivo do aplicativo ou SharedPrefernces(eventualmente ele usa um privado para o arquivo do aplicativo no sistema de arquivos interno).


10
+1 para persistir com SharedPreferences; é assim que eu tenho visto isso. Acho estranho abusar do sistema de preferências para o estado salvo, mas funciona tão bem que o problema se torna apenas uma questão de terminologia.
Cheezmeister 22/02

1
você poderia postar o código (ou fornecer um link para uma explicação) sobre como o SharedPreferences é usado para resolver o problema descrito pela Arhimed
Alguém em algum lugar

2
Preferências, banco de dados, serialização de arquivos etc. Cada atividade pode manter o estado se eles usarem o onSaveInstanceState, mas não ajudará se o usuário sair da atividade e removê-la da pilha de históricos, forçar o fechamento ou desligar o dispositivo .
Darren Hinderer

1
Esse comportamento é muito irritante - não seria tão ruim se o método onTerminate () do seu aplicativo fosse chamado para que você pudesse lidar com a situação com elegância.
Dean Wild

2
Esta é a resposta correta na minha opinião. É um erro confiar na mesma instância de aplicativo existente entre as atividades. Na minha experiência, é bastante comum que o Android desmonte completamente e recrie todo o processo enquanto você estiver em segundo plano. Estar em segundo plano pode significar apenas o lançamento de uma intenção de câmera, o objetivo do navegador ou o recebimento de uma ligação.
amigos estão dizendo

26

Apenas uma nota ..

adicionar:

android:name=".Globals"

ou o que você nomeou sua subclasse para a tag existente <application> . Continuei tentando adicionar outra <application>tag ao manifesto e receberia uma exceção.


Olá Gimbl. Eu tive o mesmo problema. Eu também tinha minha própria tag <application> e, ao tentar adicionar outra tag <application>, tive o mesmo problema que você (mensagem de exceção). Mas fiz o que você mencionou e não funcionou. Eu adiciono android: name = ". GlobalClass" à minha tag <application>, mas não funciona. Você pode explicar completamente como você resolveu isso?
Sonhja 23/09/11

3
Bom <manifesto> <aplicativo android: name = ". GlobalData"> </application> </manifest>. Bad <manifest> <application> </ application> <Aplicativo para Android: name = "GlobalData."> </ Application> </ manifest>
Gimbl

13

Também não consegui encontrar como especificar a tag do aplicativo, mas depois de pesquisar bastante no Google, ficou óbvio a partir dos documentos do arquivo de manifesto: use android: name, além do ícone e do rótulo padrão na sub-rotina do aplicativo.

android: name O nome completo de uma subclasse Aplicativo implementada para o aplicativo. Quando o processo do aplicativo é iniciado, essa classe é instanciada antes de qualquer um dos componentes do aplicativo.

A subclasse é opcional; a maioria dos aplicativos não precisará de um. Na ausência de uma subclasse, o Android usa uma instância da classe Application básica.


13

Que tal garantir a coleta de memória nativa com essas estruturas globais?

As atividades têm um onPause/onDestroy()método chamado destruição, mas a classe Application não possui equivalentes. Que mecanismo é recomendado para garantir que as estruturas globais (especialmente aquelas que contêm referências à memória nativa) sejam coletadas de maneira apropriada quando o aplicativo é morto ou a pilha de tarefas é colocada em segundo plano?


1
A solução óbvia é implementar a interface Closeable para seus objetos responsáveis ​​por recursos nativos e garantir que eles sejam gerenciados por uma instrução try-with-resources ou qualquer outra coisa. Na pior das hipóteses, você sempre pode usar um finalizador de objeto.
sooniln

5

Você só precisa definir um nome de aplicativo como abaixo, que funcionará:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

4

Como foi discutido acima, o SO pode matar o APPLICATION sem nenhuma notificação (não há evento onDestroy), portanto não há como salvar essas variáveis ​​globais.

SharedPreferences poderia ser uma solução, EXCETO você tem variáveis ​​COMPLEX STRUCTURED (no meu caso, eu tinha um array inteiro para armazenar os IDs que o usuário já manipulou). O problema com as SharedPreferences é que é difícil armazenar e recuperar essas estruturas sempre que os valores forem necessários.

No meu caso, eu tinha um SERVICE em segundo plano para poder mover essas variáveis ​​para lá e, como o serviço tem um evento onDestroy, eu poderia salvar esses valores facilmente.


Não é garantido que onDestroy () seja chamado mesmo para um serviço.
Aprenda OpenGL ES

Sim, isso pode acontecer, mas apenas em caso de situações críticas.
Adorjan Princz

4

Se algumas variáveis ​​estiverem armazenadas no sqlite e você deve usá-las na maioria das atividades em seu aplicativo. então Application talvez seja a melhor maneira de alcançá-lo. Consultar as variáveis ​​do banco de dados quando o aplicativo foi iniciado e armazená-las em um campo. Então você pode usar essas variáveis ​​em suas atividades.

Portanto, encontre o caminho certo e não há melhor caminho.


3

Você pode ter um campo estático para armazenar esse tipo de estado. Ou coloque-o no recurso Bundle e restaure a partir daí em onCreate (Bundle savedInstanceState). Apenas certifique-se de entender completamente o ciclo de vida gerenciado do aplicativo Android (por exemplo, por que o login () é chamado na alteração da orientação do teclado).


2

NÃO use outra <application>tag no arquivo de manifesto. Basta fazer uma alteração na <application>tag existente ; adicione esta linha android:name=".ApplicationName"em ApplicationNameque será o nome da sua subclasse (use para armazenar global) que você está prestes a criar.

então, finalmente, sua tag ONE AND ONLY <application> no arquivo de manifesto deve ficar assim: -

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

1

você pode usar as intenções, o sqlite ou as preferências compartilhadas. Quando se trata do armazenamento de mídia, como documentos, fotos e vídeos, você pode criar os novos arquivos.


1

Você pode fazer isso usando duas abordagens:

  1. Usando a classe Application
  2. Usando preferências compartilhadas

  3. Usando a classe Application

Exemplo:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

Você pode usar a classe acima para implementar o logon em sua MainActivity como abaixo. O código será mais ou menos assim:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

Este método funcionará para armazenamento temporário. Você realmente não tem idéia de quando o sistema operacional vai matar o aplicativo, por causa da pouca memória. Quando seu aplicativo está em segundo plano e o usuário está navegando em outro aplicativo que exige mais memória para ser executado, seu aplicativo será eliminado, pois o sistema operacional deu mais prioridade aos processos em primeiro plano do que em segundo plano. Portanto, seu objeto de aplicativo será nulo antes que o usuário efetue logout. Por isso, recomendo usar o segundo método especificado acima.

  1. Usando preferências compartilhadas.

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");

0

Na atividade, o resultado é chamado antes no resumo. Portanto, mova sua verificação de login para continuar e seu segundo login poderá ser bloqueado quando a atividade secomd retornar um resultado positivo. No currículo é chamado toda vez, então não há preocupações de não ser chamado pela primeira vez.


0

A abordagem da subclasse também foi usada pela estrutura BARACUS. Do meu ponto de vista, a subclasse de aplicativos destinava-se a trabalhar com os ciclos de vida do Android; é isso que qualquer contêiner de aplicativos faz. Em vez de ter globais, registro os beans nesse contexto e os deixo sendo injetados em qualquer classe gerenciável pelo contexto. Toda instância de bean injetado é realmente um singleton.

Veja este exemplo para detalhes

Por que o trabalho manual se você pode ter muito mais?


0
class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

@Override
public void onCreate(Bundle b){
    ...
    GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
    String state = appState.getGlobalState();
    ...
    }
}

0

Você pode criar uma classe que estenda a Applicationclasse e, em seguida, declarar sua variável como um campo dessa classe e fornecer o método getter para ela.

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

E então, para acessar essa variável em sua Atividade, use o seguinte:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
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.