Qual é a diferença entre os vários métodos para obter um contexto?

390

Em vários bits do código Android, eu já vi:

 public class MyActivity extends Activity {
    public void method() {
       mContext = this;    // since Activity extends Context
       mContext = getApplicationContext();
       mContext = getBaseContext();
    }
 }

No entanto, não consigo encontrar nenhuma explicação decente sobre qual é preferível e em que circunstâncias deve ser usada.

Seria muito apreciada a orientação sobre a documentação e orientações sobre o que poderia quebrar se a escolhida errada.

Alnitak
fonte
2
Este link pode ajudá-lo. Passe por isso ..
Jun

Respostas:

305

Concordo que a documentação é escassa no que diz respeito aos contextos no Android, mas você pode reunir alguns fatos de várias fontes.

Esta postagem no blog oficial dos desenvolvedores do Google Android foi escrita principalmente para ajudar a corrigir vazamentos de memória, mas fornece algumas boas informações sobre contextos:

Em um aplicativo Android normal, você geralmente tem dois tipos de Contexto, Atividade e Aplicativo.

Ler o artigo um pouco mais detalha a diferença entre os dois e quando você pode considerar usar o aplicativo Context ( Activity.getApplicationContext()) em vez de usar o contexto de Atividade this). Basicamente, o contexto do Aplicativo está associado ao Aplicativo e sempre será o mesmo durante todo o ciclo de vida do seu aplicativo, onde o contexto da Atividade está associado à atividade e pode ser destruído várias vezes, pois a atividade é destruída durante as alterações na orientação da tela e tal.

Não encontrei nada sobre quando usar o getBaseContext () além de uma postagem de Dianne Hackborn, uma das engenheiras do Google que trabalha no Android SDK:

Não use getBaseContext (), apenas use o contexto que você possui.

Isso foi de uma postagem no grupo de notícias de desenvolvedores do Android . Você também pode fazer sua pergunta lá, porque várias pessoas que trabalham no Android monitoram esse grupo de notícias e respondem a perguntas.

Portanto, no geral, parece preferível usar o contexto global de aplicativos sempre que possível.

snctln
fonte
13
Quando tenho uma atividade A que pode iniciar a atividade B que, por sua vez, pode reiniciar A com o sinalizador CLEAR_TOP (e possivelmente repetir esse ciclo muitas vezes) - que contexto devo usar neste caso para evitar a construção de uma trilha enorme de contextos referenciados? Diana diz que usar 'this' em vez de getBaseContext, mas ... na maioria das vezes, A será reutilizado, mas há situações em que um novo objeto para A será criado e o antigo A vazará. Portanto, parece que getBaseContext é a escolha mais adequada para a maioria dos casos. Então não está claro o porquê Don't use getBaseContext(). Alguém poderia esclarecer isso?
JBM
2
como alguém acessaria o objeto de contexto dentro de uma classe que não estende Activity?
19411 Cole
11
@Cole, você pode criar uma classe, que chamaremos de "ExampleClass" aqui, cujo construtor pega um objeto Context e instancia uma variável de instância de classe, "appContext". Em seguida, sua classe Activity (ou qualquer outra classe) pode chamar um método ExampleClass que utiliza a variável de instância "appContext" da ExampleClass.
Archie1986
54

Aqui está o que eu encontrei sobre o uso de context:

1) No Activitypróprio, use thispara aumentar layouts e menus, registrar menus de contexto, instanciar widgets, iniciar outras atividades, criar novo Intentdentro de um Activity, instanciar preferências ou outros métodos disponíveis no Activity.

Inflar layout:

View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);

Inflar menu:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    this.getMenuInflater().inflate(R.menu.mymenu, menu);
    return true;
}

Registrar menu de contexto:

this.registerForContextMenu(myView);

Instanciar widget:

TextView myTextView = (TextView) this.findViewById(R.id.myTextView);

Iniciar um Activity:

Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);

Instanciar preferências:

SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();

2) Para a classe de todo o aplicativo, use getApplicationContext()como este contexto existe para a vida útil do aplicativo.

Recupere o nome do pacote Android atual:

public class MyApplication extends Application {    
    public static String getPackageName() {
        String packageName = null;
        try {
            PackageInfo mPackageInfo = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0);
            packageName = mPackageInfo.packageName;
        } catch (NameNotFoundException e) {
            // Log error here.
        }
        return packageName;
    }
}

Vincule uma classe para todo o aplicativo:

Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
    getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}

3) Para ouvintes e outros tipos de classes do Android (por exemplo, ContentObserver), use uma substituição de contexto como:

mContext = this;    // Example 1
mContext = context; // Example 2

onde thisou contexté o contexto de uma classe (atividade etc.).

Activity substituição de contexto:

public class MyActivity extends Activity {
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        mContext = this;
    }
}

Substituição de contexto do ouvinte:

public class MyLocationListener implements LocationListener {
    private Context mContext;
    public MyLocationListener(Context context) {
        mContext = context;
    }
}

