¿Cuál es la diferencia entre los diversos métodos para obtener un contexto?

390

En varios bits de código de Android que he visto:

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

Sin embargo, no puedo encontrar ninguna explicación decente sobre cuál es preferible y bajo qué circunstancias se debe usar.

Se agradecerían los consejos sobre la documentación sobre esto y la orientación sobre lo que podría romperse si se elige el equivocado.

Alnitak
fuente
2
Este enlace puede ayudarte. Pase por esto ..
Aju

Respuestas:

305

Estoy de acuerdo en que la documentación es escasa cuando se trata de Contextos en Android, pero puede recopilar algunos datos de varias fuentes.

Esta publicación de blog en el blog oficial de desarrolladores de Google Android se escribió principalmente para ayudar a abordar las fugas de memoria, pero también proporciona buena información sobre los contextos:

En una aplicación normal de Android, generalmente tiene dos tipos de contexto, actividad y aplicación.

Leer el artículo un poco más sobre la diferencia entre los dos y cuándo podría considerar usar el contexto de la aplicación ( Activity.getApplicationContext()) en lugar de usar el contexto de la actividad this). Básicamente, el contexto de la aplicación está asociado con la aplicación y siempre será el mismo durante todo el ciclo de vida de su aplicación, mientras que el contexto de la actividad está asociado con la actividad y podría destruirse muchas veces a medida que la actividad se destruye durante los cambios de orientación de la pantalla y tal.

Realmente no pude encontrar nada sobre cuándo usar getBaseContext () aparte de una publicación de Dianne Hackborn, uno de los ingenieros de Google que trabaja en el SDK de Android:

No uses getBaseContext (), solo usa el contexto que tienes.

Eso fue de una publicación en el grupo de noticias de desarrolladores de Android , es posible que desee considerar hacer su pregunta allí también, porque un puñado de las personas que trabajan en Android monitorean ese grupo de noticias y responden preguntas.

Entonces, en general, parece preferible usar el contexto de aplicación global cuando sea posible.

snctln
fuente
13
Cuando tengo una actividad A que puede iniciar la actividad B que, a su vez, puede reiniciar A con el indicador CLEAR_TOP (y posiblemente repetir este ciclo muchas veces), ¿qué contexto debo usar en este caso para evitar construir un gran rastro de contextos referenciados? Diana dice que usa 'this' en lugar de getBaseContext, pero luego ... la mayoría de las veces A se reutilizará, pero hay situaciones en las que se creará un nuevo objeto para A y luego se filtrará el viejo A. Entonces parece que getBaseContext es la opción más adecuada para la mayoría de los casos. Entonces no está claro por qué Don't use getBaseContext(). ¿Alguien podría aclarar esto?
JBM
2
¿Cómo se accedería al objeto de contexto dentro de una clase que no extiende la Actividad?
Cole
1
@Cole, podría crear una clase, que llamaremos "ExampleClass" aquí, cuyo constructor toma un objeto Context e instancia una variable de instancia de clase, "appContext". Luego, su clase de actividad (o cualquier otra clase) puede llamar a un método ExampleClass que utiliza la variable de instancia "appContext" de ExampleClass.
Archie1986
54

Esto es lo que he encontrado con respecto al uso de context:

1) Dentro de un Activitymismo, thisúselo para inflar diseños y menús, registrar menús contextuales, crear widgets de instancias, iniciar otras actividades, crear nuevos Intentdentro de Activity, preferencias de instancia u otros métodos disponibles en un Activity.

Diseño inflado:

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

Inflar menú:

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

Menú contextual de registro:

this.registerForContextMenu(myView);

Reproductor de instancia:

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

Comience un Activity:

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

Instanciar preferencias:

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

2) Para la clase de toda la aplicación, use getApplicationContext()como este contexto existe para la vida útil de la aplicación.

Recupere el nombre del paquete actual de Android:

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;
    }
}

Enlace una clase de toda la aplicación:

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

3) Para los oyentes y otro tipo de clases de Android (por ejemplo, ContentObserver), use una sustitución de contexto como:

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

donde thiso contextes el contexto de una clase (Actividad, etc.).

Activity sustitución de contexto:

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

Sustitución del contexto del oyente:

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

ContentObserver sustitución de contexto:

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

