Usando Contexto de aplicación en todas partes?
-
13-09-2019 - |
Pregunta
En una aplicación para Android, ¿hay algo malo con el siguiente enfoque:
public class MyApp extends android.app.Application {
private static MyApp instance;
public MyApp() {
instance = this;
}
public static Context getContext() {
return instance;
}
}
y pasarlo por todas partes (por ejemplo SQLiteOpenHelper) cuando se requiera el contexto (y sin fugas por supuesto)?
Solución
Hay un par de posibles problemas con este enfoque, aunque en muchas circunstancias (como el ejemplo) va a funcionar bien.
En particular, se debe tener cuidado cuando se trata de cualquier cosa que se ocupa de la GUI
que requiere una Context
. Por ejemplo, si pasa el contexto de aplicación en el LayoutInflater
obtendrá una excepción. En términos generales, el enfoque es excelente: es una buena práctica usar un Activity's
Context
dentro de ese Activity
y Application Context
cuando se pasa de un contexto más allá de la alcance de un Activity
evitar pérdidas de memoria .
Además, como un alternativa a su patrón se puede utilizar el método abreviado de llamar getApplicationContext()
en un Context
objeto (como una actividad) para obtener el contexto de aplicación.
Otros consejos
En mi experiencia de este enfoque no debería ser necesario. Si necesita el contexto para lo que por lo general puede conseguir a través de una llamada a View.getContext () y utilizando el Context
obtenido en ella se puede llamar Context.getApplicationContext () para obtener el contexto Application
. Si usted está tratando de obtener el contexto Application
esto desde un Activity
siempre puede llamar Activity.getApplication () que debe ser capaz de pasar como el Context
necesario para una llamada a SQLiteOpenHelper()
.
En general no parece ser un problema con el enfoque de esta situación, pero cuando se trata de Context
sólo asegúrese de que no tengan fugas de memoria en cualquier lugar como se describe en la página oficial Google Android Blog de desarrolladores .
Algunas personas han preguntado:? ¿cómo puede el singleton devolver un puntero nulo Estoy respondiendo a esa pregunta. (No puedo responder en un comentario porque necesito el código postal.)
Se puede devolver null en entre dos eventos: (1) se carga la clase, y (2) se crea el objeto de esta clase. He aquí un ejemplo:
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 a ejecutar el código:
$ javac A.java
$ java A
x:X@a63599 y:Y@9036e
x:null y:null
La segunda línea muestra que Y.xinstance y X.yinstance son nula ; que son nulos porque las variables X.xinstance ans Y.yinstance se leyeron cuando eran nulas.
¿Puede ser fijo? Sí,
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);
}
}
y este código no muestra ninguna anomalía:
$ javac A.java
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e
y esta no es una opción para el objeto Application
Android:. El programador no controla el momento en que se crea
Una vez más: la diferencia entre el primer ejemplo y el segundo es que el segundo ejemplo, se crea una instancia si el puntero estática es nulo. Sin embargo, un programador no puede crear la objeto de la aplicación de Android antes de que el sistema decide hacerlo.
Actualizar
Un ejemplo más desconcertante donde los campos estáticos inicializados pasar a 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());
}
}
Y se obtiene:
$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull
Tenga en cuenta que no se puede mover la variable declaración de una línea estática superior, el código no se compilará.
Usted está tratando de crear un contenedor para obtener contexto de aplicación y hay una posibilidad de que pudiera volver "null
" puntero.
Según mi entendimiento, que supongo que es mejor enfoque a CALL cualquiera de los 2
Context.getApplicationContext()
o Activity.getApplication()
.
Clase Aplicación:
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 la aplicación en el AndroidManifest:
<application android:name=".MyApplication"
...
/>
Uso:
MyApplication.getAppContext()
Es un buen enfoque. Yo lo uso yo también. Me gustaría sugerir para anular onCreate
para establecer el singleton en lugar de utilizar un constructor.
Y ya que ha mencionado SQLiteOpenHelper
:. En onCreate ()
puede abrir la base de datos, así
En lo personal creo que la documentación se equivocó al decir que Normalmente no hay necesidad de una subclase de aplicación . Creo que es todo lo contrario: debe siempre Aplicación subclase
.Me gustaría utilizar contexto de aplicación para obtener un servicio del sistema en el constructor. Esto facilita las pruebas y los beneficios de la composición
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
}
clase de prueba sería entonces utilizar el constructor sobrecargado.
Android usaría el constructor por defecto.
Me gusta, pero yo sugeriría un producto único en su lugar:
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;
}
}
Estoy utilizando el mismo enfoque, sugiero escribir el singleton un poco mejor:
public static MyApp getInstance() {
if (instance == null) {
synchronized (MyApp.class) {
if (instance == null) {
instance = new MyApp ();
}
}
}
return instance;
}
pero no estoy usando todas partes, yo uso getContext()
y getApplicationContext()
donde puedo hacerlo!