ContentObserver substituição de contexto:

public class MyContentObserver extends ContentObserver {
    private Context mContext;
    public MyContentObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }
}

4) Para BroadcastReceiver(incluindo receptor embutido / embutido), use o próprio contexto do receptor.

Externo BroadcastReceiver:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
            sendReceiverAction(context, true);
        }
        private static void sendReceiverAction(Context context, boolean state) {
            Intent mIntent = new Intent(context.getClass().getName() + "." + context.getString(R.string.receiver_action));
            mIntent.putExtra("extra", state);
            context.sendBroadcast(mIntent, null);
        }
    }
}

Inline / Incorporado BroadcastReceiver:

public class MyActivity extends Activity {
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final boolean connected = intent.getBooleanExtra(context.getString(R.string.connected), false);
            if (connected) {
                // Do something.
            }
        }
    };
}

5) Para Serviços, use o próprio contexto do serviço.

public class MyService extends Service {
    private BroadcastReceiver mBroadcastReceiver;
    @Override
    public void onCreate() {
        super.onCreate();
        registerReceiver();
    }
    private void registerReceiver() {
        IntentFilter mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        this.mBroadcastReceiver = new MyBroadcastReceiver();
        this.registerReceiver(this.mBroadcastReceiver, mIntentFilter);
    } 
}

6) Para brindes, use geralmente getApplicationContext(), mas sempre que possível, use o contexto transmitido de uma atividade, serviço etc.

Use o contexto do aplicativo:

Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();

Use o contexto passado de uma fonte:

public static void showLongToast(Context context, String message) {
    if (context != null && message != null) {
        Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
        mToast.show();
    }
}

E por último, não use getBaseContext()como recomendado pelos desenvolvedores de estrutura do Android.

ATUALIZAÇÃO: adicione exemplos de Contextuso.

ChuongPham
fonte
11
Em vez do mContext, pode-se usar OuterClass.this; veja comentários em stackoverflow.com/questions/9605459/…
Paul Verest 15/15/15
3
+1 para uma resposta tão útil! Concordo que a resposta aceita é boa como resposta aceita, mas Santo Deus, essa resposta foi super informativa! Obrigado por todos esses exemplos, eles me ajudaram a entender melhor o uso do contexto como um todo. Eu até copiei sua resposta em um arquivo de texto na minha máquina como referência.
Ryan
13

Eu li esse tópico há alguns dias, me perguntando a mesma pergunta. Minha decisão depois de ler isso foi simples: sempre use applicationContext.

No entanto, encontrei um problema com isso, passei algumas horas para encontrá-lo e alguns segundos para resolvê-lo ... (alterando uma palavra ...)

Estou usando um LayoutInflater para inflar uma exibição que contém um Spinner.

Então, aqui estão duas possibilidades:

1)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());

2)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());

Então, eu estou fazendo algo parecido com isto:

    // managing views part
    View view = ContactViewer.mLayoutInflater.inflate(R.layout.aViewContainingASpinner, theParentView, false);
    Spinner spinner = (Spinner) view.findViewById(R.id.theSpinnerId);
    String[] myStringArray = new String[] {"sweet","love"};

    // managing adapter part
    // The context used here don't have any importance -- both work.
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this.getApplicationContext(), myStringArray, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

    theParentView.addView(view);

O que eu notei: se você instanciar seu linearLayout com o applicationContext, ao clicar no botão giratório em sua atividade, você terá uma exceção não capturada, vinda da máquina virtual da dalvik (não do seu código, é por isso que gastei muito de tempo para descobrir onde estava o meu erro ...).

Se você usar o baseContext, tudo bem, o menu de contexto será aberto e você poderá escolher entre suas opções.

Então, aqui está a minha conclusão: suponho (não o testei mais) que o baseContext é necessário ao lidar com o contextMenu em sua Atividade ...

O teste foi feito com a API 8 e testado em um HTC Desire, android 2.3.3.

Espero que meu comentário não o tenha aborrecido até agora e desejo tudo de bom. Happy coding ;-)

Mav3656
fonte
Eu sempre usei "this" ao criar visualizações em uma atividade. Com base no fato de que, se a atividade reiniciar, as visualizações serão refeitas e, talvez, haja um novo contexto para usar para criar as visualizações novamente. A desvantagem publicada no blog do desenvolvedor é que, enquanto um ImageView é destruído, o drawable / bitmap usado pode ficar nesse contexto. No entanto, é o que faço no momento. Em relação ao código em outro lugar do aplicativo (classes normais), apenas uso o contexto do aplicativo, pois não é específico para nenhuma atividade ou elemento da interface do usuário.
JonWillis
6

Primeiro, concordo que devemos usar o appcontext sempre que possível. então "isso" em atividade. eu nunca tive necessidade de basecontext.

