Was ist der Unterschied zwischen den verschiedenen Methoden, um einen Kontext zu erhalten?

390

In verschiedenen Teilen des Android-Codes habe ich gesehen:

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

Ich kann jedoch keine vernünftige Erklärung dafür finden, welche vorzuziehen ist und unter welchen Umständen welche verwendet werden sollten.

Hinweise zur Dokumentation hierzu und Hinweise dazu, was bei Auswahl des falschen Dokuments möglicherweise kaputt geht, sind sehr willkommen.

Alnitak
quelle
2
Dieser Link könnte Ihnen helfen. Gehen Sie durch diese ..
Aju

Antworten:

305

Ich bin damit einverstanden, dass die Dokumentation in Bezug auf Kontexte in Android spärlich ist, aber Sie können einige Fakten aus verschiedenen Quellen zusammenstellen.

Dieser Blog-Beitrag im offiziellen Google Android-Entwicklerblog wurde hauptsächlich geschrieben, um Speicherlecks zu beheben. Er enthält jedoch auch einige gute Informationen zu Kontexten:

In einer normalen Android-Anwendung gibt es normalerweise zwei Arten von Kontext: Aktivität und Anwendung.

Wenn Sie den Artikel ein wenig weiter lesen, erfahren Sie mehr über den Unterschied zwischen den beiden und wann Sie möglicherweise die Verwendung des Anwendungskontexts ( Activity.getApplicationContext()) anstelle des Aktivitätskontexts in Betracht ziehen möchtenthis . Grundsätzlich ist der Anwendungskontext mit der Anwendung verknüpft und bleibt während des gesamten Lebenszyklus Ihrer App immer derselbe, wobei der Aktivitätskontext mit der Aktivität verknüpft ist und möglicherweise mehrmals zerstört werden kann, wenn die Aktivität während Änderungen der Bildschirmausrichtung und zerstört wird eine solche.

Ich konnte wirklich nichts darüber finden, wann ich getBaseContext () verwenden sollte, außer einem Beitrag von Dianne Hackborn, einer der Google-Ingenieure, die am Android SDK arbeiten:

Verwenden Sie nicht getBaseContext (), sondern nur den Kontext, den Sie haben.

Das war aus einem Beitrag in der Newsgroup der Android-Entwickler. Vielleicht möchten Sie auch dort Ihre Frage stellen, da eine Handvoll Leute, die an Android arbeiten, diese Newsgroup überwachen und Fragen beantworten.

Insgesamt scheint es daher vorzuziehen, wenn möglich den globalen Anwendungskontext zu verwenden.

snctln
quelle
13
Wenn ich eine Aktivität A habe, die Aktivität B starten kann, die wiederum A mit dem Flag CLEAR_TOP neu starten kann (und diesen Zyklus möglicherweise viele Male wiederholt) - welchen Kontext sollte ich in diesem Fall verwenden, um zu vermeiden, dass eine große Spur von aufgebaut wird referenzierte Kontexte? Diana sagt, dass sie 'this' anstelle von getBaseContext verwendet, aber dann ... wird A meistens wiederverwendet, aber es gibt Situationen, in denen ein neues Objekt für A erstellt wird und dann das alte A leckt. Daher scheint getBaseContext in den meisten Fällen die beste Wahl zu sein. Dann ist nicht klar warum Don't use getBaseContext(). Könnte jemand das klarstellen?
JBM
2
Wie würde man auf das Kontextobjekt innerhalb einer Klasse zugreifen, die die Aktivität nicht erweitert?
Cole
1
@Cole, Sie könnten eine Klasse erstellen, die wir hier "ExampleClass" nennen, deren Konstruktor ein Context-Objekt verwendet und eine Klasseninstanzvariable "appContext" instanziiert. Anschließend kann Ihre Activity-Klasse (oder eine andere Klasse) eine ExampleClass-Methode aufrufen, die die Instanzvariable "appContext" von ExampleClass verwendet.
Archie1986
54

Folgendes habe ich in Bezug auf die Verwendung von gefunden context:

