Долгое время занимаясь разработкой под Android, я постепенно пришел к выводу, что многих вещей мне, как разработчику, сильно не хватает. Тогда, в начале 2010-го, не хватало только C++ exceptions и RTTI. Без них любой нетривиальный C++ код просто не мог быть портирован на Android и требовал практически полного переписывания. Это было очень существенное ограничение, которое никак не давало мне покоя. К счастью, Android — это open source, поэтому, вооружившись максимой «если тебе что-то нужно, сделай это сам», я засел за работу. К моему удивлению, сделать поддержку полноценного C++ с исключениями и RTTI оказалось довольно несложно. Потребовалось всего около недели работы. Далее был сделан сайт, на который и были выложены получившиеся пакеты для Windows, Linux и Mac OS X, а также патч и инструкция по сборке.
Проблема была актуальной, поэтому проект получился весьма популярным и в течении первого же месяца я получил огромное количество писем с вопросами, просьбами о дополнительной функциональности и просто благодарностями. Одним из самых часто задаваемых вопросов был «включит ли Google ваши изменения в mainline?». Я честно отвечал, что не знаю, но надежда есть, т.к. David Turner (system architect of Android) весьма заинтересовался моими патчами и обещал уделить им внимание.
Скоро сказка сказывается, да не скоро дело делается. У корпорации добра нашлось немало более неотложных задач, поэтому мои патчи были втянуты в mainline лишь около года спустя. За это время вышло два релиза от Google (r3 и r4) и я их адаптировал так же, как и r2. Измененные пакеты вместе с патчами и инструкциями по сборке я продолжал выкладывать на своем сайте. Таким образом, за это время набралась довольно большая аудитория пользователей, которым были нужны эти возможности. С использованием моего NDK многие Open Source и коммерческие проекты смогли портировать свой код на Android. Среди Open Source наиболее известные, пожалуй, OpenCV и Ogre3D.
Наконец, с выходом NDK r5 Google интегрировал мои патчи (почти без изменений) в mainline. Казалось бы, тут и конец проекту, но к тому моменту накопилось довольно много других претензий к NDK. Не хватало многого, и поэтому я решил продолжать проект, переориентировав его с «Android NDK + full C++» на «Улучшенный Android NDK», т.к. убедился по опыту, что это наилучший способ добиться внесения этих изменений в mainline. Конкретный список улучшений постоянно дополняется, его можно увидеть на моем сайте на страничке конкретного релиза.
Итак, здесь я опишу последовательно основные улучшения в CrystaX NDK, отличающие его от Google NDK.
Не знаю почему, но в Google NDK была совершена диверсия — wchar_t был сделан размером в 1 байт. Объяснения, которые давал David Turner, меня не удовлетворили. Он упирал на то, что код, использующий wchar_t, как правило непортируемый, поэтому нужно использовать UTF-8, хранимый в обычных char строках. Нисколько не протестуя против использования UTF-8, я, однако, указал ему, что непортируемым код с wchar_t делают именно такие решения как в Google NDK. Особой дискуссии не получилось, каждый остался при своем мнении, но я понял — поддержка широких символов/строк/потоков необходима.
Основываясь на коде из FreeBSD, я начал разрабатывать поддержку wchar_t в Standard C library, а заодно и Std C locales. Задача была непростой и потребовалось несколько месяцев, прежде чем я получил стабильную реализацию. К сожалению, она все еще не полна (полной поддержки локалей все еще нет, поддерживается только UTF-8), но на практике ее уже более чем достаточно для многих проектов. Я сам участвовал в проектах, где активно использовались wide characters/string/streams, а также знаю из писем воспользовавшихся моей реализацией.
С выходом нового международного стандарта С++ захотелось использовать новые возможности языка и стандартной библиотеки. К сожалению, Google NDK основывается на GCC 4.4.3, который, хоть и включает некоторые возможности C++0x, тем не менее несколько устарел. Поэтому было принято решение о добавлении нового, основанного на GCC 4.6.3 toolchain-а в CrystaX NDK. Сказано — сделано. На данный момент CrystaX NDK содержит две версии компилятора — 4.4.3 (как в Google) и 4.6.3 (новый). Переключение между ними осуществляется очень просто — в Application.mk вашего проекта достаточно написать:
Обратите внимание, что этого недостаточно, чтоб использовать возможности C++0x. Включить C++0x нужно явно (тоже в Application.mk):
В CrystaX NDK GCC 4.6.3 собран с поддержкой Graphite optimization framework. Долго говорить тут не о чем, поэтому просто дам ссылку на страничку wiki
Google NDK поддерживает только C и C++ для нативной разработки. Это, конечно же, неплохо, но в какой-то момент обнаружилось, что есть довольно много кода, изначально написанного для iOS на Objective-C, который теперь надо бы портировать на Android. Можно, конечно, пойти традиционным путем и переписать его с нуля, а можно добавить поддержку Objective-C в Android. Поразмыслив, я пришел к выводу, что наличие возможности лучше чем ее отсутствие и приступил к реализации этой идеи. На данный момент портирован компилятор Objective-C и базовая GNU Objective-C library. В планах — порт GNUStep на Android (тогда можно будет переносить Cocoa код с минимальными изменениями), однако эта задача немаленькая и, очевидно, займет некоторое время. Как бы то ни было, вы можете начать использовать Objective-C на Android прямо сейчас — просто добавьте в LOCAL_SRC_FILES исходники с расширениями .m (Objective-C) или .mm (Objective-C++).
Я, как и многие разработчики, немного параноик. Я никогда не бываю уверен на все сто в своем коде — слишком часто натыкался на глупые ошибки, как свои, так и чужие. Поэтому, следую главному правилу — «Слишком много проверок кода не бывает». Существует довольно много различных анализаторов кода, но для Android я решил начать с Clang static code analyzer. Этот проект активно развивается и уже весьма неплохо анализирует C и Objective-C код (C++, к сожалению, пока еще нет). Чтобы воспользоваться данной возможностью, нужно прежде всего установить Clang, убедиться, что скрипт 'scan-build' из пакета Clang доступен из командной строки, и запустить сборку Android проекта:
Эта команда соберет Android проект как обычно, но также параллельно проанализирует его с помощью Clang анализатора и, в случае найденных ошибок, отрапортует о них в конце.
Я планирую добавить поддержку других анализаторов, но пока конкретики на этот счет нет.
Эта возможность еще не готова к полноценному использованию и находится в активной разработке, но я все-таки решил о ней упомянуть — как знать, возможно она сэкономит кому-нибудь недели или месяцы работы.
В приложении, собранном с помощью CrystaX NDK, все вызовы IO API (такие как open, read, write и т.д.) перехватываются и анализируются. Это сделано для встраивания так называемых «драйверов» — фильтров, которые могут прозрачным для приложения образом изменить записываемые/считываемые данные. Например, для компрессии или шифрования данных. Обратите внимание, что изменения кода приложения при этом не требуется! Всего лишь надо будет вставить в код на старте что-то типа:
и вуаля — все данные, записанные в /target будут автоматически сжиматься gzip-ом, а прочитанные из /target — автоматически разжиматься.
Напоминаю еще раз — эта возможность еще не готова, но находится на финальной стадии разработки. Вполне возможно, в течение ближайшего месяца она стабилизируется. Однако возможно, что кому-то она покажется пригодной к использованию прямо сейчас — что ж, я не против.
Чем больше я занимаюсь платформой Android, тем больше возникает неудовлетворенных потребностей. Пока у меня хватает сил и времени реализовывать все самому, но очевидно, что это не может продолжаться вечно. Поэтому я призываю энтузиастов участвовать в проекте — давайте сделаем Android такой платформой, как нам хочется! В конце концов, в этом и состоит главнейшее преимущество Android перед iOS и Blackberry — мы можем сделать такую OS и такие инструменты для нее, как нужно нам, а не вендору. Вперед!
На этой радостной ноте позвольте откланяться.
Проблема была актуальной, поэтому проект получился весьма популярным и в течении первого же месяца я получил огромное количество писем с вопросами, просьбами о дополнительной функциональности и просто благодарностями. Одним из самых часто задаваемых вопросов был «включит ли Google ваши изменения в mainline?». Я честно отвечал, что не знаю, но надежда есть, т.к. David Turner (system architect of Android) весьма заинтересовался моими патчами и обещал уделить им внимание.
Скоро сказка сказывается, да не скоро дело делается. У корпорации добра нашлось немало более неотложных задач, поэтому мои патчи были втянуты в mainline лишь около года спустя. За это время вышло два релиза от Google (r3 и r4) и я их адаптировал так же, как и r2. Измененные пакеты вместе с патчами и инструкциями по сборке я продолжал выкладывать на своем сайте. Таким образом, за это время набралась довольно большая аудитория пользователей, которым были нужны эти возможности. С использованием моего NDK многие Open Source и коммерческие проекты смогли портировать свой код на Android. Среди Open Source наиболее известные, пожалуй, OpenCV и Ogre3D.
Наконец, с выходом NDK r5 Google интегрировал мои патчи (почти без изменений) в mainline. Казалось бы, тут и конец проекту, но к тому моменту накопилось довольно много других претензий к NDK. Не хватало многого, и поэтому я решил продолжать проект, переориентировав его с «Android NDK + full C++» на «Улучшенный Android NDK», т.к. убедился по опыту, что это наилучший способ добиться внесения этих изменений в mainline. Конкретный список улучшений постоянно дополняется, его можно увидеть на моем сайте на страничке конкретного релиза.
Итак, здесь я опишу последовательно основные улучшения в CrystaX NDK, отличающие его от Google NDK.
Поддержка wide characters
Не знаю почему, но в Google NDK была совершена диверсия — wchar_t был сделан размером в 1 байт. Объяснения, которые давал David Turner, меня не удовлетворили. Он упирал на то, что код, использующий wchar_t, как правило непортируемый, поэтому нужно использовать UTF-8, хранимый в обычных char строках. Нисколько не протестуя против использования UTF-8, я, однако, указал ему, что непортируемым код с wchar_t делают именно такие решения как в Google NDK. Особой дискуссии не получилось, каждый остался при своем мнении, но я понял — поддержка широких символов/строк/потоков необходима.
Основываясь на коде из FreeBSD, я начал разрабатывать поддержку wchar_t в Standard C library, а заодно и Std C locales. Задача была непростой и потребовалось несколько месяцев, прежде чем я получил стабильную реализацию. К сожалению, она все еще не полна (полной поддержки локалей все еще нет, поддерживается только UTF-8), но на практике ее уже более чем достаточно для многих проектов. Я сам участвовал в проектах, где активно использовались wide characters/string/streams, а также знаю из писем воспользовавшихся моей реализацией.
С++ 11
С выходом нового международного стандарта С++ захотелось использовать новые возможности языка и стандартной библиотеки. К сожалению, Google NDK основывается на GCC 4.4.3, который, хоть и включает некоторые возможности C++0x, тем не менее несколько устарел. Поэтому было принято решение о добавлении нового, основанного на GCC 4.6.3 toolchain-а в CrystaX NDK. Сказано — сделано. На данный момент CrystaX NDK содержит две версии компилятора — 4.4.3 (как в Google) и 4.6.3 (новый). Переключение между ними осуществляется очень просто — в Application.mk вашего проекта достаточно написать:
APP_TOOLCHAIN_VERSION := 4.6.3
Обратите внимание, что этого недостаточно, чтоб использовать возможности C++0x. Включить C++0x нужно явно (тоже в Application.mk):
APP_USE_CPP0X := true
Graphite optimization framework
В CrystaX NDK GCC 4.6.3 собран с поддержкой Graphite optimization framework. Долго говорить тут не о чем, поэтому просто дам ссылку на страничку wiki
Objective-C
Google NDK поддерживает только C и C++ для нативной разработки. Это, конечно же, неплохо, но в какой-то момент обнаружилось, что есть довольно много кода, изначально написанного для iOS на Objective-C, который теперь надо бы портировать на Android. Можно, конечно, пойти традиционным путем и переписать его с нуля, а можно добавить поддержку Objective-C в Android. Поразмыслив, я пришел к выводу, что наличие возможности лучше чем ее отсутствие и приступил к реализации этой идеи. На данный момент портирован компилятор Objective-C и базовая GNU Objective-C library. В планах — порт GNUStep на Android (тогда можно будет переносить Cocoa код с минимальными изменениями), однако эта задача немаленькая и, очевидно, займет некоторое время. Как бы то ни было, вы можете начать использовать Objective-C на Android прямо сейчас — просто добавьте в LOCAL_SRC_FILES исходники с расширениями .m (Objective-C) или .mm (Objective-C++).
Статический анализ кода
Я, как и многие разработчики, немного параноик. Я никогда не бываю уверен на все сто в своем коде — слишком часто натыкался на глупые ошибки, как свои, так и чужие. Поэтому, следую главному правилу — «Слишком много проверок кода не бывает». Существует довольно много различных анализаторов кода, но для Android я решил начать с Clang static code analyzer. Этот проект активно развивается и уже весьма неплохо анализирует C и Objective-C код (C++, к сожалению, пока еще нет). Чтобы воспользоваться данной возможностью, нужно прежде всего установить Clang, убедиться, что скрипт 'scan-build' из пакета Clang доступен из командной строки, и запустить сборку Android проекта:
ndk-build ANALYZE=1
Эта команда соберет Android проект как обычно, но также параллельно проанализирует его с помощью Clang анализатора и, в случае найденных ошибок, отрапортует о них в конце.
Я планирую добавить поддержку других анализаторов, но пока конкретики на этот счет нет.
Драйвер файловой системы на уровне приложения
Эта возможность еще не готова к полноценному использованию и находится в активной разработке, но я все-таки решил о ней упомянуть — как знать, возможно она сэкономит кому-нибудь недели или месяцы работы.
В приложении, собранном с помощью CrystaX NDK, все вызовы IO API (такие как open, read, write и т.д.) перехватываются и анализируются. Это сделано для встраивания так называемых «драйверов» — фильтров, которые могут прозрачным для приложения образом изменить записываемые/считываемые данные. Например, для компрессии или шифрования данных. Обратите внимание, что изменения кода приложения при этом не требуется! Всего лишь надо будет вставить в код на старте что-то типа:
mount("/path/to/storage", "/target", "compressed", 0, "GZIP")
и вуаля — все данные, записанные в /target будут автоматически сжиматься gzip-ом, а прочитанные из /target — автоматически разжиматься.
Напоминаю еще раз — эта возможность еще не готова, но находится на финальной стадии разработки. Вполне возможно, в течение ближайшего месяца она стабилизируется. Однако возможно, что кому-то она покажется пригодной к использованию прямо сейчас — что ж, я не против.
Заключение
Чем больше я занимаюсь платформой Android, тем больше возникает неудовлетворенных потребностей. Пока у меня хватает сил и времени реализовывать все самому, но очевидно, что это не может продолжаться вечно. Поэтому я призываю энтузиастов участвовать в проекте — давайте сделаем Android такой платформой, как нам хочется! В конце концов, в этом и состоит главнейшее преимущество Android перед iOS и Blackberry — мы можем сделать такую OS и такие инструменты для нее, как нужно нам, а не вендору. Вперед!
На этой радостной ноте позвольте откланяться.