Nos meus testes, na maioria dos casos, eles podem ser trocados. Na maioria dos casos, o motivo pelo qual você deseja conhecer um contexto é acessar arquivos, preferências, banco de dados etc. Esses dados são refletidos como arquivos na pasta de dados particulares do seu aplicativo (/ data / data /). Não importa qual contexto você use, eles serão mapeados para a mesma pasta / arquivos, para que você esteja bem.

Foi o que eu observei. Talvez haja casos em que você deve distingui-los.

samsonsu
fonte
Eu precisava do basecontext para definir globalmente o idioma do aplicativo na inicialização (quando não corresponde ao idioma padrão do telefone).
Tina
3

Em alguns casos, você pode usar o contexto de atividade sobre o contexto do aplicativo ao executar algo em um encadeamento. Quando o encadeamento conclui a execução e você precisa retornar o resultado à atividade do chamador, você precisa desse contexto com um manipulador.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
Paulo
fonte
2

Em palavras simples

getApplicationContext()como o nome do método sugere, seu aplicativo estará ciente dos detalhes gerais do aplicativo, que você pode acessar de qualquer lugar no aplicativo. Assim, você pode fazer uso disso na vinculação de serviço, no registro de transmissão etc. Application contextpermanecerá ativo até a saída do aplicativo.

getActivity()ou thistornará seu aplicativo ciente da tela atual, visível também os detalhes no nível do aplicativo fornecidos por application context. Portanto, tudo o que você deseja saber sobre a tela atual Window ActionBar Fragementmangerestá disponível neste contexto. Basicamente e Activityestender Context. Esse contexto permanecerá ativo até que o componente atual (atividade) esteja ativo

arjun
fonte
1

A confusão decorre do fato de que existem inúmeras maneiras de acessar o Contexto, sem (na superfície) diferenças discerníveis. Abaixo estão quatro das maneiras mais comuns pelas quais você pode acessar o Contexto em uma Atividade.

getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new

O que é um contexto? Pessoalmente, gosto de pensar no Contexto como o estado do seu aplicativo a qualquer momento. O Contexto do aplicativo representa uma configuração global ou básica do seu aplicativo e uma Atividade ou Serviço pode ser construído sobre ele e representa uma instância de configuração do seu Aplicativo ou um estado transitivo para ele.

Se você consultar a fonte android.content.Context, verá que Context é uma classe abstrata e os comentários na classe são os seguintes:

Interface com informações globais sobre um ambiente de aplicativo. Esta é uma classe abstrata cuja implementação é fornecida pelo sistema Android. Ele permite o acesso a application-specificrecursos e classes, bem como chamadas para application-leveloperações como iniciar atividades, transmitir e receber intenções, etc. O que retiro disso é que o Context fornece uma implementação comum para acessar o nível do aplicativo e o nível do sistema Recursos. Os recursos no nível do aplicativo podem estar acessando coisas como recursos [getResources()]ou ativos de String [getAssets()]e recurso no nível do sistema é qualquer coisa com a qual você acessaContext.getSystemService().

De fato, dê uma olhada nos comentários sobre os métodos e eles parecem reforçar essa noção:

getSystemService(): Retorne o identificador para um system-levelserviço pelo nome. A classe do objeto retornado varia de acordo com o nome solicitado. getResources(): Retorne uma instância de recursos para o pacote do seu aplicativo. getAssets(): Retorne uma instância de recursos para o pacote do seu aplicativo. Vale a pena ressaltar que na classe abstrata Context, todos os métodos acima são abstratos! Somente uma instância de getSystemService (Class) possui uma implementação e isso chama um método abstrato. Isso significa que a implementação deve ser fornecida principalmente pelas classes de implementação, que incluem:

ContextWrapper
Application
Activity
Service
IntentService

Observando a documentação da API, a hierarquia das classes é semelhante a esta:

Contexto

| - ContextWrapper

| - - Aplicação

| - - ContextThemeWrapper

| - - - - Atividade

| - - Serviço

| - - - IntentService

Como sabemos que Contextele próprio não está fornecendo nenhum insight, descemos a árvore, observamos ContextWrappere percebemos que também não há muito. Como o Aplicativo se estende ContextWrapper, não há muito o que analisar por lá, pois ele não substitui a implementação fornecida pelo ContextWrapper. Isso significa que a implementação do Contexto é fornecida pelo SO e oculta do API. Você pode dar uma olhada na implementação concreta do Context, olhando a fonte da classe ContextImpl.

Chanaka Weerasinghe
fonte
0

Eu só usei isso e getBaseContextquando brindar de um onClick(noob muito verde para Java e android). Eu uso isso quando meu clicker está diretamente na atividade e preciso usá-lo getBaseContextem um clicker interno anônimo. Acho que esse é o truque getBaseContext, talvez esteja retornando o contexto da atividade na qual a classe interna está se escondendo.

Tony
fonte
11
Isso está errado, está retornando o contexto base da própria atividade. Para obter a atividade (a que você deseja usar como contexto) de uma classe interna anônima, use algo parecido MyActivity.this. Usar o contexto base como você descreve provavelmente não causará problemas, mas está errado.
nickmartens1980