Frage

In einem Android-App gibt es etwas falsch mit dem folgenden Ansatz:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

und gibt sie überall (z SQLiteOpenHelper), in den Kontext erforderlich ist (und natürlich nicht undicht)?

War es hilfreich?

Lösung

Es gibt ein paar mögliche Probleme mit diesem Ansatz, wenn auch in vielen Fällen (wie Ihr Beispiel) es gut funktionieren wird.

Insbesondere sollten Sie vorsichtig sein, wenn sie mit irgendetwas zu tun, die mit der Angebote GUI , die ein Context erfordert. Zum Beispiel werden, wenn Sie den Anwendungskontext in die LayoutInflater übergeben Sie eine Ausnahme erhalten. Generell Ihr Ansatz ist ausgezeichnet: es gute Praxis ist eine verwenden Activity's Context innerhalb dieses Activity und Application Context bei der Übergabe einen Kontext über die Rahmen eines Activity , um .

Auch als Alternative , um Ihre Muster können Sie die Verknüpfung des Aufrufs getApplicationContext() auf einem verwenden Context Objekt (wie eine Aktivität), um den Anwendungskontext zu erhalten.

Andere Tipps

Nach meiner Erfahrung sollte dieser Ansatz nicht notwendig sein. Wenn Sie den Kontext für irgendetwas brauchen, können Sie in der Regel es über einen Anruf erhalten View.getContext () und mit Hilfe der Context erhalten dort kann man nennen Context.getApplicationContext () den Application Kontext zu erhalten. Wenn Sie versuchen, den Application Kontext dies von einem Activity bekommen kann man immer anrufen Activity.getApplication () die in der Lage sein sollte, für einen Anruf benötigt als Context weitergegeben werden, um SQLiteOpenHelper().

Insgesamt scheint es kein Problem mit dem Ansatz für diese Situation zu sein, aber wenn sie mit Context Umgang nur sicherstellen, dass Sie nicht Speicher überall undicht werden als auf dem offiziellen Google Android Developers Blog .

Einige Leute haben gefragt: Wie kann die Singleton einen Null-Zeiger zurückgeben Ich beantworte diese Frage. (Ich kann nicht in einem Kommentar antworten, weil ich Code, um zu schreiben.)

Es kann zwischen zwei Ereignissen null zurück: (1) die Klasse geladen, und (2) das Objekt dieser Klasse wird erstellt. Hier ein Beispiel:

class X {
    static X xinstance;
    static Y yinstance = Y.yinstance;
    X() {xinstance=this;}
}
class Y {
    static X xinstance = X.xinstance;
    static Y yinstance;
    Y() {yinstance=this;}
}

public class A {
    public static void main(String[] p) {
    X x = new X();
    Y y = new Y();
    System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
    System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
    }
}

Lassen Sie uns den Code ausführen:

$ javac A.java 
$ java A
x:X@a63599 y:Y@9036e
x:null y:null

Die zweite Zeile zeigt, dass Y.xinstance und X.yinstance sind null ; sie sind null, weil die Variablen X.xinstance am Y.yinstance gelesen wurde, wenn sie null waren.

Kann man das beheben? Ja,

class X {
    static Y y = Y.getInstance();
    static X theinstance;
    static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
    static X x = X.getInstance();
    static Y theinstance;
    static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}

public class A {
    public static void main(String[] p) {
    System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
    System.out.println("x:"+Y.x+" y:"+X.y);
    }
}

und dieser Code zeigt keine Anomalie:

$ javac A.java 
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e

ABER Dies ist keine Option für das Android-Application Objekt:. Die Programmierer nicht die Zeit steuern, wenn es erstellt wird

Noch einmal: die Differenz zwischen dem ersten Beispiel und das zweite ist, dass das zweite Beispiel erzeugt eine Instanz, wenn der statische Zeiger Null ist. Aber ein Programmierer kann nicht erstellen die Android Anwendungsobjekt, bevor das System entscheidet, es zu tun.

UPDATE

Ein weiteres Rätsel Beispiel, wo initialisiert statische Felder passieren null werden.

Main.java :

enum MyEnum {
    FIRST,SECOND;
    private static String prefix="<", suffix=">";
    String myName;
    MyEnum() {
        myName = makeMyName();
    }
    String makeMyName() {
        return prefix + name() + suffix;
    }
    String getMyName() {
        return myName;
    }
}
public class Main {
    public static void main(String args[]) {
        System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
        System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
        System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
    }
}

Und Sie erhalten:

$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull

Beachten Sie, dass Sie nicht die statische Variablendeklaration einer Zeile obere bewegen können, wird der Code nicht kompiliert werden.

Sie versuchen, einen Wrapper zu erstellen, um Anwendungskontext zu erhalten und es besteht die Möglichkeit, dass es zurückgeben könnte „null“ Zeiger.

Wie pro meinem Verständnis, ich denke, es ist besser Ansatz eine der 2 bis der Call Context.getApplicationContext() oder Activity.getApplication().

Anwendungsklasse:

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context mContext;

    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return mContext;
    }

}

Deklarieren Sie die Anwendung in der AndroidManifest:

<application android:name=".MyApplication"
    ...
/>

Verbrauch:

MyApplication.getAppContext()

Es ist ein guter Ansatz. Ich benutze es mich auch. Ich würde nur vorschlagen onCreate außer Kraft zu setzen, die Singleton zu setzen stattdessen einen Konstruktor zu verwenden.

Und da Sie erwähnt SQLiteOpenHelper. In onCreate () Sie auch die Datenbank öffnen können

Ich persönlich denke, die Dokumentation haben es falsch zu sagen, dass Es ist normalerweise nicht notwendig Anwendung Unterklasse . Ich denke, das Gegenteil ist der Fall: Sie sollten immer Unterklasse Application

.

Ich würde Anwendungskontext verwenden, um einen Systemdienst im Konstruktor zu bekommen. Dies erleichtert Tests und Vorteile aus der Zusammensetzung

public class MyActivity extends Activity {

    private final NotificationManager notificationManager;

    public MyActivity() {
       this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
    }

    public MyActivity(NotificationManager notificationManager) {
       this.notificationManager = notificationManager;
    }

    // onCreate etc

}

Test-Klasse würde dann den überladenen Konstruktor verwenden.

Android würde den Standardkonstruktor verwenden.

Ich mag es, aber ich würde einen Singleton vorschlagen statt:

package com.mobidrone;

import android.app.Application;
import android.content.Context;

public class ApplicationContext extends Application
{
    private static ApplicationContext instance = null;

    private ApplicationContext()
    {
        instance = this;
    }

    public static Context getInstance()
    {
        if (null == instance)
        {
            instance = new ApplicationContext();
        }

        return instance;
    }
}

Ich bin mit dem gleichen Ansatz, schlage ich vor, die Singleton ein wenig besser zu schreiben:

public static MyApp getInstance() {

    if (instance == null) {
        synchronized (MyApp.class) {
            if (instance == null) {
                instance = new MyApp ();
            }
        }
    }

    return instance;
}

, aber ich bin nicht überall, verwende ich getContext() und getApplicationContext() wo ich es kann!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top