AndroidにおけるJavaからC++呼び出しの2つの手法 - 静的登録と動的登録

1. 静的登録

静的登録は、JavaのメソッドとC++の関数名をJNIの命名規則に従って厳密に一致させる仕組みです。この方法は実装がシンプルですが、関数名に特定のルールが適用されます。

  • Activity内でnativeメソッドを宣言
  • C++側で対応する関数を実装

サンプルコード:

Java側(MainActivity):

public native String stringFromJNI();

C++側(native-lib.cpp):

extern "C" JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Java 调用 C++ 静态注册方法";
    return env->NewStringUTF(hello.c_str());
}

2. 動的登録

動的登録は、実行時にJavaメソッドとC++関数をマッピングする仕組みです。関数名に制約がなく、柔軟性が高い方法です。

実装手順:

  1. Java側でnativeメソッドを宣言
  2. C++側で任意の名前で関数を実装
  3. 登録用テーブルを定義
  4. JVM起動時に登録処理を行う関数を実装

サンプルコード:

Java側(MainActivity):

public native String getStringFromNative();

C++側(native-lib.cpp):

jstring getStringFromNative(JNIEnv* env, jobject /* this */) {
    std::string hello = "Java 调用 C++ 动态注册方法";
    return env->NewStringUTF(hello.c_str());
}

登録テーブルの定義

static JNINativeMethod methods[] = {
        {"getStringFromNative", "()Ljava/lang/String;", (void *)getStringFromNative}
};

JVM起動時の登録処理

jint JNI_OnLoad(JavaVM *vm,void* reserved){
    JNIEnv *env =  NULL;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jclass clazz = env->FindClass("com/marxist/firstjni/MainActivity");
    if (clazz == nullptr) {
        return JNI_ERR;
    }
    if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

3. 完全なコード例

Java側(MainActivity):

package com.marxist.firstjni;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;

import com.marxist.firstjni.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("firstjni");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI()+"\n"+getStringFromNative());
    }

    public native String stringFromJNI();
    public native String getStringFromNative();
}

C++側(native-lib.cpp):

#include 
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_marxist_firstjni_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Java 调用 C++ 静态注册方法";
    return env->NewStringUTF(hello.c_str());
}

jstring getStringFromNative(JNIEnv* env, jobject /* this */) {
    std::string hello = "Java 调用 C++ 动态注册方法";
    return env->NewStringUTF(hello.c_str());
}

static JNINativeMethod methods[] = {
        {"getStringFromNative", "()Ljava/lang/String;", (void *)getStringFromNative}
};

jint JNI_OnLoad(JavaVM *vm,void* reserved){
    JNIEnv *env =  NULL;
    if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;
    }
    jclass clazz = env->FindClass("com/marxist/firstjni/MainActivity");
    if (clazz == nullptr) {
        return JNI_ERR;
    }
    if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

タグ: JNI Android NDK C++

7月11日 17:20 投稿