2015-06-12 , ,

Android SDK プロジェクトの作り方, バージョン管理, local.propertiesアップデート, キーの生成, NDKまで

Android SDKのコマンドラインを使ったAndroidアプリプロジェクトの作成方法を記しておきます。しばらく離れているとすぐに忘れるので備忘録として(というかこうして書くことでより記憶に定着させるため)。

前提: SDK, JDK, ant等必要な物をインストールしてパスを通してあること

プロジェクトの作成

android --help create project

で、ヘルプが出るので参考に。

android※1 create project -n MyAwesomeApp -t 10※2  -p ./MyAwesomeApp※3 -k com.example.my_awesome_app -a MyAwesomeApp※4
  • ※1: Cygwin等からはandroid.batとするとよい。
  • ※2: -tの値(target ID)は環境によって異なる? android list targets で表示される。
  • ※3: カレントディレクトリへ作るなら単にピリオドのみでOK。
  • ※4: Activity名。デフォルトでアプリアイコンの下に表示される名前になる。

参考: http://developer.android.com/tools/help/android.html

コンパイルは次のようにする。

cd MyAwesomeApp
ant debug

インストールはAVDか端末を接続した上で ant debug install とする。

バージョン管理に入れる(Subversionやgit等)

バージョン管理では次のファイルを無視する(.gitignore等)。

  • local.properties
  • bin
  • gen
  • libs
  • obj (NDK使用時)

バージョン管理から取り出したときの作業

リポドシリから取り出したときはlocal.propertiesが無いので生成する必要がある。

cd MyAwesomeApp
android update project -p .

キーストアの情報が必要ならlocal.propertiesへ追記する。

キーの生成方法

キーの生成は次のようにする。(keytoolはJDKのbinの中)

keytool -genkey -v -keystore my_awesome_app.keystore -alias my_awesome_app -keyalg RSA -keysize 2048 -validity 10000

local.propertiesにキーの情報を追加する。

key.store=my_awesome_app.keystore
key.alias=my_awesome_app

次のようにしてリリースビルドできるようになるはず。

cd MyAwesomeApp
ant release

参考: http://developer.android.com/tools/publishing/app-signing.html

NDK

NDKを使いたい場合はjniディレクトリを作る。NDKのsamplesディレクトリを参考にすると良い。

プロジェクトディレクトリ下にjniディレクトリを作り、次のファイルを配置する。

  • Android.mk
  • Application.mk
  • *.cpp, *.h等

ビルドはプロジェクトディレクトリトップで ndk-build とし、成功したら、普通に ant debug 等。(参考: https://developer.android.com/ndk/guides/ndk-build.html)

AndroidManifest.xml は必要に応じて修正すること。特にNativeActivityを使う場合は修正が必要。

各ファイルの例を次に記す。(android-ndk-r10e時点。バージョンによってオプションは変わっていくかも?)

jni/Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_CPP_FEATURES += exceptions
LOCAL_CPP_FEATURES += rtti
LOCAL_MODULE := my-awesome-app-jni
LOCAL_SRC_FILES := my-awesome-app-jni.cpp

include $(BUILD_SHARED_LIBRARY)
# include $(BUILD_EXECUTABLE) ←main()から実行する場合はこちら。

例外やRTTIのないC++なんて98年以前の感じですね。今更耐えられないので有効。(r10e現在デフォルト無効)

現在のバージョンではC++11はデフォルトで使えるらしいです。ライブラリはまた別の話ですが。

参考: https://developer.android.com/ndk/guides/android_mk.html

jni/Application.mk

APP_ABI := all
APP_STL := c++_static

stlport(stlport_static)にはstd::to_stringが無かったのでclangのライブラリ(c++_static)を指定しました。

参考: https://developer.android.com/ndk/guides/application_mk.html

jni/my-awesome-app-jni.cpp

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

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_my_1awesome_1app_MyAwesomeApp_nativeExampleFun(
    JNIEnv *env,
    jobject thisj,
    jstring exampleStr,
    int exampleInt,
    jobject exampleObj)
{
    struct UTFChars
    {
        JNIEnv *env;
        jstring str;
        const char *chars;
        UTFChars(JNIEnv *env, jstring str):env(env), str(str), chars(env->GetStringUTFChars(str, nullptr)){}
        ~UTFChars(){if(chars){env->ReleaseStringUTFChars(str, chars);}}
        const char *get() const {return chars;}
    };
    const std::string result = UTFChars(env, exampleStr).get() + std::to_string(exampleInt);
    return env->NewStringUTF(result.c_str());
}

関数名はパッケージ名やクラス名に合わせる。パッケージ名に_(アンダースコア)が入っているときは_の後に1が必要なので注意。_のままだとランタイムでエラーになる。

JNIに詳しくないんですが、こういうことをするための文字列ライブラリってないんですか?

src/com/example/my_awesome_app/MyAwesomeApp.java

//...略...
import android.graphics.Bitmap;
import android.widget.Toast;

public class MyAwesomeApp extends Activity
{
    //...略...

    // ネイティブ関数の宣言
    static {
        System.loadLibrary("my-awesome-app-jni"); //LOCAL_MODULEで指定した名前
    }
    native String nativeExampleFun(String exampleStr, int exampleInt, Bitmap exampleObj);

    //...略...
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // ネイティブ関数を呼び出してみる
        String result = nativeExampleFun("test", 123, null);
        Toast.makeText(this, result, Toast.LENGTH_LONG).show(); //test123と表示される
    }
}

AndroidManifest.xml (NativeActivity使用時の例)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.my_awesome_app"
      android:versionCode="1"
      android:versionName="1.0">

  <uses-sdk android:minSdkVersion="14" /><!--←SDKのバージョン-->
  <uses-feature android:glEsVersion="0x00020000"></uses-feature><!--←OpenGL ESを使う場合、そのバージョン-->

  <application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:hasCode="false"><!--←Javaのコードを使わない場合はhasCodeをfalseにすること-->
        <activity android:name="android.app.NativeActivity"
                  android:label="@string/app_name"
                  android:configChanges="orientation|keyboardHidden"><!-- nameはNativeActivityを使うならこの値で固定。configChangesは端末回転時にActivityを破棄しない設定らしいけど、APIレベルによっては効かないらしい? -->

            <meta-data android:name="android.app.lib_name"
                       android:value="my-awesome-app-jni" /><!-- ←valueはAndroid.mkのLOCAL_MODULEで指定したモジュール名 -->

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>