Android用にMonoをbuildする

AndroidスタンドアロンのMonoを使いたかったのでMonoをbuildしました。
その手順を忘れないように書いておきます。

対象となるMonoのバージョンは2.10.5です。
2.10.5の理由は手元のUbuntuのapt-getでinstallされたのが2.10.5だったからです。

以下の手順でbuildを行います。

  1. Androidのソースツリーを取得
  2. externalの下に「androidmono」という名前でディレクトリを作成
  3. external/androidmonoの中でgit cloneなりtarを展開するなりしてMonoのソースを展開
  4. 展開したMonoのディレクトリを「mono」という名前に変更
  5. Android.mkとApplication.mkを作成
  6. パッチを適用する
  7. ビルドに必要なヘッダファイル群を展開する

という感じです。

Android.mkの中身はこんな感じですね。

LOCAL_PATH := $(call my-dir)
MONO_PATH := $(LOCAL_PATH)/mono

MONO_ASSEMBLY_DIR := \"/data/local/mono/lib/\"
MONO_CONFIG_DIR   := \"/data/local/mono/bin/\"
MONO_BINARY_DIR   := \"/data/local/mono/bin/\"

MONO_CFLAGS := -DMONO_ASSEMBLIES=$(MONO_ASSEMBLY_DIR) \
               -DMONO_CFG_DIR=$(MONO_CONFIG_DIR) \
               -DMONO_BINDIR=$(MONO_BINARY_DIR) \
               -DHAVE_CONFIG_H -DARM_FPU_VFP -D_REENTRANT \
               -DPAGE_SIZE=4096 -DS_IWRITE=S_IWUSR \
               -DPLATFORM_ANDROID -D__linux__ -DREDIRECT_MALLOC=GC_malloc \
               -DHAVE_BOEHM_GC -DHAVE_GC_H -DUSE_INCLUDED_LIBGC -DHAVE_GC_GCJ_MALLOC -DHAVE_GC_ENABLE \
               -D_GNU_SOURCE -DUSE_MMAP -DUSE_MUNMAP -D_FILE_OFFSET_BITS=64 -DNO_UNALIGNED_ACCESS \
               -D__environ=environ

MONO_LIBGC_CFLAGS := -DPACKAGE_NAME=\"libgc-mono\" -DPACKAGE_VERSION=\"6.6\" -DPACKAGE_STRING=\"libgc-mono\ 6.6\" \
                     -DGC_LINUX_THREADS=1 -D_REENTRANT=1 \
                     -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 \
                     -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 \
                     -DLT_OBJDIR=\".libs/\" -DSILENT=1 -DNO_SIGNALS=1 -DNO_EXECUTE_PERMISSION=1 \
                     -DGC_GCJ_SUPPORT=1 -DJAVA_FINALIZATION=1 -DATOMIC_UNCOLLECTABLE=1 -D_IN_LIBGC=1 \

MONO_INCLUDES := $(MONO_PATH)/ \
                 $(MONO_PATH)/mono/ \
                 $(MONO_PATH)/eglib/src/ \
                 $(MONO_PATH)/mono/mini/ \
                 $(MONO_PATH)/libgc/include \
                 $(LOCAL_PATH)/configs \
                 $(LOCAL_PATH)/configs/eglib \
                 $(LOCAL_PATH)/configs/mono/mini \
                 $(LOCAL_PATH)/configs/mono/arch/arm \
                 bionic/linker

MONO_LDLIBS := -ldl

# build 'libeglib'
include $(CLEAR_VARS)

