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
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
APP_PLATFORM := android-8
同じ様に、赤字が修正箇所です。
Androidのプラットフォームバージョンを指定します。
#今回はAndoid-8(ver 2.2) 向けに作成
Java側のソースコードの記述
先にJavaソースコードにおいて、native-Methodを記述することで
ヘッダファイルをコマンドから作成することができます。
まず、以下の様にLibraryの呼び出し部分を記述します。
ここで引数に渡す値は、LOCAL_MODULEに指定した値と同じにしておきます。
public class Main extends Activity { static { System.loadLibrary("native_sample"); } ......
次に、native-Methodを記載します。
今回は、bitmapのピクセルデータをNativeへ受け渡すため、int-arrayを引数に持っています。
public native void nativeBinarization(int[] pixels, int x, int y);
最後に、Methodの呼び出し部分を記載します。
本サンプルでの流れは、 リソースから取得したBitmapオブジェクトを複製し、(AndroidはデフォルトではRGB_565を使用しています)
ピクセル毎のintデータを作成、Native-Methodへ受け渡します。
その後、初期データと改変後データを並べて表示しています。
@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名>/)
において、以下コマンドを実行することで、ヘッダを作成することができます。
$ 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 に記載したファイルを作成します。
内部のソースコードは以下の通りです。
まずは、関数の書き出しまで、
#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について調べると良いです。
続いて処理の部分について、
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するようにします。
<pre>jint* bmpColors = (*env)->GetIntArrayElements(env,colors,0); <pre>(*env)->ReleaseIntArrayElements(env, colors, bmpColors, 0);
残りの部分はRGB_565に則り、各色のデータを取得し、
Redデータのみを返さない様な形で実装しています。
NativeCodeをコンパイルする
</div> <div>$ndk-build</div> <div>