1). ActivityVerwenden Sie in einem selbst das thisAufblasen von Layouts und Menüs, das Registrieren von Kontextmenüs, das Instanziieren von Widgets, das Starten anderer Aktivitäten, das Erstellen neuer Aktivitäten Intentin einem Activity, das Instanziieren von Einstellungen oder andere in einem verfügbare Methoden Activity.

Layout aufblasen:

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

Menü aufblasen:

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

Kontextmenü registrieren:

this.registerForContextMenu(myView);

Widget instanziieren:

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

Starten Sie eine Activity:

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

Präferenzen instanziieren:

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

2). Verwenden Sie für anwendungsweite Klassen, getApplicationContext()da dieser Kontext für die Lebensdauer der Anwendung vorhanden ist.

Rufen Sie den Namen des aktuellen Android-Pakets ab:

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

Binden Sie eine anwendungsweite Klasse:

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

3) . Verwenden Sie für Listener und andere Arten von Android-Klassen (z. B. ContentObserver) eine Kontextsubstitution wie:

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

wo thisoder contextist der Kontext einer Klasse (Aktivität usw.).

Activity Kontextsubstitution:

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

Listener-Kontextsubstitution:

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

ContentObserver Kontextsubstitution:

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

4). BroadcastReceiverVerwenden Sie für (einschließlich Inline- / Embedded-Empfänger) den eigenen Kontext des Empfängers.

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

Inlined / Embedded 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). Verwenden Sie für Dienste den eigenen Kontext des Dienstes.

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). Verwenden Sie für Toasts im Allgemeinen getApplicationContext(), aber verwenden Sie nach Möglichkeit den Kontext, der von einer Aktivität, einem Dienst usw. übergeben wurde.

Verwenden Sie den Kontext der Anwendung:

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

Verwenden Sie den von einer Quelle übergebenen Kontext:

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

Und zuletzt nicht getBaseContext()wie von den Framework-Entwicklern von Android empfohlen verwenden.

UPDATE: Fügen Sie ContextAnwendungsbeispiele hinzu.

ChuongPham
quelle
1
Anstelle von mContext kann man verwenden OuterClass.this; Siehe Kommentare in stackoverflow.com/questions/9605459/…
Paul Verest
3
+1 für eine so hilfreiche Antwort! Ich bin damit einverstanden, dass die akzeptierte Antwort als akzeptierte Antwort in Ordnung ist, aber Holy Molly, diese Antwort war super informativ! Vielen Dank für all diese Beispiele. Sie haben mir geholfen, die Kontextnutzung als Ganzes besser zu verstehen. Ich habe Ihre Antwort sogar als Referenz in eine Textdatei auf meinem Computer kopiert.
Ryan
13

Ich habe diesen Thread vor ein paar Tagen gelesen und mir die gleiche Frage gestellt. Meine Entscheidung nach dem Lesen war einfach: Verwenden Sie immer applicationContext.

Ich bin jedoch auf ein Problem gestoßen. Ich habe einige Stunden damit verbracht, es zu finden, und einige Sekunden, um es zu lösen ... (ein Wort ändern ...)

Ich verwende einen LayoutInflater, um eine Ansicht mit einem Spinner aufzublasen.

Hier sind also zwei Möglichkeiten:

1)

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

2)

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

Dann mache ich so etwas:

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

Was mir aufgefallen ist: Wenn Sie Ihr linearLayout mit dem applicationContext instanziiert haben, haben Sie beim Klicken auf den Drehfeld in Ihrer Aktivität eine nicht erfasste Ausnahme, die von der virtuellen Dalvik-Maschine stammt (nicht von Ihrem Code, deshalb habe ich viel ausgegeben Zeit zu finden, wo mein Fehler war ...).

Wenn Sie den baseContext verwenden, ist das in Ordnung, das Kontextmenü wird geöffnet und Sie können zwischen Ihren Auswahlmöglichkeiten wählen.

