01 мая 2012

Android: Hello world!

Привет. Вот и пришло время первого очень простого приложения для android. Это на самом деле не так легко как может показаться на первый взгляд. Ведь мало сделать Hello world!, нужно сделать его правильно.

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

  1. Игры должны работать быстро - только нативный код.
  2. Нативный код - только оптимизированный.
  3. Оптимизированный код - потребляет мало памяти и работает быстро.
  4. Работает быстро - DOD (Data Oriented Design) и простота во всем.

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

Создание проекта

Все что нужно для создание проекта для андройда, это использовать вот эту команду:

android create project --target android-8 --path ./hello --activity Hello --package com.plambir

В результате в папке hello будет следующее:

▾ hello/ -- собственно сам проект
  ▾ bin/ -- сюда будет скомпилированно наше приложение
  ▾ libs/ -- библиотеки
  ▾ res/ -- ресурсы приложения
    ▾ layout/
        main.xml -- наш UI
    ▾ values/
        strings.xml -- строки
  ▾ src/
    ▾ com/
      ▾ plambir/
          Hello.java -- Исходный код
    AndroidManifest.xml -- файл с манифестом
    ant.properties -- настройки для ant
    build.xml -- скрипт сборки
    local.properties -- локальные настройки
    proguard-project.txt -- ProGuard
    project.properties -- настройки проекта

Сразу хочу сказать что local.properties не нужно помещать в систему контроля версий.

Давайте попробуем собрать проект с помощью команды ant debug и установим наше приложение на устройство (реальное или эмулятор, не важно).

Установка осуществляется просто adb install bin/Hello-debug.apk. Можно так же с помощью adb запустить приложение с помощью уже более хитрой команды adb shell am start -n com.plambir/com.plambir.Hello.

Что бы удалить приложение с телефона нужно выполнить adb uninstall com.plambir.

JNI и нативный код

Создаем папку jni. В ней создаем Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_CPP_EXTENSION := .cc
LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.cc

include $(BUILD_SHARED_LIBRARY)

Теперь создаем файл hello-jni.cc:

#include <string.h>
#include <jni.h>

extern "C"
{

  jstring Java_com_plambir_Hello_stringFromJNI( JNIEnv* env, jobject thiz );

}

jstring Java_com_plambir_Hello_stringFromJNI( JNIEnv* env, jobject thiz )
{
  return env->NewStringUTF("Hello World, Hello! (JNI)");
}

Для того что бы использовать этот код в приложении нам придется замараться с Java. Hello.java претерпит изменения и станет таким:

package com.plambir;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class Hello extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    public native String  stringFromJNI();

    static {
        System.loadLibrary("hello-jni");
    }
}

На этом пожалуй пока хватит. Гораздо больше и подробнее можно найти в документации и в примерах NDK. В качестве бонуса, вот мой SConstruct который я использовал для данного примера.

import os

env = DefaultEnvironment(ENV = os.environ)

env['ENV']['PATH'] += ":" + ":".join([
    '/home/user/.android/android-ndk-r7c/',
    '/home/user/.android/android-sdk-linux/tools/',
    '/home/user/.android/android-sdk-linux/platform-tools/'])

ndk_build = env.Command(
        'libs/armeabi/libhello-jni.so',
        Glob('jni/*.cc'),
        'ndk-build NDK_DEBUG=1')

ant = env.Command(
        'bin/Hello-debug.apk',
        Glob('src/com/plambir/*.java'),
        'ant debug')

install = env.Command(
        'install_run',
        ant,
        'adb install bin/Hello-debug.apk')

run = env.Command(
        'run_run',
        install,
        'adb shell am start -n com.plambir/com.plambir.Hello')

uninstall = env.Command(
        'unistall_run',
        'SConstruct',
        'adb uninstall com.plambir')

env.Alias('install', install)
env.Alias('run', run)
env.Alias('uninstall', uninstall)

Depends(ant, ndk_build)

Default(ant)

Отладка нативного кода

Тут все достаточно просто. Для начала, в AndroidManifest.xml добавим android:debuggable="true" в <application> в качестве атрибута.

После соберите приложение с помощью ndk_build NDK_DEBUG=1 и установите его на устройство.

Запуск производиться с помощью ndk-gdb --start. Под капотом славненький gdb. Вы можете конечно сказать: "фуу, он же не удобный". На что я могу ответить: "сам такой". gdb очень мощный инструмент, один раз освоив его вы уже не сможете променять его ни на что другое. Для слабых духом есть возможность использовать cgdb.

Вместо заключения

Дальше по мере появления свободного времени расскажу про фазы активности приложения и о том как это влияет на его работу.

В любом случае перерыв был долгим и накопилось очень много всего интересного о чем хотелось бы рассказать.