C言語を読み込もう | Android系女史

Android系女史

Android開発など雑多なプログラムの愚痴でもしています。

相方からスピードアップして!ってせっつかれている。
しかしながら平日は寝に帰るだけ、
休日もゲームしてたりアニメ見たり本読んでたりすると時間がないのである。

Hello Worldができたところで次はJavaからC++の関数を読み込む。
JavaからC/C++の言語で書かれた関数を読み込むためには
JNI(Java Native Interface)というインタフェースを使用するらしい。
このJNIをAndroidプログラムで利用するために必要なのがAndroid NDK

Android NDKをダウンロードするページを見ると
NDKは計算量が必要でメモリをあんまり使わない場所に使うといいよ
それ以外のときはコードが複雑になるからC++が好きって理由だけで使うべきじゃないよ
(意訳)
などと書かれている。
確かにAndroid NDKについて紹介してある他の記事等でも
アプリの高速化と絡めて書いているし。
……今回JavaからC++のプログラムを読み込む理由ってどっちかっていうとC++が好きだからなんだけどいいのかな。

一応、Windows, iOSのプログラムのコアの部分はC++で書かれているから
AndroidもなるべくC++でかける部分はC++のほうが同じソースを利用できて
保守性があがる、開発効率があがるっていう利点があるはず。
問題はJavaからC++のコードを呼ぶときにどれだけ面倒なのかかな。

すでに私のPCにはNDKがインストールされているのでサンプルプログラムを見てみる。

Java:
package com.example.hellojni;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;
public class AppFrame extends Activity
{
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);

    TextView tv = new TextView(this);
    // Cの関数呼び出し
    tv.setText( stringFromJNI() );
    setContentView(tv);
  }
  // Cから呼び出す関数名を記述
  public native String stringFromJNI();

  // Cのライブラリ呼び出し
  static {
    System.loadLibrary("hello-jni");
  }
}
hello-jni.c:
#include <string.h>
#include <jni.h>
jstring
Java_com_example_hellojni_AppFrame_stringFromJNI( JNIEnv* env,
jobject thiz )
{
  return (*env)->NewStringUTF(env, "Hello from JNI !");
}
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)
cygwinでhello-jni.cとAndroid.mkがあるディレクトリに移動しndk-buildをするとlibhello-jni.soが出来るので
呼び出すJavaプログラム側では
System.loadLibrary("hello-jni");
と記述してこの出来上がったライブラリを読み込み。
そしてCのプログラム内の呼び出したい関数名を
public native String stringFromJNI();
のように記述し、後は使用するだけ。
呼び出されるCプログラム側では
Javaから呼び出したい関数名がstringFromJniとすると
呼び出されるCのプログラム内では
Java_パッケージ名_クラス名_stringFromJniという名前で記述……

……呼び出される側で名前を変えないといけないの……?
え?何その面倒な仕様。
そういうのって呼び出す側で何とかするもんじゃないの?
ていうか呼び出される側で
呼び出す側のパッケージ名やクラス名を入れて関数名作成しないといけないとなると
このJavaから呼び出される関数は
Javaのパッケージ名やクラス名が変わるごとに作らないといけないってことになるんじゃ?

C++からFortranを呼び出したりFortranからC++を呼び出したりするのと
同じ感じだと予想していたからこの面倒はびっくりだよ。
いや、JavaとC++のつなぎ目の部分だけは面倒そうだけど
それ以外の部分はそのままコードを使える……と信じたい。

一からプロジェクトを作成して
JavaからCのコードを読み込めるテストは成功。
そろそろ実際のコードをAndroid用に変換を始めたい。