Używanie kontekstu aplikacji wszędzie?

W aplikacji na Androida, jest coś nie tak z następującym podejściem:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

I przekazać go wszędzie (np. SQLiteOpenHelper) gdzie wymagany jest kontekst (i oczywiście nie przecieka)?

Author: Segfault, 2009-06-12

10 answers

Istnieje kilka potencjalnych problemów z tym podejściem, chociaż w wielu okolicznościach (takich jak twój przykład) będzie to działać dobrze.

W szczególności powinieneś być ostrożny, gdy masz do czynienia z wszystkim, co dotyczy GUI to wymaga Context. Na przykład, jeśli przekazujesz kontekst aplikacji do LayoutInflater dostaniesz wyjątek. Ogólnie rzecz biorąc, twoje podejście jest doskonałe: dobrą praktyką jest stosowanie Activity's Context w tym Activity, oraz Application Context przy przekazywaniu kontekstu poza zakres Activity aby uniknąć wycieków pamięci.

Również jako alternatywa do wzorca możesz użyć skrótu wywołania getApplicationContext() na Context obiekt (taki jak działanie), aby uzyskać kontekst aplikacji.

 383
Author: Reto Meier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 03:28:43

Z mojego doświadczenia wynika, że takie podejście nie powinno być konieczne. Jeśli potrzebujesz kontekstu do czegokolwiek, zazwyczaj możesz go uzyskać poprzez wywołanie widoku .getContext () {[2] } i używając uzyskanego tam kontekstu można wywołać Context.getApplicationContext () {[2] } aby uzyskać kontekst aplikacji. Jeśli próbujesz uzyskać kontekst aplikacji z aktywności, zawsze możesz wywołać aktywność .getApplication () , która powinna być przekazywana jako kontekst potrzebny do wywołania SQLiteOpenHelper ()

Ogólnie nie wydaje się być problemem z twoim podejściem do tej sytuacji, ale gdy masz do czynienia z kontekstem, upewnij się, że nigdzie nie wyciekasz pamięci, jak opisano na oficjalnym blogu programistów Google Android

 28
Author: snctln,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 16:26:27

Niektórzy pytali: jak singleton może zwrócić wskaźnik null? Odpowiadam na to pytanie. (Nie mogę odpowiedzieć w komentarzu, ponieważ potrzebuję kodu pocztowego.)

Może zwracać null pomiędzy dwoma zdarzeniami: (1) klasa jest ładowana i (2) Obiekt tej klasy jest tworzony. Oto przykład:

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

Uruchom kod:

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

Drugi wiersz pokazuje, że Y. xinstance i X. yinstancenull; są null, ponieważ zmienne X. xinstance ans Y. yinstance były odczytywane, gdy były null.

Można to naprawić? Tak,

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

I ten kod nie wykazuje anomalii:

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

Ale {[12] } to nie jest opcja dla obiektu Android Application: programista nie kontroluje czasu, kiedy jest tworzony.

Jeszcze raz: różnica między pierwszym i drugim przykładem polega na tym, że drugi przykład tworzy instancję, jeśli wskaźnik statyczny jest null. Jednak programista nie może utworzyć obiektu aplikacji na Androida, zanim system zdecyduje się to zrobić.

UPDATE

Jeszcze jeden zagadkowy przykład, gdzie inicjalizowanymi polami statycznymi są 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());
    }
}

I otrzymujesz:

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

Zauważ, że nie możesz przesunąć deklaracji zmiennej statycznej o jedną linię wyżej, kod nie zostanie skompilowany.

 12
Author: 18446744073709551615,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-11 14:54:40

Próbujesz utworzyć opakowanie, aby uzyskać kontekst aplikacji i istnieje możliwość, że zwróci ono wskaźnik" null".

Zgodnie z moim zrozumieniem, myślę, że jego lepsze podejście do połączenia-każdy z 2 Context.getApplicationContext() lub Activity.getApplication().

 9
Author: Prasanta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 18:52:55

Klasa Aplikacji:

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

}

Zadeklaruj aplikację w AndroidManifest:

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

Użycie:

MyApplication.getAppContext()
 9
Author: toha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-13 10:37:44

To dobre podejście. Ja też go używam. Sugerowałbym jedynie nadpisanie onCreate aby ustawić singleton zamiast używać konstruktora.

A skoro wspomniałeś SQLiteOpenHelper: W onCreate () Możesz również otworzyć bazę danych.

Osobiście uważam, że dokumentacja pomyliła się mówiąc, że normalnie nie ma potrzeby podklasowania aplikacji . Myślę, że jest odwrotnie: zawsze powinieneś podklasować aplikację.

 4
Author: Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-07 12:46:20

Użyłbym kontekstu aplikacji, aby uzyskać usługę systemową w konstruktorze. Ułatwia to Testowanie i korzyści z kompozycji

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

}

Klasa testowa użyłaby wtedy przeciążonego konstruktora.

Android użyłby domyślnego konstruktora.

 3
Author: Blundell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-27 08:52:46

Podoba mi się, ale zamiast tego proponowałbym singleton:

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;
    }
}
 0
Author: Franklin Peña,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-16 17:16:03

Używam tego samego podejścia, proponuję napisać singleton trochę lepiej:

public static MyApp getInstance() {

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

    return instance;
}

Ale nie używam wszędzie, używam getContext() i getApplicationContext() Gdzie mogę to zrobić!

 0
Author: Seraphim's,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-05 13:52:01

Zdefiniowanie statycznie kontekstu spowoduje wyciek pamięci

Standardowy sposób na uzyskanie wszędzie kontekstu:

public class App extends Application {

    public static transient SoftReference<Context> contextReference;

    @Override
    public void onCreate() {
        super.onCreate();
        contextReference = new SoftReference<Context>(getApplicationContext());
    }
}

W ten sposób będziesz miał kontekst w dowolnym miejscu kodu TAK:

App.contextReference.get();

Każdy inny sposób zmniejszy wydajność i spowoduje wyciek pamięci

Mam nadzieję, że się przydadzą...
 0
Author: Mr.Hosseini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-05 05:32:55