Existe uma maneira de obter a Context
instância atual dentro de um método estático?
Estou procurando esse caminho porque odeio salvar a instância 'Context' sempre que ela muda.
Context
, pode haver uma maneira melhor de criar o código.
Existe uma maneira de obter a Context
instância atual dentro de um método estático?
Estou procurando esse caminho porque odeio salvar a instância 'Context' sempre que ela muda.
Context
, pode haver uma maneira melhor de criar o código.
Respostas:
Faça isso:
No arquivo Android Manifest, declare o seguinte.
<application android:name="com.xyz.MyApplication">
</application>
Em seguida, escreva a classe:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Agora, em qualquer lugar, ligue MyApplication.getAppContext()
para obter o contexto do aplicativo estaticamente.
static context
variável como volatile
?
A maioria dos aplicativos que deseja um método conveniente para obter o contexto do aplicativo cria sua própria classe, que se estende android.app.Application
.
GUIA
Você pode fazer isso criando primeiro uma classe em seu projeto, como a seguir:
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;
}
}
Em seu AndroidManifest, você deve especificar o nome da sua classe na tag do AndroidManifest.xml:
<application
...
android:name="com.example.App" >
...
</application>
Você pode recuperar o contexto do aplicativo em qualquer método estático usando o seguinte:
public static void someMethod() {
Context context = App.getContext();
}
ATENÇÃO
Antes de adicionar algo como o acima ao seu projeto, considere o que a documentação diz:
Normalmente, não há necessidade de subclassificar Application. Na maioria das situações, singletons estáticos podem fornecer a mesma funcionalidade de uma maneira mais modular. Se o seu singleton precisar de um contexto global (por exemplo, para registrar receptores de transmissão), a função para recuperá-lo poderá receber um Contexto que use internamente Context.getApplicationContext () ao construir o singleton pela primeira vez.
REFLEXÃO
Também há outra maneira de obter o contexto do aplicativo usando a reflexão. Muitas vezes, a reflexão é menosprezada no Android e, pessoalmente, acho que isso não deve ser usado na produção.
Para recuperar o contexto do aplicativo, devemos chamar um método em uma classe oculta ( ActivityThread ) que está disponível desde a API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
Há mais uma classe oculta ( AppGlobals ) que fornece uma maneira de obter o contexto do aplicativo de maneira estática. Ele obtém o contexto usando, ActivityThread
então realmente não há diferença entre o método a seguir e o postado acima:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Feliz codificação!
Supondo que estamos falando sobre como obter o Contexto do Aplicativo, eu o implementei conforme sugerido pelo @Rohit Ghatol, que estende o Aplicativo. O que aconteceu, então, é que não há garantia de que o contexto recuperado dessa maneira sempre será não nulo. No momento em que você precisa, geralmente é porque você deseja inicializar um auxiliar ou obter um recurso que você não pode atrasar no tempo; lidar com o caso nulo não o ajudará. Então eu entendi que estava basicamente lutando contra a arquitetura Android, conforme declarado nos documentos
Nota: Normalmente não há necessidade de subclassificar Aplicativo. Na maioria das situações, singletons estáticos podem fornecer a mesma funcionalidade de uma maneira mais modular. Se o seu singleton precisar de um contexto global (por exemplo, para registrar receptores de transmissão), inclua Context.getApplicationContext () como argumento de Contexto ao chamar o método getInstance () do seu singleton.
e explicado por Dianne Hackborn
A única razão pela qual o Aplicativo existe como algo de que você pode derivar é porque, durante o desenvolvimento anterior à 1.0, um de nossos desenvolvedores de aplicativos me incomodava continuamente por precisar de um objeto de aplicativo de nível superior do qual eles pudessem derivar, para que pudessem ter uma aparência mais "normal". "para o modelo de aplicação, e acabei cedendo. Vou me arrepender para sempre de ceder a esse. :)
Ela também está sugerindo a solução para este problema:
Se o que você deseja é um estado global que possa ser compartilhado em diferentes partes do seu aplicativo, use um singleton. [...] E isso leva mais naturalmente a como você deve gerenciar essas coisas - inicializando-as sob demanda.
então o que eu fiz foi me livrar da extensão Application e passar o contexto diretamente para o getInstance () do auxiliar singleton, enquanto salvava uma referência ao contexto do aplicativo no construtor 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;
}
}
o chamador passará um contexto local para o auxiliar:
Helper.getInstance(myCtx).doSomething();
Portanto, para responder adequadamente a essa pergunta: existem maneiras de acessar o Contexto do Aplicativo estaticamente, mas todas elas devem ser desencorajadas, e você deve preferir passar um contexto local para o getInstance do singleton ().
Para qualquer pessoa interessada, você pode ler uma versão mais detalhada no blog fwd
getInstance(ctx)
. Você tem uma raiz instance
do tipo GC MyHelper
, que possui um campo particular mContext
do tipo Context
, que faz referência ao contexto do aplicativo coletado por meio do contexto passado para getInstance()
. instance
nunca é definido pela segunda vez, nem limpo, para que o GC nunca pegue o contexto do aplicativo mencionado por instance
. Você não vaza nenhuma atividade, por isso é IMO de baixo custo.
this
in Application.onCreate()
, o que torna a resposta aceita melhor.
Não, acho que não existe. Infelizmente, você não consegue ligar getApplicationContext()
de Activity
ou de uma das outras subclasses de Context
. Além disso, esta questão está um pouco relacionada.
Aqui está uma maneira não documentada de obter um Aplicativo (que é um Contexto) de qualquer lugar no thread da interface do usuário. Ele se baseia no método estático oculto ActivityThread.currentApplication()
. Deve funcionar pelo menos no 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
}
Observe que é possível que esse método retorne nulo, por exemplo, quando você chama o método fora do thread da interface do usuário ou o aplicativo não está vinculado ao thread.
Ainda é melhor usar a solução do @RohitGhatol se você puder alterar o código do aplicativo.
Depende do que você está usando o contexto. Eu posso pensar em pelo menos uma desvantagem para esse método:
Se você estiver tentando criar um AlertDialog
com AlertDialog.Builder
, o Application
contexto não funcionará. Eu acredito que você precisa do contexto para o atual Activity
...
Kotlin way :
Manifesto:
<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
}
}
Você pode acessar a propriedade via MyApplication.instance
Se você estiver aberto para usar o RoboGuice , poderá injetar o contexto em qualquer classe que desejar. Aqui está uma pequena amostra de como fazer isso com o RoboGuice 2.0 (beta 4 no momento da redação deste artigo)
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) {
}
}
}
Eu usei isso em algum momento:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Esse é um contexto válido que eu usei para obter serviços do sistema e trabalhei.
Mas, usei apenas em modificações de estrutura / base e não tentei em aplicativos Android.
Um aviso que você deve saber: Ao se registrar para receptores de transmissão com esse contexto, ele não funcionará e você receberá:
java.lang.SecurityException: dado que o pacote de chamadas android não está sendo executado no processo ProcessRecord
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
e obtenha um contexto como
MyApp.mInstance
ou
MyApp.getContext()
Você pode usar o seguinte:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Qualquer outra classe:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
Se você não quiser modificar o arquivo de manifesto, poderá armazenar manualmente o contexto em uma variável estática em sua atividade inicial:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
E apenas defina o contexto quando sua atividade (ou atividades) iniciar:
// 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 as outras respostas, esse é um possível vazamento de memória.
De acordo com esta fonte, você pode obter seu próprio Contexto estendendo o 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();
}
}
Proxicar a implementação do Contexto que simplesmente delega todas as suas chamadas para outro Contexto. Pode ser subclassificado para modificar o comportamento sem alterar o contexto original.
Se, por algum motivo, você desejar o contexto Aplicativo em qualquer classe, não apenas aqueles que estendem o aplicativo / atividade, talvez para algumas classes de fábrica ou auxiliares. Você pode adicionar o seguinte singleton ao seu aplicativo.
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;
}
}
em seguida, inicialize-o no onCreate da sua classe de aplicativo com
GlobalAppContextSingleton.getInstance().initialize(this);
use-o em qualquer lugar chamando
GlobalAppContextSingleton.getInstance().getApplicationContext()
No entanto, não recomendo essa abordagem para nada além do contexto do aplicativo. Como isso pode causar vazamentos de memória.
Eu uso uma variação do padrão de design Singleton para me ajudar com isso.
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;
}
}
Então eu ligo ApplicationContextSingleton.setContext( this );
no meu activity.onCreate () e ApplicationContextSingleton.setContext( null );
em onDestroy () ;
Acabei de lançar um framework inspirado em jQuery para Android chamado Vapor API que visa simplificar o desenvolvimento de aplicativos.
A Central $
classe de fachada mantém um WeakReference
(link para o incrível post do blog sobre Java de Ethan Nicholas) sobre o Activity
contexto atual que você pode recuperar chamando:
$.act()
UMA WeakReference
mantém uma referência sem impedir que a coleta de lixo recupere o objeto original, para que você não tenha problemas com vazamentos de memória.
A desvantagem, claro, é que você corre o risco de $.act()
retornar nulo. Ainda não me deparei com esse cenário, talvez seja apenas um risco mínimo, que vale a pena mencionar.
Você também pode definir o contexto manualmente se não estiver usando VaporActivity
como sua Activity
classe:
$.act(Activity);
Além disso, grande parte dos estrutura da API Vapor usa esse contexto armazenado de forma inerente, o que pode significar que você não precisa armazená-lo sozinho se decidir usar a estrutura. Confira o site para mais informações e amostras.
Espero que ajude :)
A resposta de Rohit parece correta. No entanto, saiba que o "Instant Run" do AndroidStudio depende de não ter static Context
atributos no seu código, tanto quanto eu saiba.
no Kotlin, colocar o Contexto / Contexto do Aplicativo no objeto complementar ainda gera aviso Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
ou se você usar algo como isto:
companion object {
lateinit var instance: MyApp
}
É simplesmente enganar o fiapo para não descobrir o vazamento de memória, a instância do App ainda pode produzir vazamento de memória, pois a classe Application e seu descendente são um Contexto.
Como alternativa, você pode usar a interface funcional ou as propriedades funcionais para ajudá-lo a obter o contexto do aplicativo.
Basta criar uma classe de objeto:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
ou você pode usá-lo com mais segurança usando o tipo anulável:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
e na sua classe App, adicione esta linha:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
e no seu manifesto, declare o nome do aplicativo como . MyApp
<application
android:name=".MyApp"
Quando você quiser obter o contexto, basta ligar para:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Espero que ajude.
Tente algo como isto
import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private static Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = getApplicationContext(); } public static void getContext(View view){ Toast.makeText(context, "Got my context!", Toast.LENGTH_LONG).show(); } }