Bağlam elde etmek için çeşitli yöntemler arasındaki fark nedir?


390

Android kodunun çeşitli bitlerinde gördüm:

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

Ancak tercih edilen ve hangi koşullar altında kullanılması gerektiği konusunda iyi bir açıklama bulamıyorum.

Bu konudaki belgelere ve yanlış olanın seçilmesi durumunda neyin kırılacağına dair rehberlik çok takdir edilecektir.


2
Bu bağlantı size yardımcı olabilir. Üzerinden gidin bu ..
Aju

Yanıtlar:


305

Android'de Contexts söz konusu olduğunda dokümantasyonun seyrek olduğunu kabul ediyorum, ancak çeşitli kaynaklardan birkaç gerçeği bir araya getirebilirsiniz.

Bu blog yayını resmi Google Android geliştiricileri blog yardım adres bellek sızıntıları çoğunlukla yazılı, ama aynı zamanda bağlamlarda hakkında bazı iyi bilgi sağlar edildi:

Normal bir Android uygulamasında, genellikle iki tür Bağlam, Etkinlik ve Uygulama vardır.

Makaleyi biraz daha okumak, ikisi arasındaki farkı ve ne zaman Activity.getApplicationContext()Etkinlik bağlamını kullanmak yerine Context ( ) uygulamasını kullanmayı düşünmek isteyeceğinizi anlatır this. Temel olarak Uygulama içeriği Uygulama ile ilişkilidir ve her zaman uygulamanızın yaşam döngüsü boyunca aynı olacaktır, burada Etkinlik içeriği etkinlikle ilişkilendirilir ve muhtemelen ekran yönlendirme değişiklikleri sırasında etkinlik yok edildiğinden birçok kez yok edilebilir ve böyle.

Android SDK'da çalışan Google mühendislerinden biri olan Dianne Hackborn'un gönderisi dışında getBaseContext () 'in ne zaman kullanılacağı hakkında gerçekten hiçbir şey bulamadım:

GetBaseContext () öğesini kullanmayın, sadece sahip olduğunuz Context'i kullanın.

Bu, android geliştiricileri haber grubundaki bir gönderiden , sorunuzu orada da sormayı düşünebilirsiniz, çünkü Android gerçek çalışan bir avuç insan bu haber grubunu izliyor ve soruları cevaplıyor.

Genel olarak, mümkün olduğunda küresel uygulama bağlamının kullanılması tercih edilir.


13
B aktivitesini başlatabilecek bir etkinlik A olduğunda, bu da A'yı CLEAR_TOP bayrağıyla yeniden başlatabilir (ve muhtemelen bu döngüyü birçok kez tekrarlayabilir) - büyük bir iz oluşturmamak için bu durumda hangi bağlamı kullanmalıyım? referans verilen bağlamlar? Diana getBaseContext yerine 'this' kullanmayı söylüyor, ama sonra ... çoğu zaman A tekrar kullanılacak, ancak A için yeni bir nesne yaratılacağı ve eski A'nın sızdığı durumlar var. Yani getBaseContext çoğu durumda en uygun seçim gibi görünüyor. O zaman neden olduğu belli değil Don't use getBaseContext(). Birisi bunu açıklığa kavuşturabilir mi?
JBM

2
Etkinlik genişletmeyen bir sınıfın içindeki bağlam nesnesine nasıl erişilir?
Cole

1
@Cole'da, yapıcısı bir Context nesnesi alan ve "appContext" adlı bir sınıf örneği değişkeni başlatan burada "ÖrnekClass" olarak adlandıracağımız bir sınıf oluşturabilirsiniz. Ardından, Activity sınıfınız (veya bu konudaki herhangi bir Sınıf), SampleClass '"appContext" örnek değişkenini kullanan bir ÖrnekClass yöntemini çağırabilir.
Archie1986

54

İşte kullanımı ile ilgili bulduğum context:

1). ActivityKendi içinde , thismizanpajları ve menüleri şişirmek, bağlam menülerini kaydetmek, widget'ları başlatmak, diğer etkinlikleri başlatmak, Intentbir içinde yeni oluşturmak Activity, bir tercihleri ​​başlatmak veya bir başka yöntemde kullanılabilir Activity.

Şişirme düzeni:

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

Şişirme menüsü:

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

Bağlam menüsünü kaydet:

this.registerForContextMenu(myView);

Örnek widget'ı:

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

Başlayın Activity:

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

Örnek tercihler:

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

2). Uygulama geneli sınıf getApplicationContext()için, uygulamanın ömrü boyunca bu bağlam mevcut olduğundan kullanın .

Mevcut Android paketinin adını alın:

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

Uygulama genelinde bir sınıfı bağlama:

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

3). Dinleyiciler ve diğer Android sınıfları (ör. ContentObserver) için aşağıdaki gibi bir Bağlam değiştirme kullanın:

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

nerede thisveya contextbir sınıfın bağlamıdır (Etkinlik vb.).

Activity bağlam ikamesi:

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

