Рубрики
Java

Про Android TV.

androidTV_player-entertainment-default

Небольшая предыстория.

Так получилось, что мы пишем приложение для Android TV.
Вообще, обычно я занимаюсь тем, что в программисткой среде принято называть "суровым энтерпрайзом" — веб-службы, высокая нагрузка и прочие Java EE штуки.

Тем не менее, иногда мы делаем проекты и под мобильные платформы. Как правило это заказная разработка, например мобильные приложения для внутренних нужд компании (приложения для сотрудников организации/диспетчеров/водителей, интерактивные учебные пособия, системы учета/бронирования, системы мониторинга и так далее), которые потом обычно и интегрируют с бэкендом на "суровом энтерпрайзе". Этими приложениями пользуется персонал компании, а не простые пользователи. В большинстве случаев они даже не публикуются на маркете.

Естественно из этого есть исключения.
Итак. Да, мы пишем под Android TV. Не знаю, много ли существует мобильных разработчиков в России, которые что-то делают под эту платформу. Понятное дело, есть крупные компании, которые занимаются производством и распространением телевизионного и медиа контента и им сам Бог велел заниматься созданием приложений под эту платформу. Когда-то давно, я тоже делал мобильное приложение для одного популярного в определенных кругах телеканала, но сейчас не об этом.

У нас немного другая история. Мы изначально делали обычное приложение (с кнопочкам и картинками), и по началу думали, что оно будет работать на телефонах, планшетах и китайских свистелках-поделках - "тв приставках". Потом внезапно выяснилось, что платформа Android TV подошла на удивление хорошо для целей проекта.

Про Android TV.

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

1. Нормального эмулятора нет (как обычно). Мы используем оригинальный, привезенный нам из забугорья Android Nexus Player. Настоящий суровый американский Nexus Player с плоской вилкой на 110 В. Без намёка на РОСТЕСТ. Без такого устройства нормальная разработка под Android TV была бы затруднительна. Как и все Nexus-ы он неплохо подходит для целей отладки и разработки. Нет проблем с драйверами, подключением по adb и т.д. Это наше основное девелоперское устройство в этом проекте.

2. Конечно мы проводим бета-тесты еще и на телевизоре Sony. Как выяснилось, это вполне такой обычный телевизор, просто на нем установлен Андроид.
Естественно кроме телевизора и Nexus-а, перед тем как выложить приложение, нам приходится тестировать приложение на десятках разных телефонах и планшетах (те кто пишет под Андроид знают, что это не гипербола, а суровые будни андроид программиста).
Так вот, на телевизоре что-либо дебажить очень не удобно, поэтому опять таки основным устройство у нас является Nexus Player.

3. В какой-то момент Nexus Player внезапно обновился на Marshmallow (Android M) (что в прочем для нексусов вполне типично).
В итоге у нас в руках появился очень редкий, можно сказать "краснокнижный" зверь. Устройство под Android TV (которых мало) и с 6-ой версией Android-а (которых еще меньше). Это прямо-таки удивительное и непредсказуемое создание. Часто было не понятно, особенности в его поведении были связанны с тем, что это был шестой андроид или потому, что это Android TV.

Выводы.

Стоит ли писать под Android TV? Думаю решать нужно исходя из целей проекта. Приведу свои, субъективные размышления на эту тему (актуально на момент публикации этой статьи).

1. ТВ стали смотреть меньше. Возможно я ошибаюсь, но это общий тренд. Я не уверен, что в будущем вообще будут много смотреть телевизоры.
2. Играть на Android TV в игры - сомнительное удовольствие. Я конечно не геймер, но мне кажется, что уж лучше подключить XBox.
3. Платформа требует отдельного внимания и тщательного изучения API.
Текстовый ввод не удобен (т.к. пульт). Если у вас много элементов управления (текстовых полей, кнопок) или используется тач, то придётся все эти моменты перерабатывать.
4. Аудитория. Может я не прав, но если сравнивать со всей массой пользователей андроид устройств её почти нет.
Вживую телефон с андроидом я видел у многих. Телевизор с Android TV только один.
5. Как операционная система Android для телевизоров удобней чем Smart TV. Это лично мое мнение, я могу сравнить, т.к. дома у меня телевизор LG с webOS, а на работе тестируем на Sony с Android-ом.

