Apa perbedaan antara berbagai metode untuk mendapatkan Konteks?

390

Dalam berbagai bit kode Android yang pernah saya lihat:

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

Namun saya tidak dapat menemukan penjelasan yang layak tentang mana yang lebih disukai, dan dalam keadaan apa yang harus digunakan.

Menunjuk dokumentasi tentang hal ini, dan panduan tentang apa yang mungkin pecah jika yang salah dipilih, akan sangat dihargai.

Alnitak
sumber
2
Tautan ini mungkin membantu Anda. Lewati ini ..
Aju

Jawaban:

305

Saya setuju bahwa dokumentasi jarang mengenai Konteks di Android, tetapi Anda dapat mengumpulkan beberapa fakta dari berbagai sumber.

Posting blog ini di blog resmi pengembang Android Google ditulis sebagian besar untuk membantu mengatasi kebocoran memori, tetapi juga memberikan beberapa informasi yang baik tentang konteks:

Dalam aplikasi Android biasa, Anda biasanya memiliki dua jenis Konteks, Aktivitas dan Aplikasi.

Membaca artikel sedikit lebih jauh menceritakan tentang perbedaan antara keduanya dan kapan Anda mungkin ingin mempertimbangkan untuk menggunakan Konteks aplikasi ( Activity.getApplicationContext()) daripada menggunakan konteks Aktivitas this). Pada dasarnya konteks Aplikasi dikaitkan dengan Aplikasi dan akan selalu sama sepanjang siklus hidup aplikasi Anda, di mana konteks Aktivitas dikaitkan dengan aktivitas dan mungkin dapat dihancurkan berkali-kali karena aktivitas dihancurkan selama perubahan orientasi layar dan seperti itu.

Saya tidak dapat menemukan apa pun tentang kapan harus menggunakan getBaseContext () selain pos dari Dianne Hackborn, salah satu insinyur Google yang bekerja pada Android SDK:

Jangan gunakan getBaseContext (), cukup gunakan Konteks yang Anda miliki.

Itu dari posting di newsgroup android-pengembang , Anda mungkin ingin mempertimbangkan untuk mengajukan pertanyaan Anda di sana, karena beberapa orang yang bekerja pada monitor Android sebenarnya yang newsgroup dan menjawab pertanyaan.

Jadi secara keseluruhan tampaknya lebih baik menggunakan konteks aplikasi global bila memungkinkan.

snctln
sumber
13
Ketika saya memiliki aktivitas A yang dapat memulai aktivitas B yang, pada gilirannya, dapat memulai kembali A dengan flag CLEAR_TOP (dan mungkin mengulangi siklus ini berkali-kali) - konteks apa yang harus saya gunakan dalam kasus ini untuk menghindari membangun jejak besar konteks yang dirujuk? Diana mengatakan menggunakan 'ini' daripada getBaseContext, tapi kemudian ... sebagian besar waktu A akan digunakan kembali tetapi ada situasi ketika objek baru untuk A akan dibuat dan kemudian bocor A lama. Jadi sepertinya getBaseContext adalah pilihan paling tepat untuk sebagian besar kasus. Maka tidak jelas mengapa Don't use getBaseContext(). Bisakah seseorang mengklarifikasi ini?
JBM
2
bagaimana cara mengakses objek konteks di dalam kelas yang tidak memperluas Activity?
Cole
1
@Cole, Anda bisa membuat kelas, yang akan kita sebut "ExampleClass" di sini, yang konstruktornya mengambil objek Konteks dan instantiate variabel instance kelas, "appContext". Kemudian, kelas Aktivitas Anda (atau Kelas lainnya dalam hal ini) dapat memanggil metode ExampleClass yang menggunakan variabel instance ExampleClass '"appContext".
Archie1986
54

Inilah yang saya temukan mengenai penggunaan context:

1). Di dalam Activitydirinya sendiri, gunakan thisuntuk menggembungkan tata letak dan menu, mendaftarkan menu konteks, membuat widget, memulai kegiatan lain, membuat yang baru di Intentdalam , membuat Activitypreferensi, atau metode lain yang tersedia di Activity.

Kembangkan tata letak:

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

Mengembang menu:

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

Daftarkan menu konteks:

this.registerForContextMenu(myView);

Widget Instantiate:

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

Mulai sebuah Activity:

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

Instansiasi preferensi:

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

2). Untuk kelas aplikasi-lebar, gunakan getApplicationContext()karena konteks ini ada untuk umur aplikasi.

Ambil nama paket Android saat ini:

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

Bind kelas aplikasi-lebar:

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

3). Untuk Pendengar dan jenis kelas Android lainnya (misalnya ContentObserver), gunakan subtitusi Konteks seperti:

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

di mana thisatau contextadalah konteks kelas (Aktivitas, dll).

Activity substitusi konteks:

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

Substitusi konteks pendengar:

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

ContentObserver substitusi konteks:

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

4). Untuk BroadcastReceiver(termasuk penerima inline / tertanam), gunakan konteks penerima itu sendiri.

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