4) Para BroadcastReceiver(incluido el receptor integrado / en línea), use el contexto propio del 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);
        }
    }
}

En línea / incrustado 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 los Servicios, use el contexto propio del servicio.

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 tostadas, generalmente use getApplicationContext(), pero cuando sea posible, use el contexto pasado de una Actividad, Servicio, etc.

Utilice el contexto de la aplicación:

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

Usar contexto pasado de una fuente:

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

Y por último, no lo use getBaseContext()según lo aconsejado por los desarrolladores de framework de Android.

ACTUALIZACIÓN: Agregue ejemplos de Contextuso.

ChuongPham
fuente
1
En lugar de mContext, uno puede usar OuterClass.this; ver comentarios en stackoverflow.com/questions/9605459/…
Paul Verest
3
¡+1 por una respuesta tan útil! Estoy de acuerdo en que la respuesta aceptada está bien como respuesta aceptada, pero santo molly, ¡esta respuesta fue súper informativa! Gracias por todos esos ejemplos, me ayudaron a comprender mejor el uso del contexto en su conjunto. Incluso copié su respuesta en un archivo de texto en mi máquina como referencia.
Ryan
13

Leí este hilo hace unos días, haciéndome la misma pregunta. Mi decisión después de leer esto fue simple: siempre use applicationContext.

Sin embargo, encontré un problema con esto, pasé unas horas para encontrarlo y unos segundos para resolverlo ... (cambiando una palabra ...)

Estoy usando un LayoutInflater para inflar una vista que contiene un Spinner.

Así que aquí hay dos posibilidades:

1)

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

2)

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

Entonces, estoy haciendo algo como esto:

    // 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);

Lo que noté: si instanciaste tu diseño lineal con el contexto de la aplicación, cuando hagas clic en el spinner en tu actividad, tendrás una excepción no detectada, proveniente de la máquina virtual dalvik (no de tu código, es por eso que he gastado mucho) de tiempo para encontrar dónde fue mi error ...).

Si usa el baseContext, entonces está bien, el menú contextual se abrirá y podrá elegir entre sus opciones.

Así que aquí está mi conclusión: supongo (no lo he probado más) que el baseContext se requiere cuando se trata de contextMenu en su Actividad ...

La prueba se realizó con la codificación API 8 y se probó en un HTC Desire, Android 2.3.3.

Espero que mi comentario no te haya aburrido hasta ahora y te deseo todo lo mejor. Feliz codificación ;-)

Mav3656
fuente
Siempre he usado "esto" al crear vistas en una actividad. Sobre la base de que si la actividad se reinicia, las vistas se vuelven a crear y puede haber un nuevo contexto que utilizar para volver a crear las vistas. El inconveniente publicado en el blog del desarrollador es que mientras un ImageView está destruido, el dibujo / mapa de bits utilizado puede colgar en ese contexto. Sin embargo, eso es lo que hago en este momento. Con respecto al código en otra parte de la aplicación (clases normales), solo uso el contexto de la aplicación, ya que no es específico de ninguna actividad o elemento de la interfaz de usuario.
JonWillis
6

Primero, estoy de acuerdo en que deberíamos usar appcontext siempre que sea posible. entonces "esto" en actividad. Nunca he necesitado el contexto base.

En mis pruebas, en la mayoría de los casos se pueden intercambiar. En la mayoría de los casos, la razón por la que desea obtener un contexto es para acceder a archivos, preferencias, base de datos, etc. Estos datos eventualmente se reflejan como archivos en la carpeta de datos privados de su aplicación (/ data / data /). Independientemente del contexto que utilice, se asignarán a la misma carpeta / archivos para que esté bien.

Eso es lo que observé. Tal vez hay casos en los que debería distinguirlos.

samsonsu
fuente
Necesité basecontext para configurar globalmente el idioma de la aplicación al inicio (cuando no coincide con el idioma predeterminado del teléfono).
Tina
3

En algunos casos, puede usar el contexto de actividad sobre el contexto de la aplicación al ejecutar algo en un hilo. Cuando el subproceso completa la ejecución y necesita devolver el resultado a la actividad del llamador, necesita ese contexto con un controlador.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
Pablo
fuente
2

En palabras simples