Hier ist meine Schlussfolgerung: Ich nehme an (ich habe es nicht weiter getestet), als der baseContext erforderlich ist, wenn Sie sich in Ihrer Aktivität mit contextMenu befassen ...

Der Test wurde mit API 8 codiert und auf einem HTC Desire, Android 2.3.3, getestet.

Ich hoffe, mein Kommentar hat Sie bisher nicht gelangweilt und wünsche Ihnen alles Gute. Viel Spaß beim Codieren ;-)

Mav3656
quelle
Ich habe immer "dies" verwendet, wenn ich Ansichten in einer Aktivität erstellt habe. Auf der Grundlage, dass beim Neustart der Aktivität die Ansichten neu erstellt werden und möglicherweise ein neuer Kontext vorhanden ist, aus dem die Ansichten erneut erstellt werden können. Der Nachteil, der im Entwicklerblog veröffentlicht wird, ist, dass die verwendete Zeichnungs- / Bitmap, während eine ImageView zerstört wird, möglicherweise an diesem Kontext hängt. Trotzdem mache ich das gerade. In Bezug auf Code an anderer Stelle in der App (normale Klassen) verwende ich nur den Anwendungskontext, da er nicht spezifisch für Aktivitäten oder UI-Elemente ist.
JonWillis
6

Erstens stimme ich zu, dass wir nach Möglichkeit Appcontext verwenden sollten. dann "dies" in Aktivität. Ich hatte noch nie einen Bedarf an Basecontext.

In meinen Tests können sie in den meisten Fällen ausgetauscht werden. In den meisten Fällen möchten Sie einen Kontext abrufen, indem Sie auf Dateien, Einstellungen, Datenbanken usw. zugreifen. Diese Daten werden schließlich als Dateien im privaten Datenordner Ihrer App (/ data / data /) wiedergegeben. Unabhängig davon, welchen Kontext Sie verwenden, werden sie demselben Ordner / denselben Dateien zugeordnet, sodass Sie in Ordnung sind.

Das habe ich beobachtet. Vielleicht gibt es Fälle, in denen Sie sie unterscheiden sollten.

samsonsu
quelle
Ich habe basecontext benötigt, um die App-Sprache beim Start global festzulegen (wenn sie nicht mit der Standardsprache des Telefons übereinstimmt).
Tina
3

In einigen Fällen können Sie den Aktivitätskontext über den Anwendungskontext verwenden, wenn Sie etwas in einem Thread ausführen. Wenn der Thread die Ausführung abgeschlossen hat und Sie das Ergebnis an die Aufruferaktivität zurückgeben müssen, benötigen Sie diesen Kontext mit einem Handler.

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

In einfachen Worten

getApplicationContext()Wie der Methodenname andeutet, macht Ihre App auf anwendungsweite Details aufmerksam, auf die Sie von überall in der App zugreifen können. Sie können dies also nutzen, um die Servicebindung, die Broadcast-Registrierung usw. Application contextbis zum Beenden der App aufrechtzuerhalten.

getActivity()oder thismacht Ihre App auf den aktuellen Bildschirm aufmerksam, auf dem auch die Details auf App-Ebene von angezeigt werden application context. Was auch immer Sie über den aktuellen Bildschirm wissen möchten Window ActionBar Fragementmangerund in diesem Kontext verfügbar sind. Grundsätzlich und Activityerweitern Context. Dieser Kontext bleibt bestehen, bis die aktuelle Komponente (Aktivität) aktiv ist

Arjun
quelle
1

Die Verwirrung ergibt sich aus der Tatsache, dass es zahlreiche Möglichkeiten gibt, auf den Kontext zuzugreifen, wobei (an der Oberfläche) keine Unterschiede erkennbar sind. Im Folgenden finden Sie vier der häufigsten Möglichkeiten, wie Sie in einer Aktivität auf den Kontext zugreifen können.

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

Was ist ein Kontext? Ich persönlich stelle mir den Kontext jederzeit als den Status Ihrer Bewerbung vor. Der Anwendungskontext stellt eine globale oder Basiskonfiguration Ihrer Anwendung dar, auf der eine Aktivität oder ein Dienst aufbauen kann, und stellt eine Konfigurationsinstanz Ihrer Anwendung oder einen transitiven Status für diese Anwendung dar.

