Apakah ada cara untuk mendapatkan Context
instance saat ini di dalam metode statis?
Saya mencari cara itu karena saya benci menyimpan contoh 'Konteks' setiap kali itu berubah.
Context
, maka mungkin ada cara yang lebih baik untuk merancang kode.
Apakah ada cara untuk mendapatkan Context
instance saat ini di dalam metode statis?
Saya mencari cara itu karena saya benci menyimpan contoh 'Konteks' setiap kali itu berubah.
Context
, maka mungkin ada cara yang lebih baik untuk merancang kode.
Jawaban:
Melakukan hal ini:
Dalam file Android Manifest, nyatakan yang berikut ini.
<application android:name="com.xyz.MyApplication">
</application>
Kemudian tulis kelas:
public class MyApplication extends Application {
private static Context context;
public void onCreate() {
super.onCreate();
MyApplication.context = getApplicationContext();
}
public static Context getAppContext() {
return MyApplication.context;
}
}
Sekarang di mana-mana panggilan MyApplication.getAppContext()
untuk mendapatkan konteks aplikasi Anda secara statis.
static context
variabel ini sebagai volatile
?
Mayoritas aplikasi yang menginginkan metode yang nyaman untuk mendapatkan konteks aplikasi membuat kelas mereka sendiri yang meluas android.app.Application
.
PANDUAN
Anda dapat melakukannya dengan terlebih dahulu membuat kelas di proyek Anda seperti berikut:
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;
}
}
Kemudian, di AndroidManifest Anda, Anda harus menentukan nama kelas Anda di tag AndroidManifest.xml:
<application
...
android:name="com.example.App" >
...
</application>
Anda kemudian dapat mengambil konteks aplikasi dalam metode statis menggunakan yang berikut:
public static void someMethod() {
Context context = App.getContext();
}
PERINGATAN
Sebelum menambahkan sesuatu seperti di atas ke proyek Anda, Anda harus mempertimbangkan apa yang dikatakan dokumentasi:
Biasanya tidak perlu untuk subkelas Aplikasi. Dalam sebagian besar situasi, lajang statis dapat menyediakan fungsionalitas yang sama dengan cara yang lebih modular. Jika singleton Anda memerlukan konteks global (misalnya untuk mendaftarkan penerima siaran), fungsi untuk mengambilnya dapat diberikan Konteks yang secara internal menggunakan Context.getApplicationContext () ketika pertama kali membangun singleton.
REFLEKSI
Ada juga cara lain untuk mendapatkan konteks aplikasi menggunakan refleksi. Refleksi sering dipandang rendah di Android dan saya pribadi berpikir ini tidak boleh digunakan dalam produksi.
Untuk mengambil konteks aplikasi kita harus memanggil metode pada kelas tersembunyi ( ActivityThread ) yang telah tersedia sejak API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
Ada satu lagi kelas tersembunyi ( AppGlobals ) yang menyediakan cara untuk mendapatkan konteks aplikasi secara statis. Itu mendapatkan konteks menggunakan ActivityThread
sehingga benar-benar tidak ada perbedaan antara metode berikut dan yang diposting di atas:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Selamat coding!
Dengan asumsi kita sedang berbicara tentang mendapatkan Konteks Aplikasi, saya menerapkannya seperti yang disarankan oleh @Rohit Ghatol memperluas Aplikasi. Apa yang terjadi kemudian, adalah bahwa tidak ada jaminan bahwa konteks yang diambil sedemikian rupa akan selalu non-nol. Pada saat Anda membutuhkannya, biasanya karena Anda ingin menginisialisasi pembantu, atau mendapatkan sumber daya, Anda tidak dapat menunda waktu; menangani case null tidak akan membantu Anda. Jadi saya mengerti bahwa saya pada dasarnya berjuang melawan arsitektur Android, seperti yang dinyatakan dalam dokumen
Catatan: Biasanya tidak perlu untuk subkelas Aplikasi. Dalam kebanyakan situasi, lajang statis dapat menyediakan fungsionalitas yang sama dengan cara yang lebih modular. Jika singleton Anda memerlukan konteks global (misalnya untuk mendaftarkan penerima siaran), sertakan Context.getApplicationContext () sebagai argumen Konteks saat menggunakan metode getInstance () singleton Anda.
dan dijelaskan oleh Dianne Hackborn
Satu-satunya alasan Aplikasi ada sebagai sesuatu yang dapat Anda peroleh adalah karena selama pengembangan pra-1.0 salah satu pengembang aplikasi kami terus-menerus menggangguku tentang perlunya memiliki objek aplikasi tingkat atas yang dapat mereka peroleh sehingga mereka dapat memiliki yang lebih "normal". "kepada mereka model aplikasi, dan saya akhirnya menyerah. Saya akan selamanya menyesal menyerah pada yang satu itu. :)
Dia juga menyarankan solusi untuk masalah ini:
Jika yang Anda inginkan adalah keadaan global yang dapat dibagikan di berbagai bagian aplikasi Anda, gunakan singleton. [...] Dan ini mengarah secara lebih alami pada bagaimana Anda seharusnya mengelola hal-hal ini - menginisialisasinya sesuai permintaan.
jadi apa yang saya lakukan adalah menyingkirkan memperpanjang Aplikasi, dan meneruskan konteks langsung ke getInstance () pembantu tunggal, sambil menyimpan referensi ke konteks aplikasi di konstruktor pribadi:
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;
}
}
penelepon kemudian akan memberikan konteks lokal kepada penolong:
Helper.getInstance(myCtx).doSomething();
Jadi, untuk menjawab pertanyaan ini dengan benar: ada cara untuk mengakses Konteks Aplikasi secara statis, tetapi mereka semua harus berkecil hati, dan Anda harus lebih suka meneruskan konteks lokal ke getInstance singleton ().
Bagi siapa pun yang tertarik, Anda dapat membaca versi yang lebih rinci di blog fwd
getInstance(ctx)
. Anda memiliki akar instance
tipe GC MyHelper
, yang memiliki bidang mContext
tipe pribadi Context
, yang merujuk konteks aplikasi yang dikumpulkan melalui konteks yang diteruskan ke getInstance()
. instance
tidak pernah diset kedua kalinya, juga tidak dihapus, sehingga GC tidak akan pernah menangkap konteks aplikasi yang dirujuk oleh instance
. Anda tidak membocorkan kegiatan apa pun sehingga IMO biaya rendah.
this
di Application.onCreate()
, yang membuat jawaban yang diterima lebih baik.
Tidak, saya kira tidak ada. Sayangnya, Anda mengalami panggilan macet getApplicationContext()
dari Activity
atau salah satu subclass dari Context
. Juga, ini pertanyaan agak terkait.
Berikut adalah cara tidak berdokumen untuk mendapatkan Aplikasi (yang merupakan Konteks) dari mana saja di utas UI. Itu bergantung pada metode statis tersembunyi ActivityThread.currentApplication()
. Ini harus bekerja setidaknya di 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
}
Perhatikan bahwa mungkin untuk metode ini mengembalikan nol, misalnya ketika Anda memanggil metode di luar utas UI, atau aplikasi tidak terikat pada utas.
Masih lebih baik menggunakan solusi @RohitGhatol jika Anda dapat mengubah kode Aplikasi.
Tergantung pada apa Anda menggunakan konteks untuk. Saya dapat memikirkan setidaknya satu kelemahan dari metode itu:
Jika Anda mencoba membuat AlertDialog
dengan AlertDialog.Builder
, Application
konteksnya tidak akan berfungsi. Saya percaya Anda perlu konteks untuk saat ini Activity
...
Cara Kotlin :
Nyata:
<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
}
}
Anda kemudian dapat mengakses properti melalui MyApplication.instance
Jika Anda terbuka untuk menggunakan RoboGuice , Anda dapat menyuntikkan konteksnya ke kelas yang Anda inginkan. Berikut adalah contoh kecil bagaimana melakukannya dengan RoboGuice 2.0 (beta 4 pada saat penulisan ini)
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) {
}
}
}
Saya telah menggunakan ini di beberapa titik:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Ini adalah konteks yang valid yang saya gunakan untuk mendapatkan layanan sistem dan bekerja.
Tapi, saya menggunakannya hanya dalam modifikasi framework / base dan tidak mencobanya di aplikasi Android.
Sebuah peringatan bahwa Anda harus tahu: Ketika mendaftar untuk penerima siaran dengan konteks ini, tidak akan bekerja dan Anda akan mendapatkan:
java.lang.SecurityException: Paket penelepon yang diberikan android tidak berjalan dalam proses ProcessRecord
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
dan dapatkan Context like
MyApp.mInstance
atau
MyApp.getContext()
Anda dapat menggunakan yang berikut ini:
MainActivity.this.getApplicationContext();
MainActivity.java:
...
public class MainActivity ... {
static MainActivity ma;
...
public void onCreate(Bundle b) {
super...
ma=this;
...
Kelas lain:
public ...
public ANY_METHOD... {
Context c = MainActivity.ma.getApplicationContext();
Jika Anda tidak ingin mengubah file manifes, Anda dapat secara manual menyimpan konteks dalam variabel statis di aktivitas awal Anda:
public class App {
private static Context context;
public static void setContext(Context cntxt) {
context = cntxt;
}
public static Context getContext() {
return context;
}
}
Dan atur konteks saat aktivitas (atau aktivitas) Anda dimulai:
// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Catatan: Seperti semua jawaban lain, ini adalah potensi kebocoran memori.
Menurut sumber ini Anda bisa mendapatkan Konteks Anda sendiri dengan memperluas 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();
}
}
Proksi implementasi Konteks yang hanya mendelegasikan semua panggilannya ke Konteks lain. Dapat disubklasifikasikan untuk memodifikasi perilaku tanpa mengubah Konteks asli.
Jika Anda karena suatu alasan menginginkan konteks Aplikasi di kelas mana pun, bukan hanya yang memperpanjang aplikasi / aktivitas, mungkin untuk beberapa kelas pabrik atau pembantu. Anda dapat menambahkan singleton berikut ke aplikasi Anda.
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;
}
}
kemudian inisialisasi di onCreate dengan kelas aplikasi Anda
GlobalAppContextSingleton.getInstance().initialize(this);
gunakan di mana saja dengan menelepon
GlobalAppContextSingleton.getInstance().getApplicationContext()
Saya tidak merekomendasikan pendekatan ini untuk apa pun kecuali konteks aplikasi. Karena dapat menyebabkan kebocoran memori.
Saya menggunakan variasi pola desain Singleton untuk membantu saya dalam hal ini.
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;
}
}
Saya kemudian menelepon ApplicationContextSingleton.setContext( this );
di saya activity.onCreate () dan ApplicationContextSingleton.setContext( null );
di onDestroy () ;
Saya baru saja merilis kerangka kerja yang terinspirasi jQuery untuk Android bernama Vapor API yang bertujuan untuk membuat pengembangan aplikasi lebih sederhana.
Kelas $
fasad pusat mempertahankan WeakReference
(tautan ke posting blog Java yang mengagumkan tentang ini oleh Ethan Nicholas) ke Activity
konteks saat ini yang dapat Anda ambil dengan menelepon:
$.act()
A WeakReference
menyimpan referensi tanpa mencegah pengumpulan sampah merebut kembali objek asli, jadi Anda seharusnya tidak memiliki masalah dengan kebocoran memori.
Kelemahannya tentu saja adalah Anda menjalankan risiko yang $.act()
bisa mengembalikan nol. Saya belum menemukan skenario ini, jadi itu mungkin hanya risiko minimal, layak disebut.
Anda juga dapat mengatur konteks secara manual jika Anda tidak menggunakan VaporActivity
sebagai Activity
kelas Anda :
$.act(Activity);
Juga, banyak kerangka kerja Vapor API menggunakan konteks tersimpan ini secara inheren yang mungkin berarti Anda tidak perlu menyimpannya sama sekali jika Anda memutuskan untuk menggunakan kerangka kerja tersebut. Lihatlah situs untuk informasi lebih lanjut dan sampel.
Saya harap itu membantu :)
Jawaban Rohit sepertinya benar. Namun, perlu diketahui bahwa "Instant Run" AndroidStudio tergantung pada tidak memiliki static Context
atribut dalam kode Anda, sejauh yang saya tahu.
di Kotlin, menempatkan Konteks / Konteks Aplikasi di objek pendamping masih menghasilkan peringatan Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)
atau jika Anda menggunakan sesuatu seperti ini:
companion object {
lateinit var instance: MyApp
}
Ini hanya menipu serat untuk tidak menemukan kebocoran memori, contoh App masih dapat menghasilkan kebocoran memori, karena kelas Aplikasi dan turunannya adalah sebuah Konteks.
Atau, Anda dapat menggunakan antarmuka fungsional atau properti Fungsional untuk membantu Anda mendapatkan konteks aplikasi Anda.
Cukup buat kelas objek:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
atau Anda bisa menggunakannya lebih aman menggunakan jenis nullable:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
dan di kelas Aplikasi Anda tambahkan baris ini:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
dan di manifes Anda menyatakan nama aplikasi ke . MyApp
<application
android:name=".MyApp"
Ketika Anda ingin mendapatkan konteksnya, hubungi:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Semoga ini bisa membantu.
Coba sesuatu seperti ini
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(); } }