Подтвердить что ты не робот

Использование контекста приложения во всем мире?

В приложении для Android есть что-то неправильное в следующем подходе:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

и передавать его всюду (например, SQLiteOpenHelper), где требуется контекст (а не утечка конечно)?

4b9b3361

Ответ 1

Есть несколько потенциальных проблем с этим подходом, хотя во многих случаях (например, в вашем примере) это будет работать хорошо.

В частности, вы должны быть осторожны при работе со всем, что имеет дело с GUI, для которого требуется Context. Например, если вы передаете контекст приложения в LayoutInflater, вы получите исключение. Вообще говоря, ваш подход превосходный: рекомендуется использовать Activity's Context Activity, а Application Context при передаче контекста за пределы Activity в избежать утечек памяти.

Кроме того, в качестве альтернативного для вашего шаблона вы можете использовать ярлык вызова getApplicationContext() объекта Context (например, Activity), чтобы получить Контекст приложения.

Ответ 2

По моему опыту такой подход не должен быть необходимым. Если вам нужен контекст для чего-либо, вы обычно можете получить его через вызов View.getContext() и, используя полученный там Context вы можете вызвать Context.getApplicationContext(), чтобы получить контекст Application. Если вы пытаетесь получить контекст Application из Activity вы всегда можете вызвать Activity.getApplication(), которую можно передать как Context необходимый для вызова SQLiteOpenHelper().

В целом, похоже, нет проблем с вашим подходом к этой ситуации, но при работе с Context просто убедитесь, что вы не теряете память нигде, как описано в официальном блоге разработчиков Google Android.

Ответ 3

Некоторые люди спрашивали: как синглтон может вернуть нулевой указатель? Я отвечаю на этот вопрос. (Я не могу ответить в комментарии, потому что мне нужно отправить код.)

Он может возвращать значение null между двумя событиями: (1) класс загружается и (2) создается объект этого класса. Вот пример:

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

Позвольте запустить код:

$ javac A.java 
$ java A
x:[email protected] y:[email protected]
x:null y:null

Вторая строка показывает, что Y.xinstance и X.yinstance являются нулевыми; они равны нулю, поскольку переменные X.xinstance ans Y.yinstance были прочитаны, когда они были пустыми.

Можно ли это исправить? Да,

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

и этот код не показывает аномалии:

$ javac A.java 
$ java A
x:[email protected] y:[email protected]
x:[email protected] y:[email protected]

НО это не вариант для объекта Android Application: программист не контролирует время его создания.

Еще раз: разница между первым примером и вторым заключается в том, что второй пример создает экземпляр, если статический указатель имеет значение null. Но программист не может создать объект приложения Android до того, как система решит это сделать.

ОБНОВИТЬ

Еще один загадочный пример, когда инициализированные статические поля имеют значение 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());
    }
}

И вы получаете:

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

Обратите внимание, что вы не можете переместить объявление статической переменной на одну строку вверх, код не будет компилироваться.

Ответ 4

Вы пытаетесь создать оболочку, чтобы получить контекст приложения, и есть вероятность, что он может вернуть указатель "null".

В соответствии с моим пониманием, я думаю, что его лучший подход к вызову - любой из 2 Context.getApplicationContext() или Activity.getApplication().

Ответ 5

Класс приложения:

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

}

Объявите приложение в AndroidManifest:

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

Использование:

MyApplication.getAppContext()

Ответ 6

Это хороший подход. Я тоже использую его сам. Я бы предложил только переопределить onCreate, чтобы установить singleton вместо использования конструктора.

И поскольку вы упомянули SQLiteOpenHelper: В onCreate () вы также можете открыть базу данных.

Лично я думаю, что в документации было неправильно говорить, что обычно нет необходимости в подклассе Application. Я думаю, что обратное верно: вы всегда должны подклассифицировать приложение.

Ответ 7

Я бы использовал контекст приложения, чтобы получить системную службу в конструкторе. Это облегчает тестирование и выгоды от состава

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

}

Затем тестовый класс будет использовать перегруженный конструктор.

Android будет использовать конструктор по умолчанию.

Ответ 8

Мне это нравится, но я бы предложил вместо него сингл:

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

Ответ 9

Я использую тот же подход, я предлагаю немного написать синглтон:

public static MyApp getInstance() {

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

    return instance;
}

но я не использую везде, я использую getContext() и getApplicationContext(), где я могу это сделать!

Ответ 10

Статическое определение контекста приведет к утечке памяти

Стандартный способ получить контекст везде:

public class App extends Application {

    public static transient SoftReference<Context> contextReference;

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

Таким образом, вы будете иметь контекст где-нибудь в коде, как это:

App.contextReference.get();

Любой другой способ снизит производительность и вызовет утечку памяти

Надеюсь быть полезным...