この記事は「Android Advent Calendar 2011 」の裏エントリとして書いています。
17日の表のエントリは@out_of_kaya
さんの「見せてもらおうGNのフェイスアンロックの性能とやらを! - だらだらいこうぜ」
前の日の記事は同僚の@9reによる「Support PackageとMapView _level0 Kayac Interactive Designer’s Blog」
最近は時間を見つけてC
でhttp server
を書いている亀田(@Gemmbu)です。
今回のネタは簡単にいうとlibcurlをiPhoneアプリからつかう方法 (ついでにopensslも)の Android 版です。
背景
Android/iOS の開発をしていると同じ機能をそれぞれ Java
/Objective-C
で開発することになります。
たいていの場合どちらかで作成したものを移植するのですがテストの手間を考えると頭が痛くなります。
マルチプラットフォーム開発ツールではちょっと力が足りないと感じる場面があったり、開発ツールを学ぶ時間確保するところから始めなければなりません。
それならいっそ、両環境で共通に使えるC
のライブラリでやるのがいいんじゃないでしょうか?
前提
以後の作業はすべて OSX 10.6 上で行っています。
途中Android のソースをコンパイルします。そのため xcode
は3系である必要があります。
… 早くxcode
4系に対応してくれないと、Lion
にいけなくてiCloud
が使えないよ ><
openssl のビルド
openssl
は普通のライブラリの流儀にそっていなくてビルドするのはちょっと大変です。
しかしながら、 Android のソースのexternal
に含まれています。これを利用することにしましょう。
android open source project
の[以下のページ](http://source.android.com/source/downloading.html)の手順にしたがってソースを取得しましょう。
結構時間がかかるので気軽に待ちましょう。
ここからは取得したソースのルートを
<ANDROID_SRC_ROOT>
として話をすすめます。
さあビルドを…の前に openssl と crypto が静的ライブラリを出力するように<ANDROID_SRC_ROOT>/external/openssl/Android.mk
に下記を追記しましょう。
# for libcrypto.a
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=
LOCAL_C_INCLUDES:=
LOCAL_WHOLE_STATIC_LIBRARIES += libcrypto_static
LOCAL_MODULE:= libcrypto
include $(BUILD_STATIC_LIBRARY)
# for libssl.a
include $(CLEAR_VARS)
LOCAL_SRC_FILES:=
LOCAL_C_INCLUDES:=
LOCAL_WHOLE_STATIC_LIBRARIES += libcrypto_static libssl_static
LOCAL_MODULE:= libssl
include $(BUILD_STATIC_LIBRARY)
これで準備ができました。
android open source project
の以下のページの手順に従いビルドを実行してください。
正しくビルドが完了すると以下のファイルが生成されているはずです。
<ANDROID_SRC_ROOT>/out/target/product/generic/obj/STATIC_LIBRARIES/libcrypto_intermediates/libcrypto.a
<ANDROID_SRC_ROOT>/out/target/product/generic/obj/STATIC_LIBRARIES/libssl_intermediates/libssl.a
Android
プロジェクトの作成
eclipse
等からAndroid
プロジェクトを作成します。
ここからは作成したプロジェクトのルートを
<PROJECT_ROOT>
として話をすすめます。
次に以下フォルダ/ファイルを作成し、jni
を利用する準備をします。
<PROJECT_ROOT>/jni
を作成します。<PROJECT_ROOT>/jni/Application.mk
を作成します。<PROJECT_ROOT>/jni/Android.mk
を作成します。
Application.mk
の内容は以下となります
APP_MODULES := \
libcurl
# APP_OPTIM := release
APP_OPTIM := debug
Android.mk
の内容は以下となります
include $(call all-subdir-makefiles)
libcurl のビルド
次に目的のlibcurl
をビルドします
ダウンロードページから最新のソースを取得します。
<PROJECT_ROOT>/jni/curl-*.**.*
以下に展開し、以下のようなスクリプトを作成し叩きます。
NDK_ROOT
は以下から取得し展開したディレクトリを指定します。
ANDROID_SRC_ROOT
は<ANDROID_SRC_ROOT>
を指定します。
#!/bin/sh
NDK_ROOT=/path/to/your/ndk/root
NDK_VER=8
ANDROID_SRC_ROOT=/path/to/your/android/src/root
PATH=$NDK_ROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin:$PATH
./configure \
--with-ssl \
--disable-ftp \
--disable-file \
--disable-ldap \
--disable-ldaps \
--disable-rtsp \
--disable-proxy \
--disable-dict \
--disable-telnet \
--disable-tftp \
--disable-pop3 \
--disable-imap \
--disable-smtp \
--disable-gopher \
--build=`./config.guess` \
--target=arm-eabi \
--host=arm-eabi \
LD=arm-linux-androideabi-ld \
AR=arm-linux-androideabi-ar \
AS=arm-linux-androideabi-as \
NM=arm-linux-androideabi-nm \
RANLIB=arm-linux-androideabi-ranlib \
SIZE=arm-linux-androideabi-size \
STRIP=arm-linux-androideabi-strip \
CC=arm-linux-androideabi-gcc \
CXX=arm-linux-androideabi-g++ \
OBJDUMP=arm-linux-androideabi-objdump \
CPPFLAGS="-I$NDK_ROOT/platforms/android-$NDK_VER/arch-arm/usr/include/ -I$ANDROID_SRC_ROOT/external/openssl/include/" \
CFLAGS="-nostdlib" \
LDFLAGS="-Wl,-rpath-link=$NDK_ROOT/platforms/android-$NDK_VER/arch-arm/usr/lib/ -L$NDK_ROOT/platforms/android-$NDK_VER/arch-arm/usr/lib/ -L$ANDROID_SRC_ROOT/out/target/product/generic/obj/STATIC_LIBRARIES/libssl_intermediates/ -L$ANDROID_SRC_ROOT/out/target/product/generic/obj/STATIC_LIBRARIES/libcrypto_intermediates/" \
LIBS="-lc -lz"
以下の文言が出力されているのを確認してください。
SSL support: enabled (OpenSSL)
次は<PROJECT_ROOT>/jni/curl-*.**.*/Android.mk
の修正です。
curl
には既にビルドするためのAndroid.mk
が用意されているのですがSSL
に対応していません。
そこで以下の設定を追加します。ヘッダファイルの検索先にopenssl
を追加します。
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include/ \
/path/to/your/android/src/root/external/openssl/include/
また、実行ファイルは必要ないため
# Build the curl binary
以下を削除します。
ターミナルから<PROJECT_ROOT>/jni/
に移動し、以下のコマンドを実行してください。
$ ndk-build
以下の文言が出力されていれば正しく作成されています。
StaticLibrary : libcurl.a
libcurl を実際に使用する
あとは普通のJNI
プログラムと同様にJava
との連携部分を C
でごりごり書いていくだけです。
そのため<PROJECT_ROOT>/jni/external/lib
を作成し、libssl.a
およびlibcrypto.a
をコピーしたうえで、
libcurl とリンクさせるために以下のようなAndroid.mk
を書く必要があります。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libhttpclient
LOCAL_LDLIBS := -lz
LOCAL_LDLIBS += -Lexternal/lib/ -lcrypto -lssl
LOCAL_STATIC_LIBRARIES := \
libcurl
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../curl-*.**.*/include \
/path/to/your/android/src/root/external/openssl/include/
LOCAL_SRC_FILES := \
com_kayac_curltest_HttpClient.c
include $(BUILD_SHARED_LIBRARY)
まとめ
以上のような方法で、curl
およびopenssl
がAndroidアプリから利用できるようになります。
このようにビルドさえ出来てしまえば既存のライブラリをそのまま使用できるのはJNI
のメリットと言えるでしょう。 特にこのような枯れたライブラリの再発明をJava
でやる必要はありません。どんどん車輪を使って効率の良い開発をしていきましょう。