getApplicationContext()como sugiere el nombre del método, su aplicación estará al tanto de todos los detalles de la aplicación a los que puede acceder desde cualquier lugar de la aplicación. Por lo tanto, puede hacer uso de esto en el enlace de servicio, el registro de transmisión, etc. Application contextestará activo hasta que la aplicación salga.

getActivity()o thishará que su aplicación esté al tanto de la pantalla actual que también es visible y los detalles del nivel de la aplicación proporcionados por application context. Entonces, todo lo que quieras saber sobre la pantalla actual Window ActionBar Fragementmangery, por lo tanto, está disponible con este contexto. Básicamente yActivity extender Context. Este contexto seguirá vivo hasta que el componente actual (actividad) esté vivo

arjun
fuente
1

La confusión surge del hecho de que existen numerosas formas de acceder al Contexto, sin diferencias (en la superficie) perceptibles. A continuación se presentan cuatro de las formas más comunes en las que puede acceder al contexto en una actividad.

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

¿Qué es un contexto? Personalmente, me gusta pensar en Context como el estado de su solicitud en un momento dado. El contexto de la aplicación representa una configuración global o base de su aplicación y una Actividad o Servicio puede basarse en ella y representa una instancia de configuración de su Aplicación o un estado transitivo para ella.

Si observa la fuente de android.content.Context, verá que Context es una clase abstracta y los comentarios sobre la clase son los siguientes:

Interfaz con información global sobre un entorno de aplicación. Esta es una clase abstracta cuya implementación es proporcionada por el sistema Android. Permite el acceso a application-specificrecursos y clases, así como llamadas para application-leveloperaciones tales como actividades de lanzamiento, difusión y recepción de intenciones, etc. Lo que quito de esto es que Context proporciona una implementación común para acceder a nivel de aplicación y nivel de sistema recursos Los recursos a nivel de aplicación pueden estar accediendo a cosas como recursos de cadena [getResources()]o activos [getAssets()]y el recurso a nivel de sistema es cualquier cosa a la que accedaContext.getSystemService().

De hecho, eche un vistazo a los comentarios sobre los métodos y parecen reforzar esta noción:

getSystemService(): Devuelva el identificador a un system-levelservicio por su nombre. La clase del objeto devuelto varía según el nombre solicitado. getResources(): Devuelve una instancia de Recursos para el paquete de su aplicación. getAssets(): Devuelve una instancia de Recursos para el paquete de su aplicación. ¡Vale la pena señalar que en la clase abstracta Context, todos los métodos anteriores son abstractos! Solo una instancia de getSystemService (Class) tiene una implementación y eso invoca un método abstracto. Esto significa que la implementación de estos debe ser proporcionada principalmente por las clases de implementación, que incluyen:

ContextWrapper
Application
Activity
Service
IntentService

Mirando la documentación de la API, la jerarquía de las clases se ve así:

Contexto

El | - ContextWrapper

| - - Aplicación

El | - - ContextThemeWrapper

| - - - - Actividad

El | - - Servicio

| - - - IntentService

Como sabemos que en Contextsí mismo no proporciona ninguna información, nos movemos hacia abajo del árbol y echamos un vistazo al ContextWrappery nos damos cuenta de que tampoco hay mucho allí. Dado que la aplicación se extiende ContextWrapper, tampoco hay mucho que ver allí, ya que no anula la implementación proporcionada por ContextWrapper. Esto significa que la implementación del contexto la proporciona el sistema operativo y está oculta para el API. Puede echar un vistazo a la implementación concreta de Context mirando la fuente de la clase ContextImpl.

Chanaka Weerasinghe
fuente
0

Solo he usado esto y getBaseContextal tostar desde un onClicknovato muy verde para Java y Android. Lo uso cuando mi clicker está directamente en la actividad y tengo que usarlo getBaseContexten un clicker interno anónimo. Supongo que eso es más o menos el truco getBaseContext, tal vez es devolver el contexto de la actividad en la que se oculta la clase interna.

Tony
fuente
1
Esto está mal, está devolviendo el contexto base de la actividad en sí. Para obtener la actividad (la que desea usar como contexto) de una clase interna anónima, use algo como MyActivity.this. Usar el contexto base como lo describe probablemente no causará problemas, pero está mal.
nickmartens1980