Синглетон можно реализовать разными способами. Обычно его реализуют так, чтобы он был доступен в любой точке приложения. Например, в виде класса, в котором все функции статические. Или в виде обычного класса со статичной функцией
GetInstance()
и приватным конструктором. Главное, учесть при реализации синглетона особенности языка программирования и платформы. Иначе ваше приложение рискует погрязнуть в ворохе непонятных и трудноуловимых багов. Я уже как-то писал об одной такой нетривиальной особенности реализации синглетона на CLI/C++. Оказалось, что и в реализации синглетона под Android есть подобная нетривиальная особенность. Простейшая и вроде бы очевидная реализация синглетона под Android неверна. Пример:
public final class MySingleton { private static MyClass m_A = new MyClass(); private static MyClass getA() { return m_A; } }Такой синглетон заработает. И может работать очень долго. Но в один прекрасный момент, вы рискуете получить
null
в getA()
. Со всеми вытекающими последствиями...Причина проста. Вы инициализируете статический класс синглетона в контексте Activity. Если в вашем приложении несколько activity, то синглетон доступен в каждой из них. Но если первоначальная activity, в которой создавался синглетон, будет уничтожена, то и синглетон тоже будет уничтожен. После этого, в других activity вы будете получать null вместо A. А ведь под андроидом подобное уничтожение activity дело обычное..
Проблема эта известна. Имеются решения:
- создавать синглетон в контексте сервиса, а не в контексте activity;
- унаследовать класс синглетона от класса
Application
.
Application
, будет выглядеть так:public final class MySingleton extends Application { private MyClass m_A = new MyClass(); @Override public void onCreate() { super.onCreate(); //инициализация объектов синглетона Resources r = this.getResources(); ... } private MyClass getA() { return m_A; } }Нужно не забыть немножко подправить манифест, указав класс синглетона в качестве класса приложения:
......
MySingleton
стал нестатическим классом. Тем не менее, обращаться к нему можно отовсюду, где имеется context - из виджета, из activity и т.п.//виджет: public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { MySingleton s = (MySingleton)context.getApplication(); } //Activity: public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MySingleton s = (MySingleton)this.getApplication(); }Не так удобно, как раньше, но вполне терпимо.
"Но если первоначальная activity, в которой создавался синглетон, будет уничтожена, то и синглетон тоже будет уничтожен."
ОтветитьУдалитьВы не могли бы пояснить, почему так?
Это архитектурная особенность Android OS. Подробнее можно почитать здесь. Цитирую: "Lifetime of a static variable: A static variable comes into existence when a class is loaded by the JVM and dies when the class is unloaded.
ОтветитьУдалитьSo if you create an android application and initialize a static variable, it will remain in the JVM until one of the following happens:
1. the class is unloaded
2. the JVM shuts down
3. the process dies
Note that the value of the static variable will persist when you switch to a different activity of another application and none of the above three happens. Should any of the above three happen the static will lose its value."
и далее
"Well, the Singleton pattern is also based on using static variables so actually you would be in the same position. While the static approach may work most of the times, it may happen that in some cases when memory is full and another activity takes the foreground before your application moves to its next screen, your activity's process could be killed and you lose the static values."
Подскажите, что может быть не так, сделал все по инструкции, возникает
ОтветитьУдалитьjava.lang.ClassCastException: android.app.Application
В строке
MySingleton s = (MySingleton)this.getApplication();
Вы точно в манифесте не забыли android:name=".MySingleton" в application прописать?
ОтветитьУдалитьВажный момент: тег application в манифесте должен быть один единственный.
Проверьте, что возвращают:
this.getApplicationContext() instanceof MySingleton
и
this.getApplication() instanceof MySingleton
возможно следует использовать getApplicationContext.
Вот здесь
http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables
в комментариях пишут, что иногда имя с точкой не срабатывает, и приходится писать
android:name="MySingleton"
Лично я пишу всегда с точкой.
Больше идей нет, надо исходники смотреть.
Здесь сказано, что "унаследовать класс синглетона от класса Application" не выход.
ОтветитьУдалитьhttp://habrahabr.ru/qa/12794/
Автор не приводит подробностей реализации, а просто констатирует факт - "не работает". Разобраться, чем вызваны описанные им проблемы без кода не возможно. Подозреваю, дело там совсем не в классе Application.
ОтветитьУдалитьЛично мой опыт показывает, что метод прекрасно работает на практике.
Возможно, автор не учитывает возможность перезапуска приложения. Вот что написано в
Android Application Framework FAQ: Even while an application appears to continue running, the system may choose to kill its process and restart it later. If you have data that you need to persist from one activity invocation to the next, you need to represent that data as state that gets saved by an activity when it is informed that it might go away.
For sharing complex persistent user-defined objects, the following approaches are recommended:...
Другими словами, не стоит использовать глобальный синглетон для хранения данных. Синглетон идеально подходит для хранения объектов, использующих данные (всякие кеши, менеджеры и т.д.). При пере-запуске приложения эти объекты будут пере-создаваться. И у них должна быть возможность загрузить необходимые для работы данные из какого-то внешнего источника.