Использование контекста приложения везде? - PullRequest
453 голосов
/ 12 июня 2009

В приложении Android что-то не так с таким подходом:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

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

Ответы [ 10 ]

400 голосов
/ 12 июня 2009

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

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

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

28 голосов
/ 12 июня 2009

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

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

12 голосов
/ 09 декабря 2013

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

Может возвращать значение 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:X@a63599 y:Y@9036e
x:null y:null

Вторая строка показывает, что Y.xinstance и X.yinstance равны null ; они являются нулевыми, потому что переменные 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:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e

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

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

UPDATE

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

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

9 голосов
/ 13 августа 2016

Класс применения:

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()
9 голосов
/ 21 июня 2010

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

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

4 голосов
/ 07 августа 2013

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

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

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

3 голосов
/ 27 июня 2014

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

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 будет использовать конструктор по умолчанию.

0 голосов
/ 22 июня 2018

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

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

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

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

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

0 голосов
/ 05 июня 2013

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

public static MyApp getInstance() {

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

    return instance;
}

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

0 голосов
/ 24 февраля 2011

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

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...