NDKを使って、nativeに処理をさせる
|Android NDKを使う(アプリの高速化) の項目において、NDKのセットアップとサンプルの実行方法を紹介しました。
今回は、実際にNDKを使用する手法を紹介します。
Sampleとして作成するのは、用意した画像データからRedの色データを消去するものです。
1ピクセル毎に処理が必要になるため、Nativeで処理を行うことで高速化を狙っています。
#画像データは初期データと処理後データを見比べた物
詳細な実装方法は続きをご覧ください。
NDKを使用する時に必要なこと
- JNIフォルダの追加
- Application.mk Android.mk (ヘッダファイル)の追加
- NativeSourceの追加
- JavaSourceでのStaticLibraryの呼び出し
以上の4点が必要です。
追加するファイルの場所等は以下、今回のプロジェクト構成を参考にしてください。
Android.mkとApplication.mk
Android.mkとApplication.mkを作成します。
これらは一から作成するよりは、既存のサンプルプロジェクトからコピーしてくるのがよいと思います。
#今回は ndk/samples/native_activity/jni/ からコピーし作成しました。
コピーしてきたものを修正します。
Android.mk
1 2 3 4 5 6 7 8 | LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native_sample LOCAL_SRC_FILES := native_sample.c include $(BUILD_SHARED_LIBRARY) |
LOCAL_SRC_FILESにはソースコードを指定、LOCAL_MODULEにはモジュール名(任意)を指定します。
Application.mk
1 | APP_PLATFORM := android- 8 |
同じ様に、赤字が修正箇所です。
Androidのプラットフォームバージョンを指定します。
#今回はAndoid-8(ver 2.2) 向けに作成
Java側のソースコードの記述
先にJavaソースコードにおいて、native-Methodを記述することで
ヘッダファイルをコマンドから作成することができます。
まず、以下の様にLibraryの呼び出し部分を記述します。
ここで引数に渡す値は、LOCAL_MODULEに指定した値と同じにしておきます。
1 2 3 4 5 | public class Main extends Activity { static { System.loadLibrary( "native_sample" ); } ...... |
次に、native-Methodを記載します。
今回は、bitmapのピクセルデータをNativeへ受け渡すため、int-arrayを引数に持っています。
1 | public native void nativeBinarization( int [] pixels, int x, int y); |
最後に、Methodの呼び出し部分を記載します。
本サンプルでの流れは、 リソースから取得したBitmapオブジェクトを複製し、(AndroidはデフォルトではRGB_565を使用しています)
ピクセル毎のintデータを作成、Native-Methodへ受け渡します。
その後、初期データと改変後データを並べて表示しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @Override public void onCreate(Bundle savedInstanceState) { ...... Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.sample); Bitmap subbmp = bmp.copy(Bitmap.Config.RGB_565, true ); int width = subbmp.getWidth(); int height = subbmp.getHeight(); int pixels[] = new int [width * height]; int cnt = 0 ; subbmp.getPixels(pixels, 0 , width, 0 , 0 , width, height); nativeBinarization(pixels,subbmp.getWidth(),subbmp.getHeight()); subbmp.setPixels(pixels, 0 , width, 0 , 0 , width, height); LinearLayout llayout = (LinearLayout)findViewById(R.id.llayout); ImageView binarizeBmp = new ImageView( this ); binarizeBmp.setImageBitmap(subbmp); ImageView normalBmp = new ImageView( this ); normalBmp.setImageBitmap(bmp); llayout.addView(normalBmp); llayout.addView(binarizeBmp); } |
ヘッダファイルの作成
ヘッダファイルの作成を行います。
#win環境では、cygwinを用いて下さい。
#また、javah に対してPATHを通す必要があるかもしれません。
プロジェクトのトップディレクトリ(Eclipse-workspace/<Project名>/)
において、以下コマンドを実行することで、ヘッダを作成することができます。
1 | $ javah -classpath bin -o jni/<header名.h> org.jpn.techbooster.sample.nativesample2.Main |
変更が必要な部分はheader名と、最後の引数です。
org.jpn.techbooster.sample.nativesample2.Main は、
org.jpn.techbooster.sample.nativesample パッケージの Main クラスにあるNative-Methodを探しに行ってね!
という意味合いです。
これで、jniフォルダにヘッダが作成されています。
NativeCodeを記述する
最後に、Native部分を実装します。
まず、Android.mkにおいて LOCAL_SRC_FILES に記載したファイルを作成します。
内部のソースコードは以下の通りです。
まずは、関数の書き出しまで、
1 2 3 4 5 6 7 | #include <jni.h> #include <android/log.h> #include "native_sample.h" JNIEXPORT void JNICALL Java_org_jpn_techbooster_sample_nativesample2_Main_nativeBinarization (JNIEnv *env, jobject thiz, jintArray colors, jint w, jint h) { |
jniヘッダは必ずincludeする必要があります。
関数の名称ですが、作成したヘッダファイルからコピーし、作成します。
#また、自作した引数の他に2つ引数が増えていますが、JNIの仕様だと思ってください。
#詳しくはJNIについて調べると良いです。
続いて処理の部分について、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | int r, g, b; int cnt; jint* bmpColors = (*env)->GetIntArrayElements(env,colors, 0 ); for (cnt= 0 ; cnt < (w * h); cnt++){ /* rgb 抽出 */ r = (bmpColors[cnt] & 0x0000F800 ) >> 11 ; g = (bmpColors[cnt] & 0x000007E0 ) >> 5 ; b = (bmpColors[cnt] & 0x0000001F ); /* 色データ組み換え */ bmpColors[cnt] = (r<< 11 ) | (g << 5 ) | b; } (*env)->ReleaseIntArrayElements(env, colors, bmpColors, 0 ); } |
以下二行に関しては、JNIでの特殊な書き方になります。 対になっており、cでのmalloc-freeと同様に、使用後は必ずReleaseするようにします。
1 2 | <pre>jint* bmpColors = (*env)->GetIntArrayElements(env,colors, 0 ); <pre>(*env)->ReleaseIntArrayElements(env, colors, bmpColors, 0 ); |
残りの部分はRGB_565に則り、各色のデータを取得し、
Redデータのみを返さない様な形で実装しています。
NativeCodeをコンパイルする
1 2 3 | </div> <div>$ndk-build</div> <div> |