AndroidでC言語/C++のソースコードを動かそう(ソースコード編)

AndroidでC言語/C++のソースコードを動かそう(ソースコード編)

いよいよ、ネイティブコードでAndroidを動かしていきます。

はまったポイントは、「C++で書く場合には「exturn “C”」が要る」こと。
手元にあるAndroidNDKの書籍がC言語用だったのでなかなか気づけず、だいぶ時間を無駄にしてしまいました…。
さて、前置きが長くなりましたが、本題に入ります。

JavaのソースコードにC/C++呼び出し処理を追加しよう

今回は「incrementValue(int value)」という「渡した値を+1して返す」というとってもシンプルな関数をC/C++に作成し、それをJavaの「MainActivity.java」から呼び出すという処理を行います。

★MainActivity.java
[java]
package com.otowabi.samplesampler;

public class MainActivity extends Activity {
private Button m_button;
private TextView m_text;
// +1対象の変数
private int m_value = 0;
// C/C++の関数を宣言
protected native int incrementValue(int value);
// C/C++のライブラリを読み込み
static {
System.loadLibrary("calc");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ボタン・テキスト欄の取得
m_button = (Button)findViewById(R.id.button1);
m_text = (TextView)findViewById(R.id.textView1);
// ボタンを押したときのイベント
m_button.setOnClickListener(new OnClickListener(){
public void onClick(View v){
// 現在のm_valueを+1
m_value = incrementValue(m_value);
// テキスト欄にm_valueの値を表示
m_text.setText("How are you. " + m_value);
}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
[/java]

ポイントとなる箇所は下記のところです。
・C/C++側の関数をJava側でnative関数として宣言するところ
・「calc」という名前のライブラリを読み込むところ

[java]
// C/C++の関数を宣言
protected native int incrementValue(int value);
// C/C++のライブラリを読み込み
static {
System.loadLibrary("calc");
}
[/java]

これで、「calc」というライブラリ内にある「incrementValue」という関数が呼び出せるようになります。

C言語のソースコードを書こう

C言語でソースコードを書く場合、下記のような感じになります。

★calc.c
[c]
jint Java_com_otowabi_samplesampler_MainActivity_incrementValue(JNIEnv* env, jobject obj, jint value){
return value + 1;
}
[/c]

関数の名称は「Java_パッケージ名(「.」は「_」に置き換え)_クラス名_関数名」という規則に従って付けます。
この規則に沿うことで、Java側からこの関数を見つけてくれるようになります。
なお、パッケージ名(今回は「package com.otowabi.samplesampler」)の途中に「_」が含まれていると、うまくJava側から見つけてもらえなくなることがあるようです。

引数の1つめ「JNIEnv* env」と2つめ「jobject obj」は固定、3つめ以降がJavaと受け渡して使用する変数となります。

ソースコードが出来上がったら、設定ファイル「Android.mk」の「LOCAL_MODULE」と「LOCAL_SRC_FILES」に、作成したネイティブコードのファイル名、ライブラリ名を指定します。
(「Add Native Support」実施時に作成したファイル名から変更を行っていなければ、既に正しい値が設定されているはずです。)

★Android.mk
[bash]
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := calc
LOCAL_SRC_FILES := calc.c

include $(BUILD_SHARED_LIBRARY)
[/bash]

C++でもソースコードを書こう

cppの場合は、関数の頭に「extern “C”」を付けます。
これで、C言語のときと同じように呼び出しが行えるようになります。
これを付け忘れると、Javaから「そんな関数は見つからん」と言われました…。

後のところは今回特にC言語とC++で変わる箇所がないので、まったくもって同一となります。

★calc.cpp
[cpp]
extern "C" jint Java_com_otowabi_samplesampler_MainActivity_incrementValue(JNIEnv* env, jobject obj, jint value){
return value + 1;
}
[/cpp]

こちらも同様。

★Android.mk
[bash]
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := calc
LOCAL_SRC_FILES := calc.cpp

include $(BUILD_SHARED_LIBRARY)
[/bash]

動かしてみた

押したら増える
押したら増える

まとめ

ようやくソースコードを書くところまでやってきました!
そして、ソースコードを書くと記事が文字だらけになるんだなあということを思い知りました。

次回からは音楽アプリっぽい内容にしていけたらいいなあと思います。