Usando contexto de aplicação em todos os lugares?
-
13-09-2019 - |
Pergunta
Em um aplicativo Android, há de errado nada com a seguinte abordagem:
public class MyApp extends android.app.Application {
private static MyApp instance;
public MyApp() {
instance = this;
}
public static Context getContext() {
return instance;
}
}
e passá-lo em todos os lugares (por exemplo SQLiteOpenHelper) onde o contexto é necessário (e que não tem fugas é claro)?
Solução
Há um par de potenciais problemas com esta abordagem, embora em muitas circunstâncias (como o seu exemplo) ele vai funcionar bem.
Em particular, você deve ter cuidado ao lidar com qualquer coisa que lida com o GUI
que requer um Context
. Por exemplo, se você passar o contexto de aplicação para o LayoutInflater
você receberá uma exceção. De um modo geral, a sua abordagem é excelente: é uma boa prática para usar um Activity's
Context
dentro desse Activity
e Application Context
quando passa um contexto para além da escopo de uma vazamentos de memória Activity
Evitar .
Além disso, como um alternativa para o seu padrão que você pode usar o atalho de getApplicationContext()
chamando em um Context
objeto (como uma Atividade) para obter o contexto da aplicação.
Outras dicas
Na minha experiência não deve ser necessária esta abordagem. Se você precisar o contexto para qualquer coisa que você normalmente pode obtê-lo através de uma chamada para View.getContext () e usando o Context
obtido lá você pode chamar Context.getApplicationContext () para obter o contexto Application
. Se você está tentando obter o contexto Application
isso a partir de um Activity
você pode sempre chamar Activity.getApplication () que deve ser capaz de ser transmitido como o Context
necessário para uma chamada para SQLiteOpenHelper()
.
No geral não parece ser um problema com a sua abordagem para esta situação, mas quando se lida com Context
apenas certifique-se que você não está vazando em qualquer lugar de memória como descrito no site oficial Google Android Developers Blog .
Algumas pessoas perguntaram:? como pode o singleton retornar um ponteiro nulo Eu estou respondendo a essa pergunta. (Eu não posso responder em um comentário porque eu preciso código postal.)
Pode retornar nulo entre dois eventos: (1) a classe é carregada, e (2) o objecto desta classe é criada. Aqui está um exemplo:
class X {
static X xinstance;
static Y yinstance = Y.yinstance;
X() {xinstance=this;}
}
class Y {
static X xinstance = X.xinstance;
static Y yinstance;
Y() {yinstance=this;}
}
public class A {
public static void main(String[] p) {
X x = new X();
Y y = new Y();
System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
}
}
Vamos executar o código:
$ javac A.java
$ java A
x:X@a63599 y:Y@9036e
x:null y:null
A segunda linha mostra que Y.xinstance e X.yinstance nulo ; eles são nulos porque as variáveis ?? X.xinstance ans Y.yinstance foram lidos quando eles foram nulos.
Isso pode ser corrigido? Sim,
class X {
static Y y = Y.getInstance();
static X theinstance;
static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
static X x = X.getInstance();
static Y theinstance;
static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}
public class A {
public static void main(String[] p) {
System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
System.out.println("x:"+Y.x+" y:"+X.y);
}
}
e shows este código nenhuma anomalia:
$ javac A.java
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e
MAS isso não é uma opção para o objeto Application
Android:. O programador não controla o momento em que é criado
Mais uma vez: a diferença entre o primeiro exemplo e o segundo é que o segundo exemplo, se cria uma instância do ponteiro estático é nula. Mas um programador não pode criar o objeto de aplicativo Android antes que o sistema decide fazê-lo.
Atualizar
Um exemplo mais intrigante, onde campos estáticos inicializados acontecer de ser null
.
Main.java :
enum MyEnum {
FIRST,SECOND;
private static String prefix="<", suffix=">";
String myName;
MyEnum() {
myName = makeMyName();
}
String makeMyName() {
return prefix + name() + suffix;
}
String getMyName() {
return myName;
}
}
public class Main {
public static void main(String args[]) {
System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
}
}
E você tem:
$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull
Note que você não pode mover a variável estática declaração uma linha superior, o código não será compilado.
Você está tentando criar um wrapper para obter contexto de aplicativos e há uma possibilidade de que ele pode retornar "null
" ponteiro.
De acordo com o meu entendimento, eu acho que é melhor abordagem para call qualquer um dos 2
Context.getApplicationContext()
ou Activity.getApplication()
.
Aplicação Classe:
import android.app.Application;
import android.content.Context;
public class MyApplication extends Application {
private static Context mContext;
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static Context getAppContext() {
return mContext;
}
}
declarar o pedido na AndroidManifest:
<application android:name=".MyApplication"
...
/>
Uso:
MyApplication.getAppContext()
É uma boa abordagem. Eu usá-lo mesmo assim. Gostaria de sugerir para substituir onCreate
para definir o singleton em vez de usar um construtor.
E já que você mencionou SQLiteOpenHelper
:. Em onCreate ()
você pode abrir o banco de dados, bem
Pessoalmente acho que a documentação entendeu errado em dizer que Normalmente, não há necessidade de Aplicação subclasse . Eu acho que o oposto é verdadeiro: Você deve sempre Aplicação subclasse
.Gostaria de usar Aplicação Contexto para obter um serviço de sistema no construtor. Isso facilita Teste e benefícios da composição
public class MyActivity extends Activity {
private final NotificationManager notificationManager;
public MyActivity() {
this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
}
public MyActivity(NotificationManager notificationManager) {
this.notificationManager = notificationManager;
}
// onCreate etc
}
classe de teste, então, usar o construtor sobrecarregado.
Android usaria o construtor padrão.
Eu gosto dele, mas gostaria de sugerir um singleton em vez disso:
package com.mobidrone;
import android.app.Application;
import android.content.Context;
public class ApplicationContext extends Application
{
private static ApplicationContext instance = null;
private ApplicationContext()
{
instance = this;
}
public static Context getInstance()
{
if (null == instance)
{
instance = new ApplicationContext();
}
return instance;
}
}
Eu estou usando a mesma abordagem, sugiro para escrever o singleton um pouco melhor:
public static MyApp getInstance() {
if (instance == null) {
synchronized (MyApp.class) {
if (instance == null) {
instance = new MyApp ();
}
}
}
return instance;
}
mas eu não estou usando em todos os lugares, eu uso getContext()
e getApplicationContext()
onde posso fazê-lo!