Мое личное мнение, использовать Android TV приложения, только в том случае, если это действительно вам подходит.
Сил и времени потратите уйму. Если делать по-уму, нужно оптимизировать пользовательский интерфейс (иначе приложением нельзя будет пользоваться), обрабатывать нажатие с пульта, менять систему навигации и многое другое.

Стоит ли следить за развитием платформы? Да, однозначно стоит. Бросить всё, уволиться с работы, продать машину и квартиру и начать делать ещё один клон "ангри бёрдс" под Android TV, в надежде что он вас озолотит — на мой взгляд не самая здравомыслящая идея.

Делать прогнозы сложно, но вряд ли эта платформа даст такой же взрывной рост как и обычные мобильные приложения. Android TV — очень специфичный продукт.
В целом, ситуация с ним чем-то напоминает бум умных часов. Было очень много маркетинговой шумихи, а в итоге никакого стремительно роста никто пока не наблюдает. Эти умные часы я тоже купил, вроде как на всякий случай, думал буду что-то писать под них, но пока они просто лежат у меня в ящике рабочего стола.

PS: Хотел уточнить, чтобы не возникло искаженное понимание ситуации, т.к. я с одной стороны критикую платформу, а с другой стороны пишу что мы под нее активно разрабатываем. Да я считаю, что нет особого смысла в обычных приложениях под Android TV. У нас совсем другой случай, мы делаем специализированный продукт для B2B-сектора по заказу. Другими словами, пишем приложение специально предназначенное для работе на телевизоре.

Рубрики
0. Мироощущение и бытие Java

FWD: Just Say mNo to Hungarian Notation!

mNo

Не могу пройти мимо призыва Джейка Вартона - "Скажи mNo венгерской нотации!".

Во-первых (и это мое личное мнение), использовать такую нотацию в исходниках на Java действительно нелепо.
Во-вторых, когда-то давно я grep-ом проходил исходники андроида (кажется 16-ый API Level) и там нотация не соблюдалась. Более того она была некорректной (статические поля с m-префиксом).

Вартон молодец, что наконец-то поднял эту тему.

Рубрики
4. Полезняшки Java

Заметка об экспериментах со SnappyDB (NoSQL KeyValue DB под Android)

Небольшая заметка про SnappyDB. Это NoSQL база данных под Android, которая базируется на LevelDB и алгоритме сжатия Snappy.

LevelDB - это key-value база данных. Написана Google-ом для каких-то своих мега проектов.
Snappy - метод сжатия данных, сбалансированный на скорость (т.е. приоритет быстрота, а не степень сжатия). Также написана Google-ом.

Авторы SnappyDB на своем блоге приводят очень привлекательный график сравнивая скорость работы SnappyDB с SQLite.

Все это конечно очень и очень заманчиво, но лично меня она заинтересовала не поэтому. Дело не в скорости и даже не в том, что какие-то части этой БД делал Сам Google, а в более-менее нормальном API.

На практике у меня пока не возникали задачи, в которых нужно было обрабатывать на мобильных устройствах (а я напомню это именно БД для Android) терабайты данных. При этом хранить различные небольшие справочники, настройки, профили и прочую ерунду приходится регулярно.

Конечно по-хорошему можно использовать API Preferenes или SQLite, но в некоторых случаях это не очень удобно.
В частности у SQLite несколько громоздкий API, а в preferences нет удобных методов для сложных выборок по ключам.

Упоминая более-менее нормальное API я имел в виду следующее. Вот что нужно сделать чтобы получить значение по ключу в Preferences (Android API).

  SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
  String username = sharedPref.getString("name", "");

Вот что нужно сделать чтобы получить значение по ключу в Snappy DB.

  DB db = DBFactory.open(context); 
  String username =  db.get("name");

Другими словами, API не такое "многословное" как стандартное Android API.

При этом если есть несколько записей ("name:0","name:1","name:2" и т.д.), то их можно получить как-то так:

  db.findKeys("name:");

