少し間が空きましたがまたモチベーションが出てきたので続きを書きます(笑)
先回はAndroid実機上で実行するInstrumentedTestでUIを表示しました。
今回はJava側で生成した多次元配列をC++側に渡し、C++側でバイナリデータをセットします。そしてそのデータをJava側に受け渡し、ビットマップとしてImageViewに表示します。
実際のユースケースとしては二つのカメラから取得した映像データを画面に表示するような時に利用できます。特殊ですねw
Activityの変更
先回作成したActivityに二つのImageViewを配置します。
ImageViewの名称はそれぞれimageView01, imageView02としておきます。
Java側InstrumentedTestの実装
InstrumentedTestでは後述するC++メソッドに二つのバイナリバッファを渡し、データを格納させます。
@Test
public void imageRefreshTest() throws InterruptedException {
int stride = 320;
int lines = 240;
Bitmap[] bitmaps = {
Bitmap.createBitmap(stride, lines, Bitmap.Config.ARGB_8888),
Bitmap.createBitmap(stride, lines, Bitmap.Config.ARGB_8888)
};
int[][] buffers = {
new int[stride * lines],
new int[stride * lines]
};
assertTrue(getBufferArray(buffers));
bitmaps[0].setPixels(buffers[0], 0, stride, 0, 0, stride, lines);
bitmaps[1].setPixels(buffers[1], 0, stride, 0, 0, stride, lines);
this.refreshImageOnUiThread(bitmaps);
Thread.sleep(5000);
}
下記の処理を行っています。
- UIに表示する2つのBitmapを生成
- C++に受け渡す2つバイナリバッファをもつ2次元配列 buffresを生成
- C++関数 getBufferArray()をコール
- バイナリバッファの値をBitmapにセットする
- UIスレッドでBitmapをImageViewに表示する
このコードからわかるようにバッファはJava側で生成されたものです。
このバッファをC++でアクセスしてみましょう。
C++ネイティブコードからJavaの多次元配列にアクセスする
C++側は下記のようなコードとなっています。
native-lib.cpp
extern "C" JNIEXPORT jboolean JNICALL Java_com_example_satoshi_1maemoto_myapplication_ExampleInstrumentedTest_getBufferArray(JNIEnv *env, jobject, jobjectArray array) {
auto elementCount = env->GetArrayLength(array);
if (elementCount == 0) {
return false;
}
for (auto index = 0; index < elementCount; index++) {
jintArray element = (jintArray)env->GetObjectArrayElement(array , index);
auto buffer = env->GetIntArrayElements(element, nullptr);
auto bufferCount = env->GetArrayLength(element);
auto color = ((index % 2) == 0) ? 0x00000000 : 0x00FFFFFF;
auto step = ((index % 2) == 0) ? 1 : -1;
for (auto pixel = 0; pixel < bufferCount; pixel++) {
buffer[pixel] = 0xFF000000 | color;
color += step;
}
env->ReleaseIntArrayElements(element, buffer, 0);
env->DeleteLocalRef(element);
}
return true;
}
- env->GetArrayLengh()で配列の要素数を取得できる
- env->GetObjectArrayElement()でJava側2次元配列の1次元目の要素を取得する。このサンプルではint配列が格納されている要素[element]が取得できる。
- env->GetIntArrayElements()でさらに[element]に格納されているint配列[buffer]を取得する。
- bufferにデータを格納する
- 最後に参照を開放する
このようにC++側では配列の要素を取得して利用、使い終わったら参照を開放する、という手順でデータアクセスをします。
少し手間ですがJava側は非同期にGCが走ることがあるのでこのような使い方になるようです。
UIスレッドでBitmapを表示する
C++側からデータが取得できたのでUIに表示しましょう。
この処理も先回のテキスト表示と同様UIスレッドで処理を行わせる必要があります。
そのため下記のようなユーティリティ関数を用意しました。
private void refreshImageOnUiThread(Bitmap[] bitmaps)
{
class ShowImagesAction implements Runnable
{
private Bitmap[] bitmaps;
public ShowImagesAction(Bitmap[] bitmaps)
{
this.bitmaps = bitmaps;
}
@Override
public void run() {
ImageView imageView01 = rule.getActivity().findViewById(R.id.imageView01);
imageView01.setImageBitmap(this.bitmaps[0]);
ImageView imageView02 = rule.getActivity().findViewById(R.id.imageView02);
imageView02.setImageBitmap(this.bitmaps[1]);
}
}
this.rule.getActivity().runOnUiThread(new ShowImagesAction(bitmaps));
}
実際にInstrumentedTestを実行すると下記のようにグラデーションのかかった2つの映像が表示されます。

まとめ
Andoid実機で動作するInstrumentedTestでC++側コードと多次元配列をやり取りする方法をお伝えしました。
プロジェクト一式は下記にアップしています。
github.com