Wenn Sie sich die Quelle für android.content.Context ansehen, sehen Sie, dass Context eine abstrakte Klasse ist und die Kommentare zur Klasse wie folgt lauten:

Schnittstelle zu globalen Informationen über eine Anwendungsumgebung. Dies ist eine abstrakte Klasse, deren Implementierung vom Android-System bereitgestellt wird. Es ermöglicht den Zugriff auf application-specificRessourcen und Klassen sowie das Aufrufen von application-levelVorgängen wie Starten von Aktivitäten, Senden und Empfangen von Absichten usw. Was ich davon wegnehme, ist, dass Context eine gemeinsame Implementierung für den Zugriff auf Anwendungsebene sowie auf Systemebene bietet Ressourcen. Ressourcen auf Anwendungsebene greifen möglicherweise auf Dinge wie String-Ressourcen [getResources()]oder Assets zu, [getAssets()]und Ressourcen auf Systemebene sind alles, mit denen Sie zugreifenContext.getSystemService().

Schauen Sie sich in der Tat die Kommentare zu den Methoden an und sie scheinen diesen Gedanken zu bekräftigen:

getSystemService(): Geben Sie das Handle nach system-levelNamen an einen Dienst zurück. Die Klasse des zurückgegebenen Objekts hängt vom angeforderten Namen ab. getResources(): Geben Sie eine Ressourceninstanz für das Paket Ihrer Anwendung zurück. getAssets(): Geben Sie eine Ressourceninstanz für das Paket Ihrer Anwendung zurück. Es kann erwähnenswert sein, dass in der Klasse Context abstract alle oben genannten Methoden abstrakt sind! Nur eine Instanz von getSystemService (Class) verfügt über eine Implementierung, die eine abstrakte Methode aufruft. Dies bedeutet, dass die Implementierung für diese hauptsächlich von den implementierenden Klassen bereitgestellt werden sollte, zu denen gehören:

ContextWrapper
Application
Activity
Service
IntentService

In der API-Dokumentation sieht die Hierarchie der Klassen folgendermaßen aus:

Kontext

| - ContextWrapper

| - - Anwendung

| - - ContextThemeWrapper

| - - - - Aktivität

| - - Bedienung

| - - - IntentService

Da wir wissen, dass es Contextselbst keinen Einblick gibt, gehen wir den Baum hinunter und schauen uns das an ContextWrapperund stellen fest, dass dort auch nicht viel ist. Da die Anwendung erweitert wird ContextWrapper, gibt es dort auch nicht viel zu sehen, da sie die von bereitgestellte Implementierung nicht überschreibt ContextWrapper. Dies bedeutet, dass die Implementierung für Context vom Betriebssystem bereitgestellt und vor dem verborgen wird API. Sie können sich die konkrete Implementierung für Context ansehen, indem Sie sich die Quelle für die ContextImpl-Klasse ansehen.

Chanaka Weerasinghe
quelle
0

Ich habe dies nur verwendet und getBaseContextbeim Toasten von einem onClick(sehr grünen Noob auf Java und Android). Ich benutze dies, wenn mein Clicker direkt in der Aktivität ist und getBaseContextin einem anonymen inneren Clicker verwendet werden muss. Ich vermute, das ist so ziemlich der Trick getBaseContext, es gibt vielleicht den Kontext der Aktivität zurück, in der sich die innere Klasse versteckt.

Tony
quelle
1
Dies ist falsch, es gibt den Basiskontext der Aktivität selbst zurück. Um die Aktivität (die Sie als Kontext verwenden möchten) von einer anonymen inneren Klasse abzurufen, verwenden Sie so etwas wie MyActivity.this. Die Verwendung des von Ihnen beschriebenen Basiskontexts wird wahrscheinlich keine Probleme verursachen, ist jedoch falsch.
Nickmartens1980