Такое "из коробки" в Preferences сделать пока невозможно.

Поэтому мне показалось, что Snappy DB это удобно. Можно использовать например в таких задачах как:
- Сохранить справочников кодов и их описание
- Запоминать время работы каких-то специальных команд или хранить данные по мониторингу
- Идентификатор пользователя и его профиль (например в XML или JSON формате)

В общем все было хорошо, пока не наткнулся на одну приставку под Android TV, на которой был установлен Android M. Так вот, на этом устройстве Snappy DB внезапно стала работать с ошибками. Поскольку БД нативная, а желания дебажить сишный код у меня в последнее время нет, пришлось отказаться от ее использования в текущих проектах.

Рубрики
3. Инструментарий

Про Gradle для любопытных.

Предыстория.

Вот раньше был Ant. Простой и понятный инструмент для сборки проектов. Открываешь xml-ку и видишь: здесь мы хотим скомпилировать файлы, здесь скопировать всё в папку dist, а здесь сделать jar-ик.

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

В результате, в некоторых программерских компаниях разработчиков стали насильно пересаживать на Maven. Так наступило время, в течение которого процесс сборки проектов для некоторых программистов был окутан туманом.

Разобраться, как работает Maven; понять, что означают все эти тэги – было весьма затруднительно. Всё взаимодействие ограничивалось, как правило, запуском команды mvn clean install. Для многих это действие означало что-то похожее на взмах волшебной палочки с параллельным произнесением заклинаний: "Expelliarmus" или "Avada Kedavra".

Любая проблема, требующая каких-либо нестандартных исправлений в pom.xml, первым делом решалась поиском "нужного заклинания" в stackoverflow или Google. Главная причина недовольства, которое высказывали многие программисты, что не понятно как и в какой последовательности всё работает. Например, что нужно сделать, чтобы просто скопировать файлик и затем выложить его на ftp (или запустить обфускатор)?

Тем не менее, Maven постепенно занял свое место в умах людей. Кто-то из любопытствующих и упорных в итоге разобрался в его внутреннем устройстве. Кто-то просто свыкся. Но время идет, мода меняется. Находятся люди, для которых уже и Мaven - это неудобный и даже архаичный инструмент.

Время не щадит никого...

Рубрики
Java

Duke, Coworking, Widget.

За последнее время прошло несколько важных событий, о которых давно хотел написать.
Про одну новость я уже просто не имею права писать с ещё большей задержкой и опозданием...
Итак, во-первых, самое главное событие о котором я узнал в сентябре — команда российских программистов получила Duke Award 2013 в Сан-Франциско!
DukeAward2013
Ребята занимаются разработкой симулятора для Java Card Runtime Environment (jCardSim), пишут обфускатор и ПО для защиты Андроид приложений от реверса, а также ведут различные, по-настоящему сложные, Java-разработки. Другими словами занимаются не какими-то поделками вроде корпоративного веб-софта, клепание порталов и другими типовыми B2B/E-Commerce/Banking/Enterprise проектами, а работают над фундаментальными разработками и действительно непростыми вещами, в отличие от многих-многих Java программистов Москвы.

Друзья, еще раз мои поздравления!

Во-вторых, мы пытаемся возобновить наши встречи программистов или как сейчас модно говорить MeetUp-ы. Конечно сейчас уже будет не Scala, основы языка мы разобрали давным давно, у некоторых из нас использование Scala стало вполне будничным и хочется чего-то нового. Следующие встречи предполагаем посвятить процессингу, Arduino, малинке (raspberry pi). Всей нашей небольшой группе захотелось двинуться в сторону железок и интерактив арт. Думаю как всегда половина времени уйдет на общение с железками/компьютерами, а половина на распитие кофе и общение на общие темы.
Место встречи — на территории дизайн-завода Флакон или в кофейнях (как старые добрые времена).

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

Наконец последняя новость. Мы недавно с друзьями решили вместе сделать Halloween Widget для Android. Как всегда всё в последний момент, но мне кажется получилось намного круче чем я ожидал.
По сути это тот же робот, только сильно доработанный. Из функциональных вещей добавился фонарик (Бека - спасибо, за пример кода!). Включается нажатием на центральную кнопку. Поскольку фонарик по-сути использует вспышку от встроенной фотокамеры, то для работы потребуется разрешение для работы с камерой: android.permission.CAMERA.

