суббота, 17 декабря 2011 г.

Реализация Singleton в JAVA


Реализация Singleton в JAVA

В этой статье я хочу затронуть тему одного из наиболее распространенных паттернов объектно-ориентированного программирования – Singleton. Но в данном случае я не буду описывать преимущества/недостатки и области применения этого паттерна, а попытаюсь изложить свой взгляд на его имплементацию в JAVA.

Общие сведения
Паттерн Singleton гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.


Область применения
1.) В системе должно существовать не более одного экземпляра заданного класса.
2.) Экземпляр должен быть легко доступен для всех клиентов данного класса.
3.) Создание объекта on demand, то есть, когда он понадобится первый раз, а не во время инициализации системы.

Реализация (JAVA):
На данный момент существуют несколько вариантов реализации со своими недостатками и преимуществами. В них мы и попробуем сейчас разобраться.

Вариант первый – самый простой, который приходит в голову сразу после понимания проблемы.
Вариант первый

У этого решения есть единственный недостаток – оно не работает в многопоточной среде и поэтому не подходит в большинстве случаев. Решение подходит исключительно для однопоточных приложений.

Не беда, скажите вы, и предложите следующее решение.

Вариант второй:
Вариант второй

И вы будете правы, так как проблему многопоточности мы решили, но потеряли две важные вещи:
1. Ленивую инициализацию (Объект instance будет создан classloader-ом во время инициализации класса)
2. Отсутствует возможность обработки исключительных ситуаций(exceptions) во время вызова конструктора.

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

Далее возникают 2 варианта решения.
1.) Использование внутреннего класса(решение Била Пью(Bill Pugh“Initialization on Demand Holder”).
2.) Использование синхронизации.

Начнем с Била Пью.

Вариант третий:
“Initialization on Demand Holder”
Вариант третий

В данном случае мы полностью решили проблему ленивой инициализации – объект инициализируется при первом вызове метода getInstance(). Но у нас осталась проблема с обработкой исключительных ситуаций в конструкторе. Так что, если конструктор класса не вызывает опасений создания исключительных ситуаций, то смело можно использовать этот метод.

Синхронизация
Этой части я хотел бы уделить особое внимание. Можно было бы подойти к данному вопросу с заголовком «synchronized – мифы и реальность».

И так, самый прямолинейный метод.

Вариант четвертый:
Вариант четвертый

У этого варианта есть только один недостаток. Синхронизация полезна только один раз, при первом обращении к getInstance(), после этого каждый раз, при обращении этому методу, синхронизация просто забирает время. Что можно сказать по этому поводу? Ну, во-первых, если вызов getInstance() не происходит достаточно часто (что значит «достаточно часто» решать вам), то этот метод имеет преимущество перед остальными – прост, понятен, лениво инициализируется, дает возможность обрабатывать исключительные ситуации в конструкторе. А во-вторых, синхронизация в Java перестала быть обременительно медленной настолько, насколько ее боятся. Ну что еще для счастья надо?

Теперь рассмотрим вариант синхронизированного решения, в котором попытаемся решить проблему, возникшую в предыдущем варианте.

Наиболее распространенный способ - «Double-Checked Locking». В своем оригинальном варианте:
Double-Checked Locking

Не работает! Почему? Это отдельная тема, но для интересующихся могу посоветовать прочитать эту статью http://www.cs.umd.edu/~pugh/java/memoryM….

Но не надо совсем отчаиваться, в JAVA 5 проблему решили, используя модификатор volatile. На данный момент решение выглядит так:

Вариант пятый:
Вариант пятый

Не смотря на то, что этот вариант выглядит как идеальное решение, использовать его не рекомендуется т.к. товарищ Allen Holub заметил, что использование volatile модификатора может привести к проблемам производительности на мультипроцессорных системах. Но решать все же вам.

Вот, в общем-то, и все распространенные варианты имплементаций данного паттерна. Но, на этом не заканчиваются подводные камни Singleton-а. Существуют еще несколько моментов, которые нужно учитывать во время проектирования того или иного приложения, использующего Singleton.

Подводные камни
1. Наследование
В подавляющем большинстве случаев в Singleton классах наследование не нужно и, более того, излишне и является следствием over-design. Да и реализация наследования имеет определенные сложности, учитывая, что и сам instance и метод getInstance() статические.
Поэтому, я рекомендую использовать модификатор final и запретить наследование данного класса, если нет особой необходимости в обратном.

2. Две и более виртуальных машины
Каждая виртуальная машина создает свою копию Singleton объекта. И хотя на первый взгляд это выглядит очевидным, во многих распределенных системах, таких как EJB, JINI и RMI все не так просто. Когда промежуточные уровни скрывают (делают прозрачными) распределенные технологи, бывает трудно сказать, где и когда инициализирован объект.

3.Различные Class Loader-ы
Когда 2 class loader-а загружают класс, каждый из них может создать свою копию Singleton-а(в тех случаях, когда instance инициализируется class loader-ом ). Это особенно актуально в использовании сервлетов(servlet), так как в некоторых имплементациях серверов приложений(application server) каждый сервлет имеет свой class loader.

Существует еще ряд проблем, которые менее актуальны (такие как технология reflection и имплементация интерфейсов Cloneable и Serializable), и не будут мною рассмотрены в силу своей экзотичности в сфере применения Singleton классов. Но, в любом случае, с радостью отвечу на любые вопросы к этому материалу.