Dinleyici içeriği ikamesi:

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

ContentObserver bağlam ikamesi:

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

4). İçin BroadcastReceiver(inlined / gömülü alıcısı dahil), alıcının kendi bağlamı kullanın.

Dış 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);
        }
    }
}

Eğik / Gömülü 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). Hizmetler için, hizmetin kendi bağlamını kullanın.

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). Tostlar için genellikle getApplicationContext()bir Faaliyet, Hizmet vb.

Uygulamanın bağlamını kullanın:

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

Bir kaynaktan aktarılan içeriği kullan:

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

Ve son olarak, getBaseContext()Android'in çerçeve geliştiricilerinin önerdiği şekilde kullanmayın .

GÜNCELLEME:Context Kullanım örnekleri ekleyin .


1
MContext yerine OuterClass.this; içindeki açıklamalara bakınız stackoverflow.com/questions/9605459/...
Paul Verest

3
Böyle yararlı bir cevap için +1! Kabul edilen cevabın kabul edilen cevap kadar iyi olduğunu kabul ediyorum, ama kutsal molly bu cevap süper bilgilendirici oldu! Tüm bu örnekler için teşekkür ederim, bağlam kullanımını bir bütün olarak daha iyi anlamama yardımcı oldular. Cevabınızı referans olarak makinemdeki bir metin dosyasına bile kopyaladım.
Ryan

13

Bu konuyu birkaç gün önce okudum, kendime aynı soruyu sordum. Bunu okuduktan sonra verdiğim karar basitti: her zaman applicationContext kullan.

Ancak, bununla ilgili bir sorunla karşılaştım, bulmak için birkaç saat ve çözmek için birkaç saniye geçirdim ... (bir kelimeyi değiştirerek ...)

Bir Spinner içeren bir görünümü şişirmek için bir LayoutInflater kullanıyorum.

İşte iki olasılık:

1)

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

2)

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

Sonra böyle bir şey yapıyorum:

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

Ne fark ettim: linearLayout'unuzu applicationContext ile somutlaştırdıysanız, aktivitenizdeki spinner'ı tıkladığınızda, dalvik sanal makineden gelen (kodunuzdan değil, bu yüzden çok geçirdim) yakalanmamış bir istisnanız olacak benim hatamın nerede olduğunu bulmak için zamanın ...).

BaseContext'i kullanırsanız, sorun değil, bağlam menüsü açılır ve seçenekleriniz arasından seçim yapabilirsiniz.

İşte benim sonucum: Sanırım (daha fazla test etmedim) faaliyetinizde contextMenu ile uğraşırken baseContext gereklidir ...

Test, API 8 ile kodlama yapıldı ve bir HTC Desire, android 2.3.3 üzerinde test edildi.

Umarım yorumum sizi şimdiye kadar sıkmamış ve size en iyisini diliyorum. Mutlu kodlama ;-)


Bir etkinlikte görünüm oluştururken her zaman "bunu" kullandım. Faaliyet yeniden başlarsa, görüşler yeniden düzenlenir ve belki de görüşleri tekrar oluşturmak için kullanılacak yeni bir bağlam vardır. Geliştirici blogunda yayınlanan dezavantaj bir ImageView istenirken, kullanılan çizilebilir / bitmap bu bağlamda asılı kalabilir. Yine de şu an yaptığım bu. App başka bir yerde kodu (normal sınıflar) ile ilgili olarak ben sadece herhangi bir etkinlik veya kullanıcı arabirimi öğeleri için özel olarak uygulama bağlamını kullanın.
JonWillis

6

İlk olarak, mümkün olduğunda appcontext kullanmamız gerektiğini kabul ediyorum. sonra faaliyette "bu". Ben asla temel metin için bir ihtiyaç vardı.

Testlerimde, çoğu durumda değiştirilebilirler. Çoğu durumda, bir bağlamı ele geçirme nedeniniz dosyalara, tercihlere, veritabanına vb. Erişmektir. Bu veriler sonunda uygulamanızın özel veri klasöründeki (/ data / data /) dosyalar olarak yansıtılır. Hangi bağlamı kullanırsanız kullanın, bunlar aynı klasöre / dosyalara eşlenir ve böylece sorun olmaz.

Ben de öyle gözlemledim. Belki onları ayırt etmeniz gereken durumlar vardır.


Başlangıçta uygulama dilini global olarak ayarlamak için basecontext'e ihtiyacım var (telefonun varsayılan diliyle eşleşmediğinde).
Tina

3

Bazı durumlarda, bir iş parçacığında bir şey çalıştırırken Etkinlik bağlamını uygulama bağlamında kullanabilirsiniz. İş parçacığı yürütmeyi tamamladığında ve sonucu arayan etkinliğine geri döndürmeniz gerektiğinde, bu bağlamın bir işleyiciyle birlikte olması gerekir.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);

2

Basit bir deyişle

getApplicationContext()yöntem adının önerilmesi, uygulamanızı uygulamanın herhangi bir yerinden erişebileceğiniz geniş uygulama ayrıntılarından haberdar edecektir. Böylece uygulama bağlama, yayın kaydı vb Application contextyararlanabilirsiniz app çıkıncaya kadar hayatta olacak.

getActivity()veya thisuygulamanızı, sağlanan uygulama düzeyi ayrıntılarının da görebildiği geçerli ekrandan haberdar eder application context. Mevcut ekran hakkında bilmek istediğiniz her şey Window ActionBar Fragementmangerbu bağlamda kullanılabilir. Temel olarak ve Activityuzatın Context. Bu bağlam, geçerli bileşen (etkinlik) canlı olana kadar yaşayacak


1

Kafa karışıklığı, (yüzeyde) fark edilebilir bir farklılık olmadan Context'e erişmenin çeşitli yolları olduğu gerçeğinden kaynaklanmaktadır. Bir Etkinlikte Bağlama erişmenin en yaygın yollarından dördü aşağıdadır.

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

Bağlam nedir? Şahsen Context'i herhangi bir zamanda başvurunuzun durumu olarak düşünmeyi seviyorum. Uygulama Bağlamı, uygulamanızın genel veya temel bir yapılandırmasını temsil eder ve bir Etkinlik veya Hizmet bunun üzerine inşa edilebilir ve Uygulamanızın bir yapılandırma örneğini veya bunun için geçişli bir durumu temsil eder.

Android.content.Context kaynağına bakarsanız, Context'in soyut bir sınıf olduğunu ve sınıftaki yorumların aşağıdaki gibi olduğunu görürsünüz:

Bir uygulama ortamı hakkında global bilgilere arayüz. Bu, uygulaması Android sistemi tarafından sağlanan soyut bir sınıftır. application-specificKaynaklara ve sınıflara erişimin yanı sıra application-level, başlatma etkinlikleri, yayınlama ve alma amaçları, vb. Gibi işlemler için çağrılara izin verir . Bundan kaçındığım şey, Context'in uygulama düzeyine ve sistem düzeyine erişmek için ortak bir uygulama sağlamasıdır. kaynaklar. Uygulama düzeyinde kaynaklar, String kaynakları [getResources()]veya varlıkları gibi şeylere erişiyor olabilir [getAssets()]ve sistem düzeyinde kaynak, eriştiğiniz her şeydirContext.getSystemService().

Aslında, yöntemlerle ilgili yorumlara bir göz atın ve bu kavramı güçlendiriyor gibi görünüyorlar:

getSystemService(): Tanıtıcıyı system-levelada göre bir hizmete iade edin . Döndürülen nesnenin sınıfı istenen ada göre değişir. getResources(): Uygulamanızın paketi için bir Kaynaklar örneği döndürün. getAssets(): Uygulamanızın paketi için bir Kaynaklar örneği döndürün. Bağlam soyut sınıfında yukarıdaki yöntemlerin hepsinin soyut olduğunu belirtmeye değer olabilir! GetSystemService (Class) uygulamasının yalnızca bir örneğinde bir uygulama vardır ve bu da soyut bir yöntemi çağırır. Bu, bunların uygulanması çoğunlukla aşağıdakileri içeren uygulama sınıfları tarafından sağlanmalıdır:

ContextWrapper
Application
Activity
Service
IntentService

API belgelerine baktığımızda, sınıfların hiyerarşisi şöyle görünür:

bağlam

| - ContextWrapper

| - - Başvuru

| - - ContextThemeWrapper

| - - - - Etkinlik

| - - Hizmet

| - - - IntentService

Biliyoruz ki bu yana Contextkendisi herhangi bir fikir sağlamıyor, biz ağacı taşımak ve bir göz atın ContextWrapperve ya çok şey olmadığını biliyoruz. Uygulama genişletildiğinden ContextWrapper, tarafından sağlanan uygulamayı geçersiz kılmadığından bakılacak çok şey yoktur ContextWrapper. Bu, Context uygulamasının işletim sistemi tarafından sağlandığı ve API. ContextImpl sınıfının kaynağına bakarak Context için somut uygulamaya bakabilirsiniz.


0

Ben sadece bu ve getBaseContextbir onClick(Java ve android için çok yeşil bir çaylak) toasting kullandım . Tıklayıcım doğrudan etkinlikteyken ve getBaseContextanonim bir iç tıklayıcıda kullanmak zorunda kaldığımda bunu kullanıyorum . Sanırım bu hileli bir getBaseContextşey, belki de iç sınıfın saklandığı faaliyetin bağlamını döndürüyor.


1
Bu yanlış, aktivitenin kendisinin temel bağlamını döndürüyor. Etkinliği (bağlam olarak kullanmak istediğiniz etkinlik) anonim bir iç sınıftan almak için benzer bir şey kullanın MyActivity.this. Temel bağlamı açıkladığınız gibi kullanmak muhtemelen sorunlara neden olmaz, ancak yanlıştır.
nickmartens1980
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.