Огромный респект нашему дизайнеру Грише за проделанную работу и Тане, за львиную доли работы по переделке-улучшению UI/UX!
Выкладываю пару картинок, сам виджет можно скачать с маркета и при желании по тестировать.


unnamed unnamed2

Рубрики
Java UML

Про App Widgets в Android

На сегодняшний день уже опубликовано большое количество учебного материала, в котором подробно, по шагам описывается процесс создания простого App Widget в стиле "HelloWorld". Под App Widget-ами подразумеваются приложения, которые можно поместить на основной экран в ОС Android. Как правило это различные индикаторы (состояние батареи, яркость экрана, погода, твитты, пробки и т.д.). На мой взгляд лучшим руководством является статья, размещенная непосредственно на сайте гугл.

Здесь мне хотелось больше уделить внимание общей схеме работы с AppWidget-ами и некоторым особым моментам работы с ними. Публикация сделана на основе моего собственного опыта и актуальна на момент написания публикации, но поскольку платформа Android очень быстро развивается, при изучении этой статьи в будущем прошу сверяться с текущей официальной документацией.

Почему виджеты.

Идея попробовать написать свои виджеты возникла у меня давно, но до реальных опытов руки все-таки не доходили. По большому счету, для меня основным мотивов их создания было и остается любопытство и желание сделать что-то забавное.
В итоге в декабре, перед новым годом все-таки удалось выкроить пару недель для плотной работы с ними.
Для пробы были сделаны и выложены на Маркете пара виджетов (бесплатных):

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

widget_tree
Сhristmas Tree Widget

2. Робот. Тоже пять-в-одном виджет - показывает уровень громкости, заряд батареи, включен ли Wi-Fi, 3G, вибро-режим.

widget_robot
Face Widget

Приложения сейчас не только бесплатные, но и без рекламы. Я просто еще не начинал разбираться с работой AdMob SDK. Возможно, если все-таки разберусь, напишу об этом позже.

Итак.

Принципиальные отличия в работе с виджетами и диаграмма классов.

Рубрики
Java

Липкий запуск сервисов в Android-e.

В Android API есть такой абстрактный класс как Service. Он является наследником ContextWrapper-а, который в свою очередь является наследником Context-a. При некоторых допущениях можно относиться к сервисам как к "активити без UI" (хотя это не совсем правильно в деталях). Использовать сервис рекомендуется для задач не требующих прямого вмешательства пользователя.

В документации особо акцентируется внимание на том, что Service не является ни процессом, ни ниткой. Если сервис должен делать какую-то "тяжелую" работу, то нужно самому выносить ее в отдельный thread, чтобы не получить ANR (Application Not Responding).

Если делать свой thread лениво, то можно использовать готовый класс для асинхронной работы IntentService.

Про него нужно знать следующее:

  • IntentService наследуется от Service.
  • Является абстрактным классом.
  • Нужно реализовать в нём метод onHandleIntent(Intent).
  • Обработка intent-ов идёт поочередно, но не в главном, а в своем выделенном thread-e.

Диаграмма классов сервисов выглядит следующим образом:

Рубрики
Java

Андроидная венгерско-верблюжья нотация

Для разработчиков OC Android, как и для других серьёзных программистов, существует соглашение по оформлению кода.
В целом оно совпадает с соглашением в обычной Java. Как именно нужно оформлять, можно прочитать на официальной странице для Android разработчиков.

Привожу краткую справку по наименованию полей класса:

  • НЕ паблики и НЕ статики должны начинаться с "m" (анг: Non-public, non-static field names start with m).
  • Статики с "s" (Static field names start with s).
  • Все остальные - со строчной буквы (Other fields start with a lower case letter).
  • Константы - БОЛЬШИМИ_БУКВАМИ (Public static final fields (constants) are ALL_CAPS_WITH_UNDERSCORES).

Активно используется верблюжий регистр (Camel Case).

Пример с официального сайта:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

