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

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

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

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

10 ответов


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

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

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


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

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


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

он может возвращать 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 are null; они равны нулю, потому что переменные X. xinstance ans Y. yinstance были прочитаны, когда они были null.

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

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


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

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


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

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

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

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

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


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

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 class затем будет использовать перегруженный конструктор.

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


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

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

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

public static MyApp getInstance() {

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

    return instance;
}

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


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

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

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

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

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