1-安卓底層-基礎

1-安卓底層–基礎


先看看代碼: java 調用 C/C++ 代碼

1.TestJni.java
public class TestJni{
  static{
      System.loadLibrary("xxx"); //加載 libxxx.so 庫文件
  }
  public native void hello();  //本地方法,在so文件裏實現

  public static void main (String [] args){
     TestJni d = new TestJni();
     d.hello();  //調用庫文件裏面的hello方法
  }
}
2.編譯java程序 javac TestJni.java 生成 TestJni.class
3.通過javah生成jni接口 javah TestJni 生成 TestJni.h
4.新建xxx.c 文件 然後實現接口
#include<jni.h>
JNIEXPORT void JNICALL Java_TestJni_hello(JNIEnv * env, jobject obj){
    printf("hello world\n");
}
5.編譯成so庫文件 ( linux 動態庫命名規則 lib + 庫名 + .so window 庫名+.dll )

gcc -shared -fPIC xxx.c -o libxxx.so -I /usr/lib/jvm/java-7-openjdk-amd64/include/
* -I 指定頭文件的路徑 -L 指定庫的路徑 -l更上名字 -lm -lsqlite3

6.指定動態庫的路徑 export LD_LIBRARY_PATH=:
7.運行java TestJni

* jni.h 在編譯android源碼時要安裝jdk 5.0之前 直接下載甲骨文的jdk 5.0之後 要安裝openjdk (sudo apt-get install openjdk+版本)注意 android 和jdk的版本有對應關係(android 官網)

* Java_TestJni_hello 接口的名字 命名規則 Java_+類名_+本地方法名 接口的返回值和方法的返回值一致

* JNIEnv jni總管 他是一個函數指針數組的首地址 成員爲函數指針 jobject java對象


第二種

第二種方式的jni實現
 vi /usr/lib/jvm/java-7-openjdk-amd64/include/linux/jni.h
1. 完成入口函數
1944 JNIEXPORT jint JNICALL
1945 JNI_OnLoad(JavaVM *vm, void *reserved);

2. 在入口函數裏面實現 一下三步
2.1 獲得java虛擬機環境
jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);

2.2 找到先關的類
                jclass (JNICALL *FindClass)
 226       (JNIEnv *env, const char *name);
*    (*env)->FindClass(env, "java/lang/String")
2.3 註冊
 720     jint (JNICALL *RegisterNatives)
 721       (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jint nMethods);


 完成 JNINativeMethod 這個結構體 作用是本地方法和本地函數的映射關係

 180 typedef struct {
 181     char *name; //本地方法的名字
 182     char *signature; // 本地方法的簽名
 183     void *fnPtr; // 相對應的本地函數
 184 } JNINativeMethod;
函數簽名通常是以下結構:
  • 返回值 fun(參數1,參數2,參數3);
  • 其對應的Jni方法簽名格式爲:(參數1參數2參數3) 返回值
  • 注意:
    • 函數名,在Jni中沒有體現出來
    • 參數列表相挨着,中間沒有逗號,沒有空格
    • 返回值出現在()後面
    • 如果參數是引用類型,那麼參數應該爲:L類型;
第一種jni 和第二種jni 實現方式有何不一樣
  1. 第二種有入口函數 可以對jni 做一些初始化工作

  2. 第二種方式是通過 jninativemethod 這個結構體來匹配的

  3. 第一種是靠名字匹配的

實現接口文件

1 實現JNI_OnLoad 函數

2 是在入口函數裏面獲得jvm 環境變量 通過 GetEnv 這個函數

3 找類 findclass();

4 註冊 RegisterNatives method 這個結構體

5 實現 method 這個結構體 讓java的本地方法 和 jni的本地函數綁定在一起

寫法

TestJni.java
public class TestJni{
    static {
        System.loadLibrary("native");
    }
    public native int hello(int i,char j);


    public static void main (String [] args){
        TestJni d = new TestJni();
        d.hello(12,'r');
    }
}
native.c
#include <jni.h>

jint Jhello(JNIEnv *env,jobject obj,jint i,jchar j){
    printf("%d\t%c\n",i,j);
}

//函數數組 ,
//參數1 java 裏寫的本地方法名
//參數2 簽名(看下面的圖)頭文件裏自動生成
//參數3 調用的函數的指針

JNINativeMethod method[] = {
    "hello","(IC)I",(void *)Jhello,
};

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved)
{
    JNIEnv *env;
    jclass cls;

    if((*vm) -> GetEnv(vm,(void **)&env,JNI_VERSION_1_4))
      return JNI_ERR;

    cls = (*env) ->FindClass(env,"TestJni");
    if(cls == NULL) return JNI_ERR;

  //註冊函數
    (*env) -> RegisterNatives(env,cls,method,sizeof(method)/sizeof(JNINativeMethod));

    return JNI_VERSION_1_4;
}

簽名類型

簽名類型.PNG

兩者之間的數據類型

兩者之間的數據類型.PNG


java傳數組

TestJni.java
public class TestJni{

    static {
        System.loadLibrary("native");
    }
    public native int hello(int []arr,int len);

    public static void main (String [] args){
        int []ibo = {12,13,14,15};
        TestJni d = new TestJni();

        System.out.println(d.hello(ibo,ibo.length));
    }
}
native.c

“`

include

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章