LXF157:ARM и Android:Программирование
|
|
|
Ваши программные наработки не пропадут даром
ARM и Android: Программирование
Часть 3: Андрей Боровский считывает данные из видеобуфера Android, углубляясь в механизм взаимодействия приложений Linux и системы Android.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Научившись считывать данные из видеобуфера Android и выводить графику на экран устройства, вы сами сможете написать программу, которую на Google Play продают за деньги. Для этого в наше блюдо, приготовленное на C, придется добавить щепотку Java.
Стоит отметить, что от релиза к релизу в составе средств разработки для Android появляется все больше возможностей взаимодействия с системой, предназначенных для С и С++. Вероятно, разработчики Android уже поняли, что они несколько погорячились, оставив возможность разработки для своей платформы только на Java. Уже сейчас под Android можно написать полноценное приложение на С и С++, не содержащее ни одной строчки кода на Java. Правда, код Java все равно будет добавлен в это приложение (компоновщиком) для того, чтобы обеспечить его взаимодействие с остальными компонентами системы. В будущем, возможно, мы увидим интерфейс программирования, предназначенный исключительно для языков, компилируемых в машинные коды.
Программирование для Android при помощи стандартных средств взаимодействия С++ и Java подробно описано в документации и сопровождается примерами, так что останавливаться на нем мы не будем. Вместо этого мы рассмотрим некоторые альтернативные методы и средства разработки, которые хотя и не являются частью Android API, но, тем не менее, позволяют решать многие задачи, возникающие перед программистами.
Как я и обещал в прошлый раз, мы сосредоточимся на работе с графикой.
На рисунке показана приблизительная схема взаимодействия компонентов графической системы Android между собой. Эту схему можно разделить на три уровня. На самом верхнем уровне располагаются приложения Android, написанные на Java (возможно, с использованием библиотек, написанных на C и C++ и вызываемых с помощью JNI). Ниже на схеме показан интерфейс, предназначенный для организации взаимодействия графической системы Java и ядра Linux. Этот интерфейс состоит из нескольких компонентов, каждый из которых реализован в собственной библиотеке, написанной на C или C++. Данные библиотеки являются обычными разделяемыми библиотеками Linux, и наши программы могут получить прямой доступ к любой из них, хотя пользы от этого будет, вероятно, немного.
Обратите внимание на компоненты SurfaeFlinger и PixelFlinger. По какой-то причине слово «flinger» полюбилось разработчикам Android, и они широко его используют. На русский язык точнее всего было бы перевести эти названия как «жонглер поверхностями» (имеются в виду виртуальные поверхности, на которые приложения выводят графику) и «жонглер пикселями». Помимо прочего, компоненты промежуточного слоя выполняют программную эмуляцию отсутствующих компонентов графического оборудования. Например, PixelFlinger может эмулировать поддержку трехмерной графики, если в системе отсутствует аппаратная поддержка 3D. К этому же уровню я отнес файлы-устройства, предоставляющие непосредственный доступ к видеобуферу системы, хотя, возможно, их следовало бы поместить уровнем ниже.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
В соответствии с показанной трехуровневой системой, у разработчика приложений на С и С++ есть три пути взаимодействия с графическими компонентами Android.
Первый путь заключатся в том, чтобы использовать код, написанный на C и C++ внутри приложения Java, играющего роль обертки. Этот путь хорош тем, что приложение ведет себя по правилам, установленным системой, а значит, в процессе его работы мы можем избежать неприятных сюрпризов.
Второй путь заключается в получении доступа к библиотекам, реализующим интерфейс между Java и ядром системы. Как уже отмечалось, эти библиотеки вполне доступны нашим программам. Недостаток данного подхода заключается в том, что программы, взаимодействующие с нижними уровнями абстракции, не учитывают события, происходящие на более высоких уровнях, что может привести к конфликтам и непредвиденному поведению этих программ. И это не говоря о том, что сам промежуточный уровень, не являющийся частью официального API Android, может быть подвержен неожиданным и плохо документированным изменениям.
Третий путь заключается в непосредственном доступе к устройству видеобуфера. Для решения некоторых задач этот способ может быть приемлем, но в общем случае он страдает тем же недостатком, что и способ, описанный выше: программа, обращающаяся к буферу, неизбежно конфликтует с другими программами, которые работают с системой на высоком уровне.
Из перечисленных выше трех подходов к работе с графикой мы рассмотрим два наиболее рациональных: прямое взаимодействие с видеобуфером и использование тонкой оболочки Java вокруг кода программы, написанного на C. Заодно мы рассмотрим другой, более простой способ написания Make-файлов для сборки приложений Linux под Android. В отличие от рассмотренного в предыдущей статье, этот способ скрывает от нас многие интересные детали и работает только с NDK, зато позволяет создавать более компактные Make-файлы.
Считывание данных из видеобуфера
Устройство видеобуфера /dev/graphics/fb0 позволяет получить прямой доступ к тому, что происходит на экране устройства Android. Данные, которые можно считать из файла /dev/graphics/fb0, представляют собой поток значений пикселей, где первым идет верхний левый пиксель, а последним – нижний правый. К этому надо добавить, что, поскольку устройства Android используют двойную буферизацию, из файла /dev/graphis/fb0 можно прочитать содержимое сразу двух кадров видеобуфера. Чтобы преобразовать этот поток «сырой» информации в наглядный графический формат, необходимо знать по крайней мере два параметра видеосистемы устройства: формат хранения пикселей, который тесно связан с количеством цветов, отображаемых устройством, и разрешение экрана устройства по вертикали и по горизонтали. Пиксели на экранах Android могут быть представлены 16-ю, 24-мя и 32-мя битами. Количество пикселей на экране колеблется от 240 × 320 до 800 × 1280.
Рассмотрим фрагмент программы fboutput, которая делает снимок экрана устройства и сохраняет его на диске в формате BMP. Весь текст программы вы найдете на прилагающемся диске, в то время как фрагмент, представленный ниже, определяет параметры видеобуфера.
int fd;
fb_fix_screeninfo fi;
fb_var_screeninfo vi;
fd = open(“/dev/graphics/fb0”, O_RDWR);
ioctl(fd, FBIOGET_FSCREENINFO, &fi);
ioctl(fd, FBIOGET_VSCREENINFO, &vi);
Мы открываем файл /dev/graphics/fb0 как обычный файл Linux, а затем делаем два вызова ioctl(). Первый вызов позволит получить фиксированные характеристики экрана, второй – переменные характеристики. Структуры, в которые вызовы записывают данные (переменные fi и vi соответственно), имеют тип fb_fix_screeninfo и fb_var_screeninfo. Оба типа объявлены в заголовочном файле /include/linux/fb.h. Мы не будем описывать все поля этих структур; скажем только, что после выполнения приведенных выше вызовов ioctl() переменная vi.xres будет одержать число пиксельных столбцов в данном режиме работы экрана устройства, vi.yres будет одержать число строк, а переменная vi.bits_per_pixel – число битов на один пиксель. Переменная fi.smem_len будет содержать размер видеобуфера в байтах (в данном случае – объем памяти, необходимый для хранения двух кадров буфера).
Самый простой и быстрый способ получить доступ к данным видеобуфера заключается в том, чтобы отобразить память видеобуфера в память нашей программы:
void * bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
В результате указатель bits указывает на начало области данных буфера. Объем памяти, занимаемый одним кадром, можно посчитать по формуле
vi.xres*vi.yres*(vi.bits_per_pixel << 3).
Для получения скриншота нам осталось считать данные одного кадра из буфера и конвертировать их в формат BMP. Все эти операции проделывает программа fboutput, и останавливаться на них подробно мы не будем.
Система позволяет нам не только считывать, но и записывать данные в видеобуфер, что должно соответствовать выводу пикселей на экран, однако на практике ничего путного из этого не выйдет, так как наша система вывода будет конфликтовать с выводом других программ.
Мы могли бы собрать программу fboutput, используя в качестве шаблона Make-файл, введенный в предыдущей статье, но поступим иначе – воспользуемся системой сборки программ NDK/SDK, которая также пригодится нам в следующем примере. Прежде всего, стандартная система сборки Android предполагает, что собираемые файлы расположены в определенных директориях. В случае программы fboutput, которая все же не является стандартным приложением Android, структура проекта выглядит довольно просто. В директории проекта расположена поддиректория jni (она называется так потому, что обычно содержит код, к которому остальные части приложения обращаются помощью одноименного механизма), где содержится файл исходных текстов нашей программы, а также два файла сценариев сборки – Android.mk и Appliation.mk. Содержимое файла Appliation.mk выглядит не просто, а очень просто:
APP_PLATFORM := android-9
NDK_APP_OUT := ./
APP_ABI := armeabi
Нетрудно догадаться, что в этом файле мы указываем версию API (в нашем случае – android-9), корневую директорию для результатов сборки и формат двоичного кода, который мы хотим получить (armeabi).
Файл Android.mk несколько сложнее:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fbout
LOCAL_SRC_FILES := capturescr.c
include $(BUILD_EXECUTABLE)
Переменная LOCAL_MODULE содержит имя, которое будет присвоено двоичному файлу, полученному в результате сборки. Переменной LOCAL_SRC_FILES следует присвоить список файлов исходных текстов нашей программы. Отдельного внимания заслуживает последняя строчка, которая определяет, чем именно должен быть результат сборки. Поскольку обычно код C и C++ используется в приложениях Android в форме разделяемых библиотек, в большинстве случаев эта строка будет выглядеть как
include $(BUILD_SHARED_LIBRARY)
но мы хотим получить на выходе файл самостоятельной программы и поэтому используем константу BUILD_EXECUTABLE.
Все, что нам теперь осталось сделать – перейти в корневую директорию нашего проекта и вызвать в ней скрипт ndk-build, входящий в состав NDK. Если сборка прошла успешно, то в поддиректории корневой директории проекта libs/armeabi появится исполняемый файл нашей программы. Не удивляйтесь, что файл появился в директории libs: все-таки основное предназначение NDK – сборка разделяемых библиотек. Тем не менее, NDK знает о том, что собирает исполняемый файл программы. В процессе сборки программа автоматически компонуется с фрагментом кода, содержащим функцию _start(), которая вызывает нашу функцию main(). Скомпилированную программу нужно загрузить в устройство Android (или его эмулятор) и в окне консоли Android командовать нечто наподобие
fboutoutput screenshot.bmp.
В результате будет создан файл screenshot.bmp, содержащий снимок экрана устройства.
Работа с графикой с помощью SDL
Как мы уже видели, нашим программам доступны все средства вывода графики Android, начиная с OpenGL ES и заканчивая видеобуфером. Осталось только встроить графический вывод нашего приложения в общую механику работы операционной системы. Самый простой способ сделать это – написать обертку на Java, которая будет вызывать полезный код нашей программы с помощью JNI. Поскольку одна из задач, которую мы ставим перед собой, заключается в упрощении переноса на Android программ, предназначенных для настольных версий Linux, возникает вопрос, насколько должен измениться код программы, написанной на C или C++. Ответ на этот вопрос зависит от того, насколько переносимая программа по своему поведению должна быть похожа на стандартное приложение Android.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
В самом простом случае код программы, написанной на C или C++, не придется менять вообще. Но полученная в результате программа будет вести себя не совсем так, как обычные приложения Android. Более глубокая «андроидизация» программ потребует внесения изменений в их архитектуру в соответствии с требованиями ОС Android.
В качестве примера программы, работающей с графикой, которую можно с минимальными изменениями перенести на платформу Android, мы рассмотрим простую программу, использующую библиотеку SDL (www.libsdl.org). Выбор SDL не случаен. Эта библиотека уже давно используется программистами-умельцами в процессе переноса игр на мобильные платформы (достаточно назвать игру OpenTTD, в которую можно поиграть и на платформе Android). Кроме того, начиная с новейшей версии SDL – SDL 2, платформа Android поддерживается библиотекой официально. Итак, допустим, мы хотим портировать на Android простую программу, написанную с использованием SDL:
#include “SDL.h” int SDL_main(int argc, char *argv[]) { SDL_Window *win = NULL; SDL_Renderer *renderer = NULL; SDL_Texture *bitmapTex = NULL; SDL_Surface *bitmapSurface = NULL; SDL_Rect rect; int running = 1; SDL_Init(SDL_INIT_VIDEO); win = SDL_CreateWindow(“SDL for Android Hello World”, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 480, SDL_WINDOW_SHOWN); renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); bitmapSurface = SDL_LoadBMP(“data/android.bmp”); bitmapTex = SDL_CreateTextureFromSurface(renderer, bitmapSurface); SDL_FreeSurface(bitmapSurface); while (1) { SDL_Event e; if (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { break; } } SDL_RenderClear(renderer); SDL_RenderCopy(renderer, bitmapTex, NULL, NULL); SDL_RenderPresent(renderer); } SDL_DestroyTexture(bitmapTex); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(win); return 0; }
Программа целиком позаимствована из документации SDL. Единственное, что она делает – выводит на экран картинку, загруженную из файла data/android.bmp. Поскольку на этот раз результатом сборки станет программа с расширением APK, нам придется выполнить все формальности, связанные с созданием проекта приложения Android: организовать структуру директорий, как того требует система сборки Android, и добавить необходимые файлы, описывающие проект. Все это вы найдете на диске (проект называется sdl-android). Все файлы находятся на своих местах, за исключением исходных текстов библиотеки SDL. Я не стал включать их потому, что SDL 2.0 все еще пребывает в состоянии разработки и к моменту выхода журнала поддержка Android в этой библиотеке, скорее всего, будет расширена и улучшена.
Итак, рассмотрим бегло структуру проекта приложения SDL-Android. Исходный текст самой программы находится в директории jni/src. В директории jni/SDL нужно разместить исходные тексты библиотеки SDL, произведя в них небольшие изменения (описанные в файле README для Android, входящем в состав дистрибутива библиотеки). В директории src находится файл исходных текстов на языке Java. Этот файл реализует ту самую «обертку», о которой говорилось выше. В директории assets/data находится файл BMP, из которого программа загружает изображение. Во время выполнения собранной программы этот файл будет загружаться из системы ресурсов приложения.
Приступим к сборке. Сначала, с помощью знакомого нам скрипта ndk-build, мы соберем ту часть кода, которая написана на C. В результате у нас появятся две разделяемые библиотеки: libSDL2.so и libmain.so. Вторая библиотека содержит код нашей программы, то есть функцию SDL_main().
Обратите, кстати, внимание на новые директивы в файле jni/src/Android.mk:
LOCAL_SHARED_LIBRARIES := SDL2
LOCAL_LDLIBS := -lGLESv1_CM –llog
Первая директива указывает на то, что библиотеку libmain.so следует связать с библиотекой libSDL2.so, собранной в рамках этого же проекта. Вторая директива указывает на необходимость подключения библиотек libGLESv1_CM.so и liblog.so, которые являются частью Android.
Теперь нам придется воспользоваться инструментом, которым мы прежде не пользовались – программой ant. Эта программа предназначена для сборки проектов Java; она же создаст нам файл с расширением APK. В корневой директории проекта командуем
ant -debug
или
ant -release
И получаем файл APK, который можно установить на устройство Android так же, как и любое другое приложение.
Написание программ для Android на C требуется не только хакерам и тем, кто хочет перенести на мобильную платформу написанный ранее код. Только используя C и C++, мы можем сделать то, что часто требуется в высокопроизводительных программах: обратиться напрямую к возможностям процессора ARM. Об этом – в следующей части.