android - contexto - activity y context
¿Forma estática de obtener ''Context'' en Android? (20)
¿Hay una manera de obtener la instancia de Context
actual dentro de un método estático?
Estoy buscando de esa manera porque odio guardar la instancia ''Context'' cada vez que cambia.
Acabo de lanzar un marco para Android inspirado en jQuery llamado API de Vapor que apunta a simplificar el desarrollo de aplicaciones.
La clase de $
fachada central mantiene una WeakReference
(enlace a la increíble publicación de blog de Java sobre esto de Ethan Nicholas) en el contexto de la Activity
actual, que puede recuperar llamando al:
$.act()
Una WeakReference
mantiene una referencia sin impedir que la recolección de basura recupere el objeto original, por lo que no debería tener problemas con las pérdidas de memoria.
La desventaja, por supuesto, es que corre el riesgo de que $.act()
pueda devolver nulo. Sin embargo, aún no me he topado con este escenario, así que quizás sea solo un riesgo mínimo, vale la pena mencionarlo.
También puede establecer el contexto manualmente si no está utilizando VaporActivity
como su clase de Activity
:
$.act(Activity);
Además, gran parte del marco de la API de Vapor utiliza este contexto almacenado de forma inherente, lo que podría significar que no es necesario que lo almacene usted mismo si decide utilizar el marco. Echa un vistazo al sitio para obtener más información y muestras.
Espero que eso ayude :)
Aquí hay una forma no documentada de obtener una developer.android.com/reference/android/app/Application.html (que es un contexto) desde cualquier lugar en el hilo de la interfaz de usuario. Se basa en el método estático oculto ActivityThread.currentApplication()
. Debería funcionar al menos en Android 4.x.
try {
final Class<?> activityThreadClass =
Class.forName("android.app.ActivityThread");
final Method method = activityThreadClass.getMethod("currentApplication");
return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
// handle exception
} catch (final NoSuchMethodException e) {
// handle exception
} catch (final IllegalArgumentException e) {
// handle exception
} catch (final IllegalAccessException e) {
// handle exception
} catch (final InvocationTargetException e) {
// handle exception
}
Tenga en cuenta que es posible que este método devuelva nulo, por ejemplo, cuando llama al método fuera del subproceso de la interfaz de usuario o si la aplicación no está vinculada al subproceso.
Aún es mejor usar la solución de @RohitGhatol si puede cambiar el código de la aplicación.
Así que modifiqué la respuesta aceptada porque está causando una pérdida de memoria, esto es lo que se me ocurrió ...
AndroidManifest.xml
<application android:name="com.xyz.MyApplication">
...
</application>
MyApplication.java
public class MyBakingAppContext extends Application {
private static Object mContext;
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static Context getAppContext() {
return (Context)mContext;
}
}
Lo que realmente hice es asignar un contexto a un objeto y devolver el objeto como contexto (convertirlo en contexto). Espero que te ayude.
Creo que necesitas un cuerpo para el método getAppContext()
:
public static Context getAppContext()
return MyApplication.context;
De acuerdo con esta fuente , puede obtener su propio Contexto extendiendo ContextWrapper
public class SomeClass extends ContextWrapper {
public SomeClass(Context base) {
super(base);
}
public void someMethod() {
// notice how I can use "this" for Context
// this works because this class has it''s own Context just like an Activity or Service
startActivity(this, SomeRealActivity.class);
//would require context too
File cacheDir = getCacheDir();
}
}
Proxiar la implementación del Contexto que simplemente delega todas sus llamadas a otro Contexto. Puede clasificarse para modificar el comportamiento sin cambiar el contexto original.
Depende de para qué estés usando el contexto. Puedo pensar en al menos una desventaja de ese método:
Si está intentando crear un AlertDialog
con AlertDialog.Builder
, el contexto de la Application
no funcionará. Creo que necesitas el contexto para la Activity
actual ...
Hacer esto:
En el archivo de manifiesto de Android, declare lo siguiente.
<application android:name="com.xyz.MyApplication">
</application>
Luego escribe la clase:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Ahora, en todas partes, llame a MyApplication.getAppContext()
para obtener el contexto de su aplicación de forma estática.
He probado todas las sugerencias aquí descritas y puedo decir:
Si uso código de seguimiento:
public class App extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getAppContext() {
return context;
}
}
Me meto en el Studio 3 un mensaje de error:
Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
.
Pero si uso código de seguimiento:
public class App extends Application {
private static List<Context> context = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
context.add(0, getApplicationContext());
}
public static Context getAppContext() {
return context.get(0);
}
}
Funciona bien sin errores.
He usado esto en algún momento:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Este es un contexto válido que usé para obtener servicios del sistema y para trabajar.
Pero, lo usé solo en modificaciones de marco / base y no lo probé en aplicaciones de Android.
Una advertencia que debe saber: Cuando se registre para receptores de transmisión en este contexto, no funcionará y obtendrá:
java.lang.SecurityException: el paquete del llamador de Android no se está ejecutando en el proceso ProcessRecord
La mayoría de las aplicaciones que desean un método conveniente para obtener el contexto de la aplicación crean su propia clase que amplía la aplicación developer.android.com/reference/android/app/Application.html .
GUÍA
Puedes lograr esto creando primero una clase en tu proyecto como la siguiente:
import android.app.Application;
import android.content.Context;
public class App extends Application {
private static Application sApplication;
public static Application getApplication() {
return sApplication;
}
public static Context getContext() {
return getApplication().getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
sApplication = this;
}
}
Luego, en tu AndroidManifest debes especificar el nombre de tu clase en la etiqueta de AndroidManifest.xml:
<application
...
android:name="com.example.App" >
...
</application>
Luego, puede recuperar el contexto de la aplicación en cualquier método estático usando lo siguiente:
public static void someMethod() {
Context context = App.getContext();
}
ADVERTENCIA
Antes de agregar algo como lo anterior a su proyecto, debe considerar lo que dice la documentación:
Normalmente no hay necesidad de subclase de aplicación. En la mayoría de las situaciones, los singletons estáticos pueden proporcionar la misma funcionalidad de una manera más modular. Si su singleton necesita un contexto global (por ejemplo, para registrar receptores de difusión), a la función para recuperarlo se le puede dar un Contexto que utiliza internamente Context.getApplicationContext () cuando se construye por primera vez el singleton.
REFLEXIÓN
También hay otra forma de obtener el contexto de la aplicación mediante la reflexión. La reflexión se ve a menudo en Android y personalmente creo que esto no debería usarse en la producción.
Para recuperar el contexto de la aplicación, debemos invocar un método en una clase oculta ( ActivityThread ) que ha estado disponible desde la API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
Hay una clase oculta más ( AppGlobals ) que proporciona una manera de obtener el contexto de la aplicación de forma estática. Obtiene el contexto usando ActivityThread
por lo que realmente no hay diferencia entre el siguiente método y el publicado anteriormente:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
¡Feliz codificación!
La respuesta de Rohit parece correcta. Sin embargo, tenga en cuenta que la "Ejecución instantánea" de AndroidStudio depende de no tener atributos de static Context
en su código, que yo sepa.
No, no creo que haya. Desafortunadamente, estás atascado llamando a getApplicationContext()
de Activity
o una de las otras subclases de Context
. Además, this pregunta está algo relacionada.
Puedes usar lo siguiente:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Cualquier otra clase:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
Si está abierto a usar RoboGuice , puede hacer que se inyecte el contexto en cualquier clase que desee. Aquí hay una pequeña muestra de cómo hacerlo con RoboGuice 2.0 (beta 4 en el momento de escribir este artículo)
import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;
import javax.inject.Inject;
@ContextSingleton
public class DataManager {
@Inject
public DataManager(Context context) {
Properties properties = new Properties();
properties.load(context.getResources().getAssets().open("data.properties"));
} catch (IOException e) {
}
}
}
Si no desea modificar el archivo de manifiesto, puede almacenar manualmente el contexto en una variable estática en su actividad inicial:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
Y simplemente configure el contexto cuando comience su actividad (o actividades):
// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Nota: como todas las otras respuestas, esta es una pérdida potencial de memoria.
Si por alguna razón quiere contexto de Aplicación en cualquier clase, no solo en aplicaciones / actividades extendidas, tal vez para algunas clases de fábrica o de ayudante. Puede agregar el siguiente singleton a su aplicación.
public class GlobalAppContextSingleton {
private static GlobalAppContextSingleton mInstance;
private Context context;
public static GlobalAppContextSingleton getInstance() {
if (mInstance == null) mInstance = getSync();
return mInstance;
}
private static synchronized GlobalAppContextSingleton getSync() {
if (mInstance == null) mInstance =
new GlobalAppContextSingleton();
return mInstance;
}
public void initialize(Context context) {
this.context = context;
}
public Context getApplicationContext() {
return context;
}
}
luego inicialízalo en onCreate de tu clase de aplicación con
GlobalAppContextSingleton.getInstance().initialize(this);
Úsalo en cualquier lugar llamando
GlobalAppContextSingleton.getInstance().getApplicationContext()
Sin embargo, no recomiendo este enfoque a nada que no sea el contexto de la aplicación. Como puede causar pérdidas de memoria.
Suponiendo que estamos hablando de obtener el contexto de la aplicación, lo implementé como lo sugirió la aplicación de extensión @Rohit Ghatol. Lo que sucedió entonces, es que no hay garantía de que el contexto recuperado de esa manera siempre sea no nulo. En el momento en que lo necesita, por lo general, debido a que desea inicializar un ayudante u obtener un recurso, no puede demorar el tiempo; manejar el caso nulo no te ayudará. Así que entendí que básicamente estaba luchando contra la arquitectura de Android, como se indica en los docs
Nota: normalmente no hay necesidad de subclase de aplicación. En la mayoría de las situaciones, los singletons estáticos pueden proporcionar la misma funcionalidad de una manera más modular. Si su singleton necesita un contexto global (por ejemplo, para registrar receptores de difusión), incluya Context.getApplicationContext () como un argumento de contexto cuando invoque el método getInstance () de su singleton.
y explicado por Dianne Hackborn
La única razón por la que la aplicación existe como algo de lo que se puede derivar es porque durante el desarrollo pre-1.0, uno de nuestros desarrolladores de aplicaciones me estaba molestando continuamente sobre la necesidad de tener un objeto de aplicación de nivel superior del que pudieran derivar para que pudieran tener una "Para ellos, el modelo de aplicación, y finalmente cedí. Siempre lamentaré haber cedido en ese. :)
Ella también está sugiriendo la solución a este problema:
Si lo que desea es un estado global que se pueda compartir en diferentes partes de su aplicación, use un singleton. [...] Y esto conduce más naturalmente a cómo debe administrar estas cosas: inicializarlas a pedido.
así que lo que hice fue deshacerme de la extensión de la aplicación, y pasar el contexto directamente a getInstance () del ayudante de singleton, mientras se guardaba una referencia al contexto de la aplicación en el constructor privado:
private static MyHelper instance;
private final Context mContext;
private MyHelper(@NonNull Context context) {
mContext = context.getApplicationContext();
}
public static MyHelper getInstance(@NonNull Context context) {
synchronized(MyHelper.class) {
if (instance == null) {
instance = new MyHelper(context);
}
return instance;
}
}
la persona que llama pasará un contexto local al ayudante:
Helper.getInstance(myCtx).doSomething();
Entonces, para responder a esta pregunta correctamente: hay formas de acceder al Contexto de la aplicación de forma estática, pero todas deberían estar desalentadas, y usted debería preferir pasar un contexto local a getInstance () del singleton.
Para cualquier persona interesada, puedes leer una versión más detallada en el blog fwd
Utilizo una variación del patrón de diseño de Singleton para ayudarme con esto.
import android.app.Activity;
import android.content.Context;
public class ApplicationContextSingleton {
private static Activity gContext;
public static void setContext( Activity activity) {
gContext = activity;
}
public static Activity getActivity() {
return gContext;
}
public static Context getContext() {
return gContext;
}
}
Entonces llamo a ApplicationContextSingleton.setContext( this );
en mi activity.onCreate () y ApplicationContextSingleton.setContext( null );
en onDestroy () ;
En Kotlin
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
y obtener contexto como
MyApp.mInstance
o
MyApp.getContext()
Kotlin forma :
Manifiesto:
<application android:name="MyApplication">
</application>
MyApplication.kt
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication
private set
}
}
A continuación, puede acceder a la propiedad a través de MyApplication.instance