LOCAL_MODULE := eglib
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/eglib/src
LOCAL_SRC_FILES := garray.c gerror.c ghashtable.c gmem.c goutput.c \
    gstr.c gslist.c gstring.c gptrarray.c glist.c gqueue.c gpath.c \
    gshell.c gspawn.c gfile.c gfile-posix.c gpattern.c gmarkup.c \
    gutf8.c gunicode.c gdate-unix.c gdir-unix.c gfile-unix.c \
    gmisc-unix.c gmodule-unix.c gtimer-unix.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoutils'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoutils
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/utils
LOCAL_SRC_FILES := mono-semaphore.c mono-md5.c \
    mono-sha1.c mono-logger.c mono-codeman.c dlmalloc.h dlmalloc.c \
    mono-counters.c mono-compiler.h mono-dl.c mono-dl.h \
    mono-internal-hash.c mono-internal-hash.h \
    mono-io-portability.c mono-io-portability.h monobitset.c \
    mono-math.c mono-mmap.c mono-mmap.h mono-proclib.c \
    mono-proclib.h mono-time.c mono-time.h strtod.h strtod.c \
    strenc.h strenc.c mono-uri.c mono-poll.c mono-path.c \
    mono-stdlib.c mono-property-hash.h mono-property-hash.c \
    mono-value-hash.h mono-value-hash.c freebsd-elf_common.h \
    freebsd-elf32.h freebsd-elf64.h freebsd-dwarf.h dtrace.h \
    mono-filemap.c mono-networkinterfaces.c mono-error.c \
    mono-publib.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoruntime'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoruntime
LOCAL_PATH := $(MONO_PATH)/mono/metadata
LOCAL_SRC_FILES := runtime.c \
    mono-basic-block.c mono-wsq.c mono-hash.c reflection.c object.c object-internals.h \
    icall.c icall-def.h char-conversions.h decimal.c decimal.h \
    boehm-gc.c sgen-gc.c sgen-gc.h gc.c gc-internal.h method-builder.h method-builder.c \
    mono-mlist.c mono-mlist.h tabledefs.h threads-types.h threadpool-internals.h \
    file-io.c file-io.h socket-io.c socket-io.h exception.c exception.h \
    debug-mono-symfile.h debug-mono-symfile.c mono-debug.h mono-debug.c \
    mono-debug-debugger.h mono-debug-debugger.c profiler-private.h attach.h attach.c \
    rand.h rand.c security.c security.h security-core-clr.c security-core-clr.h \
    string-icalls.c string-icalls.h sysmath.h sysmath.c process.c process.h \
    environment.c environment.h locales.c locales.h normalization-tables.h \
    filewatcher.c filewatcher.h culture-info.h culture-info-tables.h \
    security-manager.c security-manager.h console-io.h console-unix.c \
    coree.c coree.h domain.c domain-internals.h opcodes.c cil-coff.h \
    metadata.c metadata-internals.h number-formatter.h verify.c verify-internals.h \
    mono-endian.c mono-endian.h mono-config.c loader.c \
    class.c class-internals.h wrapper-types.h mempool.c \
    mono-perfcounters.c mono-perfcounters.h mono-perfcounters-def.h \
    debug-helpers.c mempool-internals.h metadata-verify.c \
    mono-cq.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmonoruntimearm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := monoruntimearm
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/metadata
LOCAL_SRC_FILES := \
    monitor.c marshal.c threadpool.c threads.c appdomain.c \
    profiler.c cominterop.c assembly.c image.c null-gc.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libwapi'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := wapi
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/io-layer
LOCAL_SRC_FILES := access.h atomic.c atomic.h \
    collection.h context.c context.h critical-sections.c \
    critical-sections.h critical-section-private.h error.c error.h \
    events.c events.h event-private.h handles.h \
    handles-private.h io.h io-portability.c io-portability.h \
    io-private.h io-layer.h macros.h messages.c messages.h misc.c \
    misc-private.h mutexes.c mutexes.h mutex-private.h \
    mono-mutex.c mono-mutex.h mono-spinlock.h processes.c \
    processes.h process-private.h security.c security.h \
    semaphores.c semaphores.h semaphore-private.h shared.c \
    shared.h sockets.c sockets.h socket-private.h \
    socket-wrappers.h status.h system.c system.h \
    threads.h thread-private.h timefuncs.c timefuncs.h \
    timefuncs-private.h types.h uglify.h versioninfo.c \
    versioninfo.h wait.c wait.h wapi_glob.h wapi_glob.c locking.c posix.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libwapiarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := wapiarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/io-layer
LOCAL_SRC_FILES := collection.c io.c wthreads.c handles.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libgc'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := gc
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/libgc
LOCAL_SRC_FILES := allchblk.c blacklst.c \
    checksums.c dyn_load.c \
    headers.c mark.c \
    new_hblk.c pcr_interface.c \
    ptr_chck.c real_malloc.c solaris_pthreads.c \
    solaris_threads.c specific.c stubborn.c backgraph.c \
    win32_threads.c pthread_stop_world.c \
    darwin_stop_world.c mach_dep.c
LOCAL_CFLAGS := $(MONO_CFLAGS) $(MONO_LIBGC_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libgcarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := gcarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/libgc
LOCAL_SRC_FILES := alloc.c dbg_mlc.c finalize.c gc_dlopen.c gcj_mlc.c malloc.c \
    mallocx.c  mark_rts.c obj_map.c os_dep.c misc.c reclaim.c pthread_support.c typd_mlc.c
LOCAL_CFLAGS := $(MONO_CFLAGS) $(MONO_LIBGC_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmini'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := mini
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := mini.c method-to-ir.c \
    xdebug.c decompose.c \
    jit-icalls.c trace.c \
    mini-arch.h dominators.c cfold.c regalloc.c \
    helpers.c liveness.c ssa.c abcremoval.c ssapre.c \
    local-propagation.c driver.c debug-mini.c \
    linear-scan.c aot-runtime.c \
    graph.c mini-exceptions.c mini-codegen.c mini-trampolines.c \
    declsec.c wapihandles.c branch-opts.c \
    mini-generic-sharing.c regalloc2.c simd-intrinsics.c \
    unwind.h unwind.c mini-posix.c \
    mini-arm.c exceptions-arm.c tramp-arm.c image-writer.c \
    dwarfwriter.c mini-gc.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libminiarm'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := miniarm
LOCAL_MODULE_TAG := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := aot-compiler.c debugger-agent.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)

include $(BUILD_STATIC_LIBRARY)

# build 'libmono'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := libmono
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := mini.c method-to-ir.c \
    xdebug.c decompose.c \
    jit-icalls.c trace.c \
    mini-arch.h dominators.c cfold.c regalloc.c \
    helpers.c liveness.c ssa.c abcremoval.c ssapre.c \
    local-propagation.c driver.c debug-mini.c \
    linear-scan.c aot-runtime.c \
    graph.c mini-exceptions.c mini-codegen.c mini-trampolines.c \
    declsec.c wapihandles.c branch-opts.c \
    mini-generic-sharing.c regalloc2.c simd-intrinsics.c \
    unwind.h unwind.c mini-posix.c \
    mini-arm.c exceptions-arm.c tramp-arm.c image-writer.c \
    dwarfwriter.c mini-gc.c
LOCAL_PRELINK_MODULE := false
LOCAL_CFLAGS := $(MONO_CFLAGS) -fPIC
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_WHOLE_STATIC_LIBRARIES := monoutils gcarm monoruntime
LOCAL_STATIC_LIBRARIES := wapiarm monoruntimearm eglib gc wapi mini miniarm
LOCAL_LDFLAGS := -Wall -L$(SYSROOT)/usr/lib -llog -ldl -lm -lc
#LOCAL_STRIP_MODULE := false

include $(BUILD_SHARED_LIBRARY)

# build 'mono'
include $(CLEAR_VARS)

LOCAL_ARM_MODE := arm
LOCAL_MODULE := mono
LOCAL_MODULE_TAGS := eng
LOCAL_PATH := $(MONO_PATH)/mono/mini
LOCAL_SRC_FILES := main.c
LOCAL_CFLAGS := $(MONO_CFLAGS)
LOCAL_C_INCLUDES := $(MONO_INCLUDES)
LOCAL_SHARED_LIBRARIES := libmono
LOCAL_LDFLAGS := -llog -ldl -lm -lc

include $(BUILD_EXECUTABLE)

長いですね・・・。あとでgithubかなんかに突っ込んで終わりにしたいですね。

Application.mkは適当に

APP_ABI := armeabi armeabi-v7a

とか書いておけば良いです。

あてるパッチはこんな感じです。

diff --git a/libgc/dyn_load.c b/libgc/dyn_load.c
index e4be3c7..d8ce7ec 100644
--- a/libgc/dyn_load.c
+++ b/libgc/dyn_load.c
@@ -99,7 +99,9 @@
 # if !defined(OPENBSD)
 #   include <elf.h>
 # endif
+# if defined(HAVE_LINK_H)
 #   include <link.h>
+# endif
 #endif

 /* Newer versions of GNU/Linux define this macro.  We
diff --git a/mono/mini/aot-runtime.c b/mono/mini/aot-runtime.c
index a3c5746..867e437 100644
--- a/mono/mini/aot-runtime.c
+++ b/mono/mini/aot-runtime.c
@@ -38,8 +38,12 @@
 #endif

 #ifdef HAVE_DL_ITERATE_PHDR
+#ifdef PLATFORM_ANDROID
+#include <linker.h>
+#else
 #include <link.h>
 #endif
+#endif

 #include <mono/metadata/tabledefs.h>
 #include <mono/metadata/class.h>
@@ -59,6 +63,21 @@
 #include "mono/utils/mono-compiler.h"
 #include <mono/utils/mono-counters.h>

+#if defined(PLATFORM_ANDROID)
+#ifdef PSR_c
+#undef PSR_c
+#endif
+#ifdef PSR_x
+#undef PSR_x
+#endif
+#ifdef PSR_s
+#undef PSR_s
+#endif
+#ifdef PSR_f
+#undef PSR_f
+#endif
+#endif
+
 #include "mini.h"
 #include "version.h"

diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c
index c15b098..92aa901 100644
--- a/mono/mini/mini-posix.c
+++ b/mono/mini/mini-posix.c
@@ -631,7 +631,7 @@ mono_runtime_syscall_fork ()
    return (pid_t) syscall (SYS_fork);
 #else
    g_assert_not_reached ();
-   return;
+   return -1;
 #endif
 }

これもあとでどっかにファイルおくだけにしたいですね・・・。

さて残りはヘッダなんですが、ソースをそのまま貼り付けるとえらいことになるので、
あとでgithubにでも突っ込んでおきます。
突っ込んだらこの記事も更新するつもりです。

libusbをAndroid用にポーティングする

お仕事で必要があってlibusbをAndroidに持っていくということをやりました。
この記事はlibusbをAndroid用にbuildする方法の備忘録です。

まず、Androidをlibusbにportingするのは先人が既にやっているので、そこを参考にします。
参考URL: http://android.serverbox.ch/?p=151

  1. androidソースコードをもってきます。 -> ここ「Downloading the Source Tree | Android Open Source」を参考に。
  2. androidソースコードをbuildします。
  3. externalの下にlibusbというディレクトリを切ります。
  4. libusbのソースを展開します。現在の最新版は「libusb-1.0.9」です。
  5. "external/libusb"の直下に「Android.mk」を作成します。
  6. "external/libusb/libusb"にも「Android.mk」を作成します。
  7. "external/libusb/libusb/io.c"を修正します。
  8. 「mm」でlibusbをbuildします。

androidのbuildは、もってきたsource codeのトップで

$ source build/envsetup.sh
$ lunch
$ make

とかやってください。

libusbの直下に作成するAndroid.mkはこんな感じです。

LOCAL_PATH := $(call my-dir)
subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, libusb))
include $(subdirs)

元にしているページのAndroid.mkは"subdirs"の頭の"s"が取れてしまっているので注意しましょう。

"external/libusb/libusb"の下に作成するAndroid.mkはこんな感じです。

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS:=-DLIBUSB_DESCRIBE

LOCAL_SRC_FILES:= \
  core.c \
  descriptor.c \
  io.c \
  sync.c \
  os/linux_usbfs.c \
  os/threads_posix.c

LOCAL_C_INCLUDES += \
external/libusb/ \
external/libusb/libusb/ \
external/libusb/libusb/os

LOCAL_MODULE:= libusb
LOCAL_MODULE_TAGS:=eng
LOCAL_PRELINK_MODULE:=false

include $(BUILD_SHARED_LIBRARY)

"libusb-1.0.9"以外のバージョンでは何らかの修正が必要になるかもしれません。
その場合は適宜修正して下さい。
あと、この方法でビルドはできると思いますが、ビルド後のバイナリの動作を保証するものではありません。

NSEG勉強会 feat. 高専カンファレンスの感想

2012年6月23日に長野で行われた「NSEG勉強会 feat. 高専カンファレンス」についての記事です。

勉強会の開催情報は↓のページに載っています。
NSEG勉強会 feat. 高専カンファレンス - 高専カンファレンス Wiki

前回の日記に書きましたが、今回の勉強会には発表者として参加してきました。
これまでにも長野へ行くチャンスはあったのですが、体調不良で過去のチャンスを逃したため、今回が初めての長野となりました。
長野って近いようで遠いなーと思いました。

長野に着くなりお約束のお蕎麦を食べたのですが、写真撮りそこねてしまいました。
ちゃんと写真撮っとけばよかったなー。
信州そば美味しかったです。

今回の発表は実はiPadを使ってやろうかなーと思い、iPadからVGA出力をするアダプタも購入したのですが、
なぜかきちんと出力されず、結局諦めてノートPCを使ってプレゼンしました。
ちょっと悲しかったです(´;ω;`)

DSC_7174

信州大学の教官の方の発表は、畑違いではあったものの話が面白くすごくわかり易く感じました。
実際の教育機関で教鞭をとられている人はさすがだなーと思いました。

今回の勉強会はばりばりのソフトウェア畑の勉強会ということで、
開発よりのお話が多かったかなーと思います。
その割にガチガチの技術ネタばかりではなかったので、
わりととっつきやすい感じの勉強会だったかなという印象を持ちました。

あと、長野周辺の方が半数を占めるのもあってか、
NSEGというのはかなりアットホームな感じの勉強会なのだなーと感じました。
長野にいるソフトウェア技術者の方は顔を出してみれば、きっと優しく迎え入れてくれると思いますよ。

今回発表の場を提供して頂いたNSEG感じのおびなたさん、
勉強会運営スタッフのみなさん、
私の話を聞いて頂いた参加者の皆さん、
どうもありがとうございました。

「NSEG勉強会 feat. 高専カンファレンス」に参加してます。

まだ終わってないので感想は後ほど。

長野で行われている「NSEG勉強会 feat. 高専カンファレンス」に参加しています。

私も15分の枠を頂いて発表をしてきました。

タイトルは「Hello, Ruby!」で、mrubyの紹介と私の作っているjamrubyの紹介の話をしてきました。
発表資料はこちらです。


DropBoxのリンク : https://www.dropbox.com/s/ajb5pp9qtt13aeb/HelloRuby.pdf


発表の様子は録画されているそうなので、Ustreamで見られなかった方も後ほど公開される予定の録画をご覧頂くことができるそうです。

AndroidのPreferenceを簡単に使いたい

AndroidでPreference関係の処理書くのすごーく面倒なんですよねー。
もう面倒すぎて鼻血でそうな感じですよね。
なので、うまいこと使える部品使って使い回したいなーっと。

そういうことで最近はこんな感じのコード書いてます。

public class Preferences {
	private static SharedPreferences prefs;
	private static Resources res;
	
	public static void create(Context context) {
		if (null == prefs || null == res) {
			prefs = PreferenceManager.getDefaultSharedPreferences(context);
			res = context.getResources();
		}
	}
	
	public static String getHoge() {
		return get(R.string.pref_key_hoge, getString(R.string.pref_defval_hoge));
	}
	
	private static String getString(int id) {
		return res.getString(id);
	}
	
	@SuppressWarnings("unchecked")
	private static <T> T get(int id, T defVal) {
		final Map<String, ?> map = prefs.getAll();
		final String key = getString(id);
		if (!map.containsKey(key)) {
			return defVal;
		}
		return (T)map.get(key);
	}
}

Preferences#createをActivityのonCreateとかonStartとかonResumeとかで適宜呼び出します。
AndroidのSingletonはアプリケーションのlife cycleにあわせて自動的にunloadされる可能性があるので気をつけてくださいね☆)

内部で設定項目を取得するのは" T get(int id, T defVal)"を呼び出します。
デフォルト値やキーは基本的にリソースに追い出して使う前提なので、
"String getString(int id)"でリソースから文字列を取得できるようにしています。

こんな感じのラッパーを書いておけば設定項目へのアクセスが楽になるかなーと思います。

jamrubyのリポジトリを分離しました。

jamruby関連のリポジトリを、私個人のリポジトリから移動しました。
jamruby専用のアカウントをgithubに作ったので、
以後はそちらからソースを引っ張って頂けると良いかなと思います。

https://github.com/jamruby

しばらくは私個人のリポジトリにもpushしますが、
どこかのタイミングから完全に移行したいなと思っています。

jamrubyの状況ですが、ちまちまとmrubyのAPIのラッパーを書いています。
まだたくさん追加すべき部分があるので、まずはここをある程度やってしまおうと思います。

Androidのセンサーで気をつけるべきこと

「気をつけるべきこと」とか書きましたが、
ありていに言えば「自分がはまったのでくやしい!ぐぬぬ!」ってことですね。

ということでAndroidのセンサーから値を取得する上で、気をつけなきゃいけないこと書いておきます。


  1. SensorManager取得時に指定したセンサーのタイプを信用するな。
  2. SensorのResolutionはよくわからない。


具体的にどういうことかコードを見ていきましょう。

Androidでセンサーの値を取るときはこんな感じのコードを書きます。

@Override
void onCreate(Bundle savedInstanceState) {
    ...
    final SensorManager mgr = (SensorManager)getSystemService(SENSOR_SERVICE);
    final Sensor acc = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    mgr.registerListener(listener, acc, SensorManager.SENSOR_DELAY_NORMAL);
}

private final SensorEventListener listener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        ...
    }
		
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
};

このとき、SensorManagerに対して指定したセンサータイプのセンサのイベントが取れるようになります。
きっと取れると思います。取れるはずなんです。本当なら。
でも実はうまくいかないことがよくあります。
で、よくよく調べてみると、Android端末のいくつかでは指定したセンサータイプと、
getDefaultSensorで実際に取得できるセンサーの物理的な種類が一致していないことがわかりました。
そりゃうまく動かないわけですよ。
だって加速度センサの値が欲しいのに実際には磁気センサを取得してたりするんですから。


これをどうやって回避するかというと、実際に取得したいセンサに関連する他の論理センサも動作させるということを行います。
具体的なコードで説明をすると、

final Sensor acc = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mgr.registerListener(listener, acc, SensorManager.SENSOR_DELAY_NORMAL);

final Sensor ori = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mgr.registerListener(listener, ori, SensorManager.SENSOR_DELAY_NORMAL);

final Sensor mag = mgr.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mgr.registerListener(listener, mag, SensorManager.SENSOR_DELAY_NORMAL);

こんなふうにして関連するセンサを全部使用します。
後はイベントを受ける側で、どのイベントが来たかを振り分けてあげればうまくいきます。
(どうして加速度センサと磁気センサと傾きセンサが関連するかの説明は省略します)

private final SensorEventListener listener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        switch(event.sensor.getType()) {
        case Sensor.TYPE_ACCELEROMETER:
            ...
            break;
        }
    }
		
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
};

次にSensorのResolutionですが、名前からして測定値を物理量に変換するのに使用するものだと思うじゃないですか普通は(たぶん普通の感覚だよね?)。
でもこれ、実は実装によってはただの固定値であまり意味のある値が入っていません。
Resolutionを使わなくても、普通に値を使えば物理量に単位変換された値が入っています。
Resolutionの単位も実装依存のため、何のために存在するかいまいちわかりません。
なので間違っても↓みたいなコードは書いちゃだめですよ!ダメ、ゼッタイ!

private final SensorEventListener listener = new SensorEventListener() {
    @Override
    public void onSensorChanged(SensorEvent event) {
        final float res = event.sensor.getResolution();
        switch(event.sensor.getType()) {
        case Sensor.TYPE_ACCELEROMETER:
            final float accX = event.value[0] * res;
            // do something
            ...
            break;
        }
    }
		
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
};

↑こういうことしちゃダメですよ!