2009-06-12 7 views
416

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

public class MyApp extends android.app.Application { 

    private static MyApp instance; 

    public MyApp() { 
     instance = this; 
    } 

    public static Context getContext() { 
     return instance; 
    } 

} 

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

+21

Просто разработать для других реализации этого, вы можете затем изменить ' 'вашего файла AndroidManifest.xml, чтобы включить следующее определение атрибута:' a ndroid: имя = "MyApp" '. MyApp должен быть в том же пакете, что и ваш манифест. –

+0

Почему статический? Экземпляр приложения * всегда * создается раньше всего. Где бы вы ни ожидали получить доступ к контексту приложения, он будет передан вам в качестве аргументов. Такой подход может усложнить ваши тесты. Static-itis способствует общей связи. – mschonaker

+6

УДИВИТЕЛЬНЫЙ способ обойти проблему предоставления контекста SQLiteOpenHelper! Я реализовал singleton «SQLiteManager» и застрял в «как F я получаю контекст для сингла?» –

ответ

370

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

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

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

+19

Спасибо за вдохновляющий ответ. Я думаю, что я буду использовать этот подход исключительно для слоя persistence (поскольку я не хочу идти с контент-провайдерами). Интересно, что послужило мотивацией для разработки SQLiteOpenHelper таким образом, что ожидается, что Контекст будет поставляться вместо того, чтобы приобретать его из самого приложения. P.S. И ваша книга великолепна! – yanchenko

+7

Использование контекста приложения с 'LayoutInflator' просто сработало для меня. Должно быть, изменились за последние три года. –

+0

Существуют ли проблемы с этим подходом? Или, этот точный код подходит для использования во всем приложении? –

26

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

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

+12

Подход, который вы предлагаете, скорее всего, приведет к утечке памяти в сообщение блога, которое вы укажете для описания. Контекст, возвращаемый объектом View, будет для Activity, а не для приложения. –

+5

Reto - Вы правы, я не упомянул, что для получения контекста Applciaiton вам нужно вызвать getApplciationContext() в контексте, который вы получаете из View.getContext(), как вы сказали в своем ответе, сейчас я редактирую включить эту информацию. Спасибо, что указали. – snctln

9

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

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

+12

Когда он должен вернуть null? – Stuck

+25

Не существует статического метода Context.getApplicationContext(), о котором я знаю. Я что-то упускаю? – Buzzer

+0

Я также применяю тот же подход в своем приложении, но при вызове в SQLiteOpenHelper он возвращает нулевой указатель. Любой ответ для такого рода ситуаций. – ashutosh

0

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

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

Расширение android.app.application уже гарантирует singleton, поэтому это необязательно. – Vincent

+8

Что делать, если вы хотите получить доступ из классов без активности? – Maxrunner

+3

Вы устанавливаете экземпляр экземпляра в onCreate ... не в конструкторе ... – radzio

-1

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

public static MyApp getInstance() { 

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

    return instance; 
} 

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

+0

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

+1

Нет необходимости в том, что операционная система гарантирует, что приложение создаётся именно один раз. Если бы я предлагал установить Singelton в onCreate(). – Martin

+1

Хороший поточно-безопасный способ ленить инициализировать синглтон, но не обязательно здесь. – naXa

4

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

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

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

10

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

Он может возвращать нуль между двумя событиями: (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, прежде чем система решит это сделать.

+2

Полезный пример; его хорошо знать, что есть такая дыра. То, что я убираю из этого, заключается в том, что следует избегать ссылки на такую ​​статическую переменную во время статической инициализации любого класса. – ToolmakerSteve

3

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

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

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

7

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

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