Android JNIのJavaとCが呼び出し合う

10178 ワード

概要
JNIって何?
JNIはJava Native Interfaceの略で、いくつかのAPIを提供してJavaと他の言語の通信(主にC&C++)を実現しています.Java 1.1からJNI標準はjavaプラットフォームの一部となり、Javaコードと他の言語で書かれたコードとのインタラクションを可能にします.
NDKって何?
NDKはNative Development Kitの略です.SDK(ソフトウェア開発キット)ソフトウェア開発キットの一部ですが、通常は単独でダウンロードする必要があります.詳細はNDKについて.
JNIのメリットとデメリット
  • の利点:
  • は他の言語と対話し、それぞれ長所を取っている.
  • 反コンパイル難易度
  • を増加
  • 欠点:
  • Javaクロスプラットフォームの優位性を失う

  • Java呼び出しC
  • 配置module下のbuild.gradle
    android {
       compileSdkVersion 25
       buildToolsVersion "25.0.0"
       defaultConfig {
           ...
           ndk {
               moduleName "CallEachOther" //    so    ,  loadLibrary       
               abiFilters "armeabi","armeabi-v7a","x86","x86_64","mips","arm64-v8a","mips64"//       
           }
       }
    }
  • .
  • Javaクラス
    public class JNITest_Java {
       static {
           System.loadLibrary("CallEachOther");
       }
    
       public native String getStringFromC();
    }
    ここでSystem.loadLibraryのパラメータはbuild.gradlemoduleNameの値、すなわちCallEachOtherである.ここではnativegetStringFromC()を定義した.これまで、Java端は一時的に一段落した.次に、ヘッダファイルを生成します.
  • .hヘッダファイルを生成
  • ASに付属のTerminalcd src/main/javaコマンドを開いてJavaフォルダの下に入ります.ps:cmdコマンドと同様の効果
  • を使用
  • コマンドjavah . を入力します.たとえば、javah com.dongyk.jnitest.JNITest_Java。は、Javaディレクトリの下で _ .hを生成します.AS2.2.2が開くと赤くなり、ASbugのようです.ただし、通常のコンパイルには影響しません.

  • mainディレクトリの下にjniフォルダを新規作成し、JniTestC.cを新規作成します.
    
    #include 
    
    
    #include 
    
    
    #include "com_dongyk_jnitest_JNITest_Java.h"
    
    
    JNIEXPORT jstring JNICALL Java_com_dongyk_jnitest_JNITest_1Java_getStringFromC(JNIEnv * env, jobject jobj){
       char* str = "I come from C";
       return (*env)->NewStringUTF(env,str);
    };
    ここの方法の名前には一定のルールがあります.フォーマット:Java_ _ .メソッド名が長すぎる場合は、生成したばかりの.hファイルからコピーすることをお勧めします.この方法を簡単に説明します.
  • JNIEnv * env envポインタは、関数ポインタテーブルを指します.
  • jobject jobjは、Javaコードでインスタンス化されたJavaオブジェクトを指し、thisポインタに相当する.
  • (*env)->NewStringUTFは、env#NewStringUTF()メソッドを呼び出すことを表す.
  • は最後に「I come from C」に戻る.

  • は、MainActivityで呼び出される.
    public class MainActivity extends AppCompatActivity {
       private TextView tv;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           tv = (TextView) findViewById(R.id.tv);
           String result = new JNITest_Java().getStringFromC();
           tv.setText(result);
       }
    }

  • C Java呼び出し
  • Javaクラスにメソッド
    public class JNITest_Java {
        static {
            System.loadLibrary("JavaCallC");
        }
    
        public native String getStringFromC();
    
        public native int callAdd();
    
        public int add(int a, int b) {
            Log.i("TAG","add was called");
            return a + b;
        }
    }
    を追加して、まず流れを理解します.Java階層で呼び出されたのはJavaコードに違いありません.ここにはcallAdd()メソッドが書かれています.このメソッドを呼び出すとき、Cadd()メソッドを呼び出すように通知します.このプロセスでは、CJavaメソッドの具体的な実装として最初に実行され、CJavaメソッドが呼び出される.その後、javahコマンドが呼び出され、.hヘッダファイルが生成される.
    /* DO NOT EDIT THIS FILE - it is machine generated */
    
    #include 
    
    /* Header for class com_dongyk_jnitest_JNITest_Java */
    
    
    #ifndef _Included_com_dongyk_jnitest_JNITest_Java
    
    
    #define _Included_com_dongyk_jnitest_JNITest_Java
    
    
    #ifdef __cplusplus
    
    extern "C" {
    
    #endif
    
    /*
     * Class:     com_dongyk_jnitest_JNITest_Java
     * Method:    getStringFromC
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL ava_com_dongyk_jnitest_JNITest_1Java_getStringFromC
    (JNIEnv
    *, jobject);
    
    /*
     * Class:     com_dongyk_jnitest_JNITest_Java
     * Method:    callAdd
     * Signature: ()V
     */
    JNIEXPORT jint JNICALL Java_com_dongyk_jnitest_JNITest_1Java_callAdd
    (JNIEnv *, jobject);
    
    
    #ifdef __cplusplus
    
    }
    
    #endif
    
    
    #endif
    
  • .c主関数の具体的な実装
    JNIEXPORT jint JNICALL Java_com_dongyk_jnitest_JNITest_1Java_callAdd(JNIEnv *env, jobject jobj){
        //      
        jclass clazz = (*env)->FindClass(env,"com/dongyk/jnitest/JNITest_Java");
        //      
        jmethodID jmethodid = (*env)->GetMethodID(env,clazz,"add","(II)I");
        //     
        jobject jobject = (*env)->AllocObject(env,clazz);
        //     
        return (*env)->CallIntMethod(env,jobject,jmethodid,3,5);
    };
    (*env)->GetMethodID の最後のパラメータは、方法署名である.Javaはメソッドのリロードをサポートするが、これらのリロードのメソッドはJniで同じ名前で命名され、関数のリロードを区別するためにメソッド署名が導入される.メソッド署名の取得:まずrebulidで工事を行い、その後cd build\intermediates\classes\debugで、その後javap -s / ですべてのメソッド署名を取得します.例えば、javap -s com/dongyk/jnitest/JNITest_Java
    public int add(int, int);
       descriptor: (II)I
    descriptorに対応するのがメソッド署名である.もちろんです.中にはFindClass、GetMethodIDなどの方法もあります.詳しくはXXXsdkdk-bundleplatformsandroid-xxarch-armusrincludejni.hを参照してください.
  • は、MainActivityで呼び出される.
    public class MainActivity extends AppCompatActivity {
        private TextView tv;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            tv = (TextView) findViewById(R.id.tv);
            int result = new JNITest_Java().callAdd();
            tv.setText(result+"");
        }
    }
    これで、Jni初入門の小Demoの作成が完了~