Inline / tertanam 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). Untuk Layanan, gunakan konteks layanan itu sendiri.

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). Untuk Toasts, umumnya digunakan getApplicationContext(), tetapi jika memungkinkan, gunakan konteks yang dilewatkan dari Aktivitas, Layanan, dll.

Gunakan konteks aplikasi:

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

Gunakan konteks yang diteruskan dari sumber:

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

Dan terakhir, jangan gunakan getBaseContext()seperti yang disarankan oleh pengembang kerangka Android.

UPDATE: Tambahkan contoh Contextpenggunaan.

ChuongPham
sumber
1
Alih-alih mContext, seseorang dapat menggunakan OuterClass.this; lihat komentar di stackoverflow.com/questions/9605459/…
Paul Verest
3
+1 untuk jawaban yang sangat membantu! Saya setuju bahwa jawaban yang diterima baik-baik saja sebagai jawaban yang diterima, tetapi suci molly jawaban ini sangat informatif! Terima kasih atas semua contoh itu, mereka membantu saya untuk lebih memahami penggunaan konteks secara keseluruhan. Saya bahkan menyalin jawaban Anda ke file teks di mesin saya sebagai referensi.
Ryan
13

Saya membaca utas ini beberapa hari yang lalu, bertanya pada diri sendiri pertanyaan yang sama. Keputusan saya setelah membaca ini sederhana: selalu gunakan applicationContext.

Namun, saya mengalami masalah dengan ini, saya menghabiskan beberapa jam untuk menemukannya, dan beberapa detik untuk menyelesaikannya ... (mengubah satu kata ...)

Saya menggunakan LayoutInflater untuk mengembang tampilan yang mengandung Spinner.

Jadi, inilah dua kemungkinan:

1)

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

2)

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

Lalu, saya melakukan sesuatu seperti ini:

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

Apa yang saya perhatikan: Jika Anda membuat LinearLayout Anda dengan applicationContext, maka ketika Anda mengklik pemintal dalam aktivitas Anda, Anda akan memiliki pengecualian yang tidak tertangkap, berasal dari mesin virtual Dalvik (bukan dari kode Anda, itu sebabnya saya telah menghabiskan banyak waktu untuk menemukan di mana kesalahan saya ...).

Jika Anda menggunakan baseContext, maka tidak apa-apa, menu konteks akan terbuka dan Anda dapat memilih di antara pilihan Anda.

Jadi, inilah kesimpulan saya: Saya kira (saya belum mengujinya lebih lanjut) daripada baseContext diperlukan ketika berhadapan dengan contextMenu dalam Aktivitas Anda ...

Tes telah dilakukan pengkodean dengan API 8, dan diuji pada HTC Desire, android 2.3.3.

Saya harap komentar saya sejauh ini tidak membuat Anda bosan, dan berharap yang terbaik untuk Anda semua. Selamat coding ;-)

Mav3656
sumber
Saya selalu menggunakan "ini" saat membuat tampilan dalam suatu aktivitas. Atas dasar bahwa jika aktivitas dimulai kembali, pandangan dibuat kembali dan mungkin ada konteks baru yang digunakan untuk membuat pandangan dari lagi. Kelemahan seperti yang diposting di blog pengembang adalah sementara ImageView rusak, drawable / bitmap yang digunakan mungkin tergantung pada konteks itu. Namun demikian itulah yang saya lakukan saat ini. Mengenai kode di tempat lain di aplikasi (kelas normal) saya hanya menggunakan konteks aplikasi karena tidak spesifik untuk aktivitas atau elemen UI.
JonWillis
6

Pertama, saya setuju bahwa kita harus menggunakan appcontext bila memungkinkan. lalu "ini" dalam aktivitas. Saya tidak pernah memiliki kebutuhan untuk basecontext.

Dalam tes saya, dalam banyak kasus mereka dapat dipertukarkan. Dalam kebanyakan kasus, alasan Anda ingin memahami suatu konteks adalah untuk mengakses file, preferensi, basis data dll. Data ini pada akhirnya dicerminkan sebagai file dalam folder data pribadi aplikasi Anda (/ data / data /). Apa pun konteks yang Anda gunakan, mereka akan dipetakan ke folder / file yang sama sehingga Anda OK.

Itu yang saya amati. Mungkin ada kasus yang harus Anda bedakan.

samsonsu
sumber
Saya membutuhkan basecontext untuk mengatur bahasa aplikasi secara global pada saat startup (ketika itu tidak cocok dengan bahasa default ponsel).
Tina
3

Dalam beberapa kasus, Anda dapat menggunakan konteks Aktivitas di atas konteks aplikasi saat menjalankan sesuatu di utas. Ketika utas menyelesaikan eksekusi dan Anda harus mengembalikan hasilnya kembali ke aktivitas pemanggil, Anda perlu konteks itu dengan seorang pawang.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
Paul
sumber
2

Dengan kata-kata sederhana

