Geçerli Context
örneği statik bir yöntemin içine almanın bir yolu var mı ?
Ben her zaman değiştiğinde 'Bağlam' örneği kaydetmek nefret çünkü bu şekilde arıyorum.
Context
gerekiyorsa, kodu tasarlamanın daha iyi bir yolu olabilir.
Geçerli Context
örneği statik bir yöntemin içine almanın bir yolu var mı ?
Ben her zaman değiştiğinde 'Bağlam' örneği kaydetmek nefret çünkü bu şekilde arıyorum.
Context
gerekiyorsa, kodu tasarlamanın daha iyi bir yolu olabilir.
Yanıtlar:
Bunu yap:
Android Manifest dosyasında aşağıdakileri bildirin.
<application android:name="com.xyz.MyApplication">
</application>
Sonra sınıfı yazın:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Şimdi her yerde MyApplication.getAppContext()
uygulama içeriğinizi statik olarak almak için arayın .
static context
değişkeni şöyle bildirmeliyiz volatile
?
Uygulama bağlamını elde etmek için uygun bir yöntem isteyen uygulamaların çoğu, kendi sınıflarını genişletir android.app.Application
.
KILAVUZ
İlk önce projenizde aşağıdaki gibi bir sınıf oluşturarak bunu yapabilirsiniz:
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;
}
}
Ardından, AndroidManifest'inizde AndroidManifest.xml etiketinde sınıfınızın adını belirtmelisiniz:
<application
...
android:name="com.example.App" >
...
</application>
Daha sonra, aşağıdakileri kullanarak uygulama içeriğini herhangi bir statik yöntemle alabilirsiniz:
public static void someMethod() {
Context context = App.getContext();
}
UYARI
Projenize yukarıdaki gibi bir şey eklemeden önce belgelerin ne dediğini göz önünde bulundurmalısınız:
Normalde Uygulamayı alt sınıfa ayırmaya gerek yoktur. Çoğu durumda, statik tek tonlar aynı işlevselliği daha modüler bir şekilde sağlayabilir. Singletonunuzun genel bir bağlama ihtiyacı varsa (örneğin yayın alıcılarını kaydetmek için), onu alma işlevine, singleton'u ilk oluştururken dahili olarak Context.getApplicationContext () kullanan bir Bağlam verilebilir.
YANSIMASI
Yansımayı kullanarak uygulama bağlamını almanın başka bir yolu da vardır. Yansıtma Android'de sık sık aşağıya bakar ve kişisel olarak bunun üretimde kullanılmaması gerektiğini düşünüyorum.
Uygulama içeriğini almak için API 1'den beri mevcut olan gizli bir sınıfta ( ActivityThread ) bir yöntem çağırmalıyız :
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
Uygulama içeriğini statik bir şekilde elde etmenin bir yolunu sağlayan bir tane daha gizli sınıf ( AppGlobals ) vardır. Bağlamı kullanarak alır, ActivityThread
bu nedenle aşağıdaki yöntem ve yukarıda yayınlanan yöntem arasında hiçbir fark yoktur:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Mutlu kodlama!
Uygulama Bağlamını almaktan bahsettiğimizi varsayarak, @Rohit Ghatol Genişletme Uygulaması'nın önerdiği şekilde uyguladım. O zaman olanlar, bu şekilde alınan bağlamın her zaman sıfır olmayacağının garantisi yoktur. İhtiyacınız olan zamanda, genellikle bir yardımcıyı başlatmak veya bir kaynak almak istediğinizde, zaman içinde geciktiremeyeceğiniz için; null durumunun ele alınması size yardımcı olmaz. Dokümanlarda belirtildiği gibi temelde Android mimarisine karşı savaştığımı anladım
Not: Normalde Uygulamayı alt sınıfa ayırmaya gerek yoktur. Çoğu durumda, statik tek tonlar aynı işlevselliği daha modüler bir şekilde sağlayabilir. Singleton'unuzun genel bir bağlama ihtiyacı varsa (örneğin yayın alıcılarını kaydetmek için), singletonunuzun getInstance () yöntemini çağırırken Context.getApplicationContext () öğesini Context bağımsız değişkeni olarak ekleyin.
ve Dianne Hackborn tarafından açıklandı
Uygulamanın türetebileceğiniz bir şey olarak var olmasının tek nedeni, 1.0 öncesi geliştirme sırasında uygulama geliştiricilerimizden birinin sürekli olarak türetebildikleri üst düzey bir uygulama nesnesine ihtiyaç duyma konusunda beni sıkmasıydı. "onlara uygulama modeli, ve sonunda teslim oldum. Sonsuza dek bu konuda vermek pişman olacak. :)
Ayrıca bu sorunun çözümünü öneriyor:
İstediğiniz şey, uygulamanızın farklı bölümlerinde paylaşılabilecek bir küresel durumsa, tek birton kullanın. [...] Ve bu daha doğal olarak bu şeyleri nasıl yönetmeniz gerektiğine yol açar - talep üzerine onları başlatır.
yani yaptığım şey Uygulama'yı genişletmekten kurtulmak ve bağlamı doğrudan singleton yardımcısının getInstance () öğesine aktarmak ve özel kurucudaki uygulama bağlamına bir referans kaydetmektir:
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;
}
}
arayan kişi yerel bir bağlamı yardımcıya iletir:
Helper.getInstance(myCtx).doSomething();
Bu nedenle, bu soruyu doğru bir şekilde cevaplamak için: Uygulama Bağlamına statik olarak erişmenin yolları vardır, ancak hepsinin cesareti kırılmalı ve yerel bağlamı singleton'un getInstance () öğesine geçirmeyi tercih etmelisiniz.
İlgilenen herkes için daha ayrıntılı bir sürümü fwd blog'da okuyabilirsiniz
getInstance(ctx)
. İletilen bağlam aracılığıyla toplanan uygulama bağlamına başvuran özel bir tür alanı olan bir GC kök instance
türüne sahipsiniz . hiçbir zaman ikinci bir kez ayarlanmaz veya silinmez, bu nedenle GC asla başvurulan uygulama metnini yakalamaz . Herhangi bir etkinlik sızdırmazsınız, bu nedenle düşük maliyetli IMO'dur. MyHelper
mContext
Context
getInstance()
instance
instance
this
içinde Application.onCreate()
daha iyi kabul cevabı yapar.
Hayır, sanmıyorum. Ne yazık ki, Sıkıştınız çağrı konum getApplicationContext()
gelen Activity
veya diğer alt sınıflarından biri Context
. Ayrıca, bu soru biraz ilişkilidir.
İşte bir olan belgesiz bir almanın yolu Uygulama UI iş parçacığı içinde herhangi bir yerden (bir Bağlam olan). Gizli statik yönteme dayanır ActivityThread.currentApplication()
. En azından Android 4.x üzerinde çalışmalıdır.
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
}
Bu yöntemin null döndürmesinin mümkün olduğunu unutmayın; örneğin, yöntemi UI iş parçacığının dışında çağırdığınızda veya uygulama iş parçacığına bağlı değilse.
Uygulama kodunu değiştirebiliyorsanız @RohitGhatol çözümünü kullanmak daha iyidir .
Bağlamı ne için kullandığınıza bağlıdır. Bu yöntemin en az bir dezavantajını düşünebilirim:
Bir oluşturmaya çalışıyorsanız AlertDialog
ile AlertDialog.Builder
, Application
bağlam çalışmaz. Mevcut için bağlama ihtiyacınız olduğunu düşünüyorum Activity
...
Kotlin yolu :
Belirgin:
<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
}
}
Daha sonra mülke şu adresten erişebilirsiniz: MyApplication.instance
RoboGuice'i kullanmaya açıksanız , içeriği istediğiniz sınıfa enjekte edebilirsiniz. RoboGuice 2.0 ile nasıl yapılacağına dair küçük bir örnek (bu yazının yazıldığı sırada beta 4)
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) {
}
}
}
Bunu bir noktada kullandım:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Bu, sistem hizmetleri almak için kullandığım ve çalıştığım geçerli bir bağlam.
Ancak, yalnızca çerçeve / taban değişikliklerinde kullandım ve Android uygulamalarında denemedim.
Bilmeniz gereken bir uyarı : Bu bağlamda yayın alıcılarına kaydolurken çalışmaz ve şunları elde edersiniz:
java.lang.SecurityException: Verilen arayan paketi android ProcessRecord çalışmıyor
Aşağıdakileri kullanabilirsiniz:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Başka herhangi bir sınıf:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
Manifest dosyasını değiştirmek istemiyorsanız, içeriği ilk etkinliğinizde statik bir değişkende manuel olarak saklayabilirsiniz:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
Ayrıca, etkinliğiniz (veya etkinlikleriniz) başladığında içeriği ayarlayın:
// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Not: Diğer tüm cevaplar gibi, bu potansiyel bir bellek sızıntısıdır.
Bu kaynağa göre ContextWrapper'ı genişleterek kendi Bağlamınızı elde edebilirsiniz.
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();
}
}
Tüm çağrılarını başka bir Bağlam'a delege eden Bağlamın proxy uygulaması. Orijinal Bağlamı değiştirmeden davranışı değiştirmek için alt sınıflara ayrılabilir.
Herhangi bir nedenle, yalnızca fabrika / yardımcı sınıflar için değil, yalnızca uygulamayı / etkinliği genişletenler için değil, herhangi bir sınıfta Uygulama bağlamı istiyorsanız. Aşağıdaki singletonu uygulamanıza ekleyebilirsiniz.
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;
}
}
ardından uygulama sınıfınızın onCreate öğesinde
GlobalAppContextSingleton.getInstance().initialize(this);
arayarak her yerde kullanın
GlobalAppContextSingleton.getInstance().getApplicationContext()
Ancak bu yaklaşımı uygulama bağlamından başka bir şeye tavsiye etmiyorum. Bellek sızıntılarına neden olabileceğinden.
Bu konuda bana yardımcı olması için Singleton tasarım modelinin bir varyasyonunu kullanıyorum.
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;
}
}
Sonra diyoruz ApplicationContextSingleton.setContext( this );
benim de () activity.onCreate ve ApplicationContextSingleton.setContext( null );
içinde OnDestroy () ;
Uygulama geliştirmeyi basitleştirmeyi amaçlayan Vapor API adlı Android için jQuery'den ilham alan bir çerçeve yayınladım .
Merkezi $
cephe sınıfı , arayarak alabileceğiniz WeakReference
mevcut Activity
bağlama bir (Ethan Nicholas tarafından bu konuda harika Java blog yazısı bağlantısı) sağlar :
$.act()
A WeakReference
, orijinal nesneyi geri almak için çöp toplama işlemini engellemeden bir referans tutar, bu nedenle bellek sızıntılarında sorun yaşamamanız gerekir.
Tabii ki dezavantajı, $.act()
null dönebilecek risk çalıştırmak olduğunu . Bu senaryoya henüz rastlamadım, bu yüzden belki de söz etmeye değer minimum bir risk.
Sınıfınız VaporActivity
olarak kullanmıyorsanız bağlamı manuel olarak da ayarlayabilirsiniz Activity
:
$.act(Activity);
Ayrıca, Vapor API çerçevesinin çoğu, bu depolanmış içeriği doğal olarak kullanır; bu, çerçeveyi kullanmaya karar verirseniz, kendiniz saklamanız gerekmeyeceği anlamına gelebilir. Daha fazla bilgi ve örnek için siteye göz atın .
Umarım bu yardımcı olur :)
Rohit'in cevabı doğru görünüyor. Ancak, AndroidStudio'nun "Anında Çalıştır" öğesinin static Context
, bildiğim kadarıyla kodunuzda nitelik bulunmamasına bağlı olduğunu unutmayın.
Kotlin'de, Bağlam / Uygulama Bağlamını tamamlayıcı nesneye koymak hala uyarı üretir Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
veya böyle bir şey kullanırsanız:
companion object {
lateinit var instance: MyApp
}
Bellek sızıntısını keşfetmemek için tiftiği kandırmak, Uygulama örneği hala bellek sızıntısı üretebilir, çünkü Application sınıfı ve onun torunu bir Bağlamdır.
Alternatif olarak, uygulama içeriğinizi almanıza yardımcı olması için işlevsel arayüzü veya İşlevsel özellikleri kullanabilirsiniz.
Yalnızca bir nesne sınıfı oluşturun:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
veya nullable tipini kullanarak daha güvenli kullanabilirsiniz:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
ve App sınıfınıza şu satırı ekleyin:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
ve bildiriminizde uygulama adını . MyApp
<application
android:name=".MyApp"
Bağlamı almak istediğinizde sadece şunu arayın:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Umarım yardımcı olur.
Böyle bir şey dene
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(); } }