Domanda

In un'applicazione per Android, c'è qualcosa di sbagliato con il seguente approccio:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

e passano ovunque (ad es.SQLiteOpenHelper) dove il contesto è necessaria (e non perde ovviamente)?

È stato utile?

Soluzione

Ci sono un paio di potenziali problemi con questo approccio, anche se in un sacco di circostanze (ad esempio) funziona bene.

In particolare, si dovrebbe essere attenti quando si tratta di qualcosa che riguarda il GUI che richiede un Context.Per esempio, se si passa il Contesto di applicazione in LayoutInflater si otterrà un'Eccezione.In generale, il tuo approccio è eccellente:e ' buona norma utilizzare un Activity's Context all'interno di tale Activity, e il Application Context quando si passa un contesto al di là della portata di un Activity per evitare perdite di memoria.

Inoltre, come alternativa il vostro modello è possibile usare la combinazione di tasti di chiamata getApplicationContext() su un Context oggetto (per esempio un'Attività) per ottenere l'Ambito di Applicazione.

Altri suggerimenti

Nella mia esperienza questo approccio non dovrebbe essere necessario.Se avete bisogno di un contesto per nulla, di solito si può ottenere tramite una chiamata a Vista.getContext() e utilizzando il Context ottenuti lì si può chiamare Contesto.getApplicationContext() per ottenere il Application contesto.Se stai cercando di ottenere il Application contesto questo da un Activity si può sempre chiamare Attività.getApplication() che dovrebbe essere in grado di essere passato come Context necessari per una chiamata a SQLiteOpenHelper().

Nel complesso non sembra essere un problema con il tuo approccio per questa situazione, ma quando ha a che fare con Context basta assicurarsi che si sono perdite di memoria ovunque come descritto sul sito ufficiale Google Android Developers blog.

Alcune persone ci hanno chiesto: come può il singleton restituire un puntatore null? Sto a rispondere a questa domanda.(Non posso rispondere in un commento, perché ho bisogno di un codice postale.)

Si può restituire null, fra le due eventi:(1) la classe viene caricata, e (2) l'oggetto di questa classe è creata.Ecco un esempio:

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

Vediamo il codice:

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

La seconda riga mostra che Y. xinstance e X. yinstance sono null;essi sono null, perché le variabili X. xinstance ans Y. yinstance sono stati letti quando erano null.

Questo può essere fisso?Sì,

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

e questo codice non mostra alcuna anomalia:

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

MA questo non è un'opzione per Android Application oggetto:il programmatore, non controllo il tempo in cui è stato creato.

Ancora una volta:la differenza tra il primo esempio e il secondo è che il secondo esempio viene creata un'istanza se la statica puntatore è null.Ma un programmatore può creare il Applicazione Android oggetto prima che il sistema decide di farlo.

AGGIORNAMENTO

Uno più sconcertante esempio di inizializzazione statica campi capita di essere null.

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

E si ottiene:

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

Si noti che non è possibile spostare la variabile statica dichiarazione di una riga superiore, il codice non viene compilato.

Si sta tentando di creare un wrapper per ottenere Ambito di Applicazione e c'è una possibilità che potrebbe tornare "null"puntatore.

Come per la mia comprensione, credo che il suo approccio migliore per chiamare uno dei 2 Context.getApplicationContext() o Activity.getApplication().

Classe Di Applicazione:

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

}

Dichiarare l'Applicazione in AndroidManifest:

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

Utilizzo:

MyApplication.getAppContext()

È un buon approccio.Io lo uso per me stesso.Vorrei solo suggerire di eseguire l'override onCreate per impostare il singleton invece di utilizzare un costruttore.

E dal momento che hai menzionato SQLiteOpenHelper:In onCreate () è possibile aprire il database.

Personalmente penso che la documentazione è sbagliato nel dire che Di norma non è necessario creare una sottoclasse di Applicazione.Penso che è vero il contrario:Si dovrebbe sempre sottoclasse Applicazione.

Vorrei utilizzare Contesto di Applicazione per ottenere un Sistema di Servizio nel costruttore.Questo facilita il test e vantaggi dalla composizione

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

}

Classe di Test quindi necessario utilizzare l'overload del costruttore.

Android sarebbe utilizzare il costruttore di default.

Mi piace, ma vorrei suggerire un singleton, invece:

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

Sto utilizzando lo stesso approccio, vi suggerisco di scrivere il singleton un po ' meglio:

public static MyApp getInstance() {

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

    return instance;
}

ma io non lo uso ovunque, io uso getContext() e getApplicationContext() dove posso farlo!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top