getApplicationContext()seperti nama metode yang disarankan akan membuat aplikasi Anda mengetahui detail aplikasi yang luas yang dapat Anda akses dari mana saja di aplikasi. Jadi Anda dapat menggunakan ini dalam penjilidan layanan, pendaftaran siaran dll. Application contextAkan hidup sampai aplikasi keluar.

getActivity()atau thisakan membuat aplikasi Anda mengetahui layar saat ini yang terlihat juga detail tingkat aplikasi yang disediakan oleh application context. Jadi apa pun yang Anda ingin tahu tentang layar saat ini suka Window ActionBar Fragementmangerdan tersedia dengan konteks ini. Pada dasarnya dan Activityluaskan Context. Konteks ini akan hidup sampai komponen (aktivitas) saat ini hidup

arjun
sumber
1

Kebingungan berasal dari kenyataan bahwa ada banyak cara untuk mengakses Konteks, dengan (di permukaan) tidak ada perbedaan yang terlihat. Di bawah ini adalah empat cara paling umum yang dapat Anda gunakan untuk mengakses Konteks dalam Aktivitas.

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

Apa itu Konteks? Saya pribadi suka menganggap Konteks sebagai keadaan aplikasi Anda pada waktu tertentu. Konteks aplikasi mewakili konfigurasi global atau basis dari aplikasi Anda dan Kegiatan atau Layanan dapat membangun atasnya dan mewakili contoh konfigurasi Aplikasi Anda atau keadaan transitif untuk itu.

Jika Anda melihat sumber untuk android.content.Context, Anda melihat bahwa Konteks adalah kelas abstrak dan komentar di kelas adalah sebagai berikut:

Antarmuka ke informasi global tentang lingkungan aplikasi. Ini adalah kelas abstrak yang implementasinya disediakan oleh sistem Android. Hal ini memungkinkan akses ke application-specificsumber daya dan kelas, serta panggilan untuk application-leveloperasi seperti meluncurkan kegiatan, menyiarkan dan menerima maksud, dll. Apa yang saya ambil dari ini adalah bahwa Konteks menyediakan implementasi umum untuk mengakses tingkat aplikasi serta tingkat sistem sumber daya. Sumber daya tingkat aplikasi mungkin mengakses hal-hal seperti sumber daya String [getResources()]atau aset [getAssets()]dan sumber daya tingkat sistem adalah apa pun yang Anda aksesContext.getSystemService().

Faktanya, perhatikan komentar pada metode dan mereka tampaknya memperkuat gagasan ini:

getSystemService(): Kembalikan gagang ke system-levellayanan dengan nama. Kelas objek yang dikembalikan bervariasi menurut nama yang diminta. getResources(): Kembalikan contoh Sumber Daya untuk paket aplikasi Anda. getAssets(): Kembalikan contoh Sumber Daya untuk paket aplikasi Anda. Mungkin pantas untuk menunjukkan bahwa dalam kelas abstrak Konteks, semua metode di atas adalah abstrak! Hanya satu instance dari getSystemService (Class) yang memiliki implementasi dan yang memanggil metode abstrak. Ini berarti, implementasi untuk ini harus disediakan sebagian besar oleh kelas implementasi, yang meliputi:

ContextWrapper
Application
Activity
Service
IntentService

Melihat dokumentasi API, hierarki kelas terlihat seperti ini:

Konteks

| - ContextWrapper

| - - Aplikasi

| - - ContextThemeWrapper

| - - - - Aktivitas

| - - Layanan

| - - - IntentService

Karena kita tahu bahwa Contextitu sendiri tidak memberikan wawasan apa pun, kita bergerak turun pohon dan melihat ContextWrapperdan menyadari bahwa tidak ada banyak juga di sana. Karena Aplikasi meluas ContextWrapper, tidak banyak yang bisa dilihat di sana karena tidak menimpa implementasi yang disediakan oleh ContextWrapper. Ini berarti bahwa implementasi untuk Konteks disediakan oleh OS dan disembunyikan dari API. Anda dapat melihat implementasi konkret untuk Konteks dengan melihat sumber untuk kelas ContextImpl.

Chanaka Weerasinghe
sumber
0

Saya hanya menggunakan ini dan getBaseContextketika memanggang dari onClick(noob sangat hijau untuk Java dan android). Saya menggunakan ini ketika clicker saya langsung dalam aktivitas dan harus digunakan getBaseContextdalam clicker batin anonim. Saya kira itu adalah tipuannya getBaseContext, mungkin mengembalikan konteks kegiatan di mana kelas batin bersembunyi.

Tony
sumber
1
Ini salah, itu mengembalikan konteks dasar dari kegiatan itu sendiri. Untuk mendapatkan aktivitas (yang ingin Anda gunakan sebagai konteks) dari kelas dalam anonim gunakan sesuatu seperti MyActivity.this. Menggunakan konteks dasar seperti yang Anda gambarkan mungkin tidak akan menyebabkan masalah tetapi itu salah.
nickmartens1980