AndroidでC言語で書いたネイティブアプリを動かしてみる
以下の説明はAndroidのroot権限が取れる環境(DevPhoneやエミュレータなど)を前提に書かれています。市販のAndroid携帯など、root権限が取れない環境では、AndroidのJavaアプリ上からインストール/実行する方法を使えば実行できます。
Androidのシェルを動かす
Androidのには、adb shellというコマンドでログインしてシェルを動かすことができます。(Android SDKをC:\android_sdkにインストールしたとすると)まずC:\android_sdk\tools\emulator.exeを起動しておいてから、
> C:\android_sdk\tools\adb shell # ls ls drwxr-xr-x root root 1970-01-01 00:00 var drwxrwxrwt root root 2007-11-17 06:18 tmp drwxr-xr-x root root 2007-11-11 20:59 system ...
という感じ。かなりコマンドが少ないですが、ちゃんとLinuxです。なんかエラーが出た場合は、ちょっと待ってからもう一度実行すればいいみたいです。adb pushでファイルの転送もできます。詳しくはこのへん。注意点として、/data以外のところに置いたファイルは、エミュレータを終了すると消えてしまいます。
# cat /proc/cpuinfo cat /proc/cpuinfo Processor : ARM926EJ-S rev 5 (v5l) BogoMIPS : 182.27 Features : swp half thumb fastmult vfp edsp java ...
CPUはARMらしいので*1、ARM用の実行ファイルを作ってつっこめば動くだろう、ということでやってみました。
Scratchboxのインストール
クロスコンパイルとかよく分からないので適当にぐぐったらScratchboxというのを使う方法が出てきたので、これでやってみました。Scratchboxを動かす環境はDebian*2。
Debianのパッケージがあったのでそこから以下のファイルをダウンロードしてインストール*3。
# dpkg -i scratchbox-core_1.0.8_i386.deb # dpkg -i scratchbox-libs_1.0.8_i386.deb # dpkg -i scratchbox-toolchain-arm-linux-2006q3-27_1.0.6-1_i386.deb # dpkg -i scratchbox-devkit-cputransp_1.0.3_i386.deb # dpkg -i scratchbox-devkit-debian_1.0.9_i386.deb # dpkg -i scratchbox-devkit-doctools_1.0.7_i386.deb # dpkg -i scratchbox-devkit-perl_1.0.4_i386.deb
あとはリンク先の手順のとおりに。ただ/scratchbox/loginで
$ /scratchbox/login Host kernel has port range under 10000. This causes problems with fakeroot. You can fix this eg. by running: echo "1024 65000" > /proc/sys/net/ipv4/ip_local_port_range
とかいうエラーに。しかも
$ sudo echo "1024 65000" > /proc/sys/net/ipv4/ip_local_port_range
ではだめ(ファイルへの書き込みがroot権限にならない)という罠にかかりました。suでrootになってから上のコマンドを実行すればOK。
Hello worldを実行してみる
Scratchboxの設定が終われば、Scratchboxの中で普通にコンパイルするだけです。ここでは簡単のためにスタティックリンクします。ダイナミックリンクする方法はこちらを参照してください(arm-none-…の代わりに普通のgccやldを使えば、Scratchboxでも同様の手順で可能です)。*4
$ /scratchbox/login [sbox-arm: ~] > gcc -static -o arm-hello hello.c [sbox-arm: ~] > file arm-hello arm-hello: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for GNU/Linux 2.6.14, statically linked, not stripped
あとはこれをエミュレータに転送して実行。
> C:\android_sdk\tools\adb push arm-hello /tmp > C:\android_sdk\tools\adb shell # chmod 755 /tmp/arm-hello # /tmp/arm-hello /tmp/arm-hello Hello
おお。
Rubyを動かしてみる
調子に乗ってRubyも動かしてみました。
$ /scratchbox/login [sbox-arm: ~] > tar xvjf ruby-1.8.6-p111.tar.bz2 [sbox-arm: ~] > cd ruby-1.8.6-p111 [sbox-arm: ~/ruby-1.8.6-p111] > LDFLAGS='-static' ./configure --with-static-linked-ext [sbox-arm: ~/ruby-1.8.6-p111] > make [sbox-arm: ~/ruby-1.8.6-p111] > file ruby ruby: ELF 32-bit LSB executable, ARM, version 1 (SYSV), for GNU/Linux 2.6.14, statically linked, not stripped
以下Windowsで。
> C:\android_sdk\tools\adb push ruby /tmp > C:\android_sdk\tools\adb shell # chmod 755 /tmp/ruby # /tmp/ruby -e'puts "Hello Android."' /tmp/ruby -e'puts "Hello Android."' Hello Android.
おお。なぜかアドレス解決ができないんですが*5、ソケットも使えます。
# /tmp/ruby -r socket -e'p IPSocket.getaddress("www.google.co.jp")' /tmp/ruby -r socket -e'p IPSocket.getaddress("www.google.co.jp")' -e:1:in `getaddress': getaddrinfo: Name or service not known (SocketError) from -e:1 # /tmp/ruby -r socket -e'sock= TCPSocket.open("66.249.89.104", 80); sock.write(" GET / HTTP/1.0\n\n"); sock.flush; sock.each_line(){ |s| puts s }' /tmp/ruby -r socket -e'sock= TCPSocket.open("66.249.89.104", 80); sock.write("GE T / HTTP/1.0\n\n"); sock.flush; sock.each_line(){ |s| puts s }' HTTP/1.0 302 Found Location: http://www.google.co.jp/ Cache-Control: private Set-Cookie: PREF=ID=bb787e2dce340091:TM=1195281862:LM=1195281862:S=0WHJFGw3n2TNe ovm; expires=Mon, 16-Nov-2009 06:44:22 GMT; path=/; domain=.google.com Content-Type: text/html Server: gws Content-Length: 221 Date: Sat, 17 Nov 2007 06:44:22 GMT Connection: Close <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved</TITLE></HEAD><BODY> <H1>302 Moved</H1> The document has moved <A HREF="http://www.google.co.jp/">here</A>. </BODY></HTML>
ちゃんと動かすなら、ライブラリとかもコピーしないとだめですけどね。
課題とか
実機にはコンソールなんてないので、コンソールアプリが動いてもしょうがないわけで、なんかの方法で普通のAndroidアプリと通信する必要がありますね。パイプとかソケットとかでしょうか。まあそもそも市販される実機でネイティブアプリの実行ができるのかはかなり怪しいですけど…。*6
当然こんなことはみんな思いつくので既にやられてるわけですが、このスレッドではJNIが使えないかという話になってるみたいです。
2009/1/11追記: Android MarketにちゃんとTerminal Emulatorがあるので、それを使って実機上で直接動かせました。また、Javaアプリ上からUNIXコマンドを呼び出すことも可能です。また、ダイナミックリンクについての情報を本文中に追記しました。
2010/7/10追記: root権限がない環境についての情報を冒頭に追加しました。
*1:Android自体はARM以外にも対応してると思いますが、このエミュレータはARMをエミュレートしてる、ということです。
*2:ScratchboxはLinuxでしか動かないっぽいので。あとでもっと普通そうでWindowsでも動くらしいクロスコンパイラを発見。
*3:devkitなんとかってのはそれ以外よりあとでインストールする必要があるようです。
*4:ダイナミックリンクしたほうが、バイナリサイズはずっと小さくなります。
*5:/etc/resolv.confがないせい?でも実機だと/etc以下って書き込めないんですよね。chrootするしかない?
*6:あとはそもそも日本で使える実機はいつ出るのか、とか。