Наверное, гугл не был бы гуглом, если бы не внёс изменения в стандартные соглашения Java. Очень активно используются префиксы.
Для обычных полей "m" (возможно от англ. member), а для статических полей - "s" (static).
Это очень похоже на венгерскую нотацию.

Занятный факт, если пройтись grep-ом по исходникам Android-a, можно найти места, где эти конвенции не соблюдаются. Например статические поля имеют префикс "m":


sources/adndroid-16

./android/app/ActivityThread.java: static ContextImpl mSystemContext = null;
./android/app/SearchManager.java: private static ISearchManager mService;
./android/bluetooth/BluetoothTetheringDataTracker.java: private static String mIface;
./android/content/res/Resources.java: /*package*/ static Resources mSystem = null;
./android/content/res/Resources.java: private static boolean mPreloaded;
./android/database/DatabaseUtils.java: private static Collator mColl = null;
и т.д.

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

UPD! Нужно уточнить, правила действительны для контрибуторов AOSP. То есть тех, кто пишет исходники Андроида.

Рубрики
1. Языки программирования 4. Полезняшки

Тренды программистских вакансий на indeed.com

Пару дней назад прочитал интересную статью на dzone.com с занятными графиками, которые показывали количество вакансий для программистов.

В статье были опубликованы данные с сайтов: indeed.com и simplyhired.com

Оба сайта имеют русскоязычный интерфейс, поэтому надеюсь, что приведенные графики отображали не только данные по США (как часто бывает), а мировые тренды.
Хотя может быть это и не так. К сожалению, не нашел способа получить картинки локализованные под Россию (или Москву).

Меня в первую очередь конечно интересовала java, а также близкие или "конкурирующие" языки программирования.
Поэтому в отличие от оригинальной статье на dzone я оставил в графике от indeed.com только такие языки как: java, C++, C#, objective c (убрал Perl и Visual Basic).

Получилась такая картинка:

Рубрики
3. Инструментарий Java

Density independent pixel (dp) в Android

Вопрос про единицу измерения длины "dp" постоянно появляется на различных форумах и сайтах посвященных разработке приложений под ОС Android.
В большинстве случаев, в качестве ответа более опытные программисты приводят цитату с официального сайта:

Density-independent pixel (dp)
A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a "medium" density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application's UI, to ensure proper display of your UI on screens with different densities.

В целом, мотивы создания dp-единицы описаны довольно ясно. Никому не хочется делать множество различных версток для экранов с разным разрешением и плотностью точек.
Тем не менее, давайте посмотрим более подробно на указанную выше формулу:

px = dp * (dpi / 160)

Здесь главное не запутаться. Если px - это длина в пикселях, а dp - длина в д-пикселях, то для dpi = 240 получится:

px = (dpi/160) * dp = 1.5 * dp

По указанной выше формуле получим, что если dp = 1, то px = 1.5.

Попробуем разобрать ситуацию по существу. Ввод единицы измерения длины dp, нужен был для того, чтобы на экранах с разным количество точек на дюйм размеры графических элементов были одинаковыми.
Например, если взять экран с 160dpi (160 точек на линейный дюйм) и с 240dpi, то для того, чтобы физический размер кнопки был одинаковый, нужно на экране с 240dpi размер кнопки в пикселях делать в 1.5 раз больше.

Здесь важно определиться в понятиях размер, разрешение и плотность:

  • Размеры (3'', 10'' и т.д.) - физические размеры экрана. В Android-е это разные группы ресурсов: small, normal, large, and extra large. Измеряются в dp.
  • Разрешение (QWGA, HVGA и т.д.) - количество точек на экране (например: 1024x720).
  • Плотность (DPI) - количество точек на линейный дюйм. В Android-е это разные группы: ldpi (low), mdpi (medium), hdpi (high), and xhdpi (extra high).

Итак понятно, что единица dp - это такой виртуальный пиксель, который на плотных экранах занимает большее количество пикселей, а на разреженных меньше.
Цель - сохранить единообразие в пользовательском интерфейсе.

Дальше самое интересное. Чтобы лучше понять, как происходит преобразование dp в px на самом деле, думаю имеет смысл посмотреть в исходный код: