JNI(.so)を使ったJARライブラリのandroidでのロードの仕方

なにこのタイトル!
初めてのandroidネタです。

お急ぎの方は最後の方から見て下さい。

Javaのライブラリはjar形式で配布可能なんですね。
公式ではやるなって言ってるような気がしますが、クライアント様様様の要求でjarで出さなきゃいけない時もありますね。

普通ならサクッとエクスポートしてオッケーなのですが、ライブラリの中でJNIなんか使ってると大変です。
/lib/armeabi配下に.soを入れてjarをエクスポートするところまでは問題なし。
しかしそのjarを組み込んでアプリをビルドしようとすると怒られます。

The library 'hoge.jar' contains native libraries that will not run on the device.
とか、
lib/ is reserved for NDK libraries.
とか言われます。

色々見てみたんですがlibに置いて動かすのはうまくいきませんでした。。
ぐーぐるの中の人が

I'm afraid there's no work around, you'll need to manually add the
library to the project that includes the jar file.

Google グループ

って言ってるのでダメなのかなーって感じ。
結構前の記事なので今は違うのかもですが、どうにもうまくいかない。。

そうは言ってもクライアントが(以下略

というわけで、stackoverflow。
いつもお世話になってる猛者達も結構苦労したみたいです。
lib下に置いて動かす解決方法は見つからなかったですが、代わりにこんなのがありました。
Mixing Android External JAR and JNI gone wild - Stack Overflow

回答の中に、zipにしてassetsに突っ込んで、ロードする時に解凍すればおkって言ってる人がいたので試したところ上手くいきました。
zipにしてるのはassetsにサイズ制限があるからだと思いますが、zipじゃなくてsoのままでも上手くいきました。

まぁこの方法も、公式的にはアレなんですけど、クライ(以下略

jniのmakefileの最後に

cp -p ../libs/armeabi/libhoge.so ../assets/.

とか書いておくと楽チンですね。

アプリからの呼び出しはこんな感じ。
例外処理は省いてるので自前でごにょごにょして下さい。

String path = "/data/data/" + HogeActivity.getContext().getPackageName() + "/libhoge.so";
InputStream is = HogeActivity.getContext().getResources().getAssets().open("libhoge.so");
File fileout = new File(path);
OutputStream os = new FileOutputStream(fileout);
final int DEFAULT_BUFFER_SIZE = 1024 * 4;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int n = 0;
while (-1 != (n = is.read(buffer))) {
	os.write(buffer, 0, n);
}
is.close();
os.close();
System.load(fileout.toString());

ちなみにnexus s(ICS)で動作確認しました。
他の機種でうまくいくかは分かりません。
うまくいったよーとか、ダメだーとか、こうすればもっといい感じってやり方をご存知の方はぜひぜひ教えて下さい。