Вот, в общем-то, и все ключевые моменты, которые я хотел осветить в данной статье. Осталось лишь заметить, что данный материал не претендует быть истиной в последней инстанции и допускает существование точек зрения, отличающихся от точки зрения автора. И даже более того, любые замечания приветствуются и будут прияты к сведению.

Спасибо за внимание.

суббота, 8 октября 2011 г.

Ошибка "Невозможно выполнить установку на USB-накопитель или SD-карту"

Случилась как-то неприятность..
Во время деинсталляции приложения мой телефон ушел в ребут. 
После этого я не мог устанавливать приложения с маркета, которые подефолту переносятся на SD карточку. При этом оно скачивались, начиналась установка и в результате я вижу сообщение "Невозможно выполнить установку на USB-накопитель или SD-карту".

В итоге нашел выход из этой ситуации. 
Необходимо удалить .android_secure\smdl2tmp1.asec файлик с SD карточки. Я это сделал прямо с телефона,  используя для этого FileExplorer приложение. 
Так же можно его удалить с PC, подключив телефон через USB.

суббота, 11 июня 2011 г.

Propagate ffmpeg to Android

ffmpeg — это, в первую очередь, очень удобная консольная утилита для работы с видео, и только потом библиотека, которую можно использовать в своих проектах. Соответственно, и информации по консольной утилите в разы больше. А если учесть, что код ffmpeg, хоть и открытый, но написан так, что черт в нем ногу сломит, то использование данной библиотеки превращается в занятие просто самоубийственное.


Может в ближайшее время пригодиться;)

Собираем ffmpeg для Android

How to compile Android build (Ubuntu)

Статья Compile Android Gingerbread on Ubuntu 10.10 понравилась и пригодилась.
В моем случае помогла с компиляцией Android 2.2 Froyo.

Официальная Google инструкция

воскресенье, 22 мая 2011 г.

How to: Native Application for Android - Hello World


Для начала,
скачиваем NDK последней версии  http://developer.android.com/sdk/ndk/index.html
и SDK (вернее приложение adb) http://developer.android.com/sdk/index.html
рутованный телефон (не уверен что этот пункт обязателен)

Я использовал ubuntu 10.xx
1. распаковываем ndk (у меня в /rusboy/android-ndk-r5b)
2. заходим в папку ndk/samples создаем директорию hellonative
3. для простоты копируем из hello-jni директорию jni в hellonative и файл default.properties
4. в директории jni изменяем Android.mk таким образом

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE    := hn
LOCAL_SRC_FILES := hellonative.c

include $(BUILD_EXECUTABLE)

5. Си файл можно удалить и создать новый hellonative.c
6. В него вставляем код:

#include <stdio.h>

int main()
{
    printf("Hello Native\n");
    getchar();
}

adb файл скопирован в папку с проектом(для удобства)
7. Компилируем
8. Открываем терминал на Ubuntu и выполняем

# cd /root/android-ndk-r5b/samples/hellonative/
# ../../ndk-build
Install        : hn => libs/armeabi/hn

Бинарный файл находится здесь libs/armeabi/hn
9. выполняем в терминале из папки проекта

# adb shell
# su
# mkdir /data/tmp
# exit
# exit
# adb push /root/android-ndk-r5b/samples/hellonative/libs/armeabi/hn /data/tmp
56 KB/s (2368 bytes in 0.041s)
# adb shell
# chmod 744 /data/tmp/hn
# /data/tmp/hn
Hello Native

Источник информации: Simple NDK

понедельник, 2 мая 2011 г.

Событие onClick на Button

Для того что б получить повесить onClick событие на кнопку необходимо создать новый объект класса OnClickListener. Переопределить в нем метод onClick. После этого назначаем этот объект методом setOnClickListener кнопке.
Второй вариант, это создать новый класс и через implement привязать его к OnClickListener. Далее перегружаем onClick метод и уже на вход setOnClickListener передаем объект нового класса.
Пример второго способа ниже:

Как дописывать текст в конец файла

Вызываем конструктор FileWriter со вторым параметром true.
Пример кода под катом:

суббота, 23 апреля 2011 г.

Thread/Runnable in Java


В Java термин “поток” может обозначать две разные вещи:
  1. Экземпляр класса java.lang.Thread
  2. Поток выполнения
Экземпляр Thread – это обычный объект. Подобно другим объектам в Java, он имеет переменные и методы, а живет и умирает в куче. Поток выполнения (thread of execution) – это отдельный процесс (“легковесный” процесс), имеющий свой собственный стэк вызовов. В Java для каждого потока существует один стэк вызовов. Даже если вы явно не создаете потоков в вашей программе, они там все равно есть. Детали тут: 

Потоки в Java. Часть 1
Потоки в Java. Часть 2
Потоки в Java. Часть 3

Class AsyncTask: сделать поток работающий с view

Здесь классный пример использования AsyncTask: AsyncTaskExample.java

How to: Заполучить исходники Android 2.2

Исходники находятся тут: http://android.git.kernel.org/
Система управления версиями: Git

Во первых, под Windows Android Enviroment установить нельзя, потому я установил виртуальную машину с Ubuntu.
И сразу выделите не меньше 10GB только для исходников!

Linux установлен, теперь идем в командную строку и выполняем: