如何在android下使用jni

2025-03-24 06:26:20
推荐回答(1个)
回答1:

第一步:

  使用Java编写HelloWorld 的Android应用程序:

  复制代码
  package com.lucyfyr;
  import android.app.Activity;
  import android.os.Bundle;
  import android.util.Log;

  public class HelloWorld extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  Log.v("dufresne", printJNI("I am HelloWorld Activity"));
  }
  static
  {
  //加载库文件
  System.loadLibrary("HelloWorldJni");
  }
  //声明原生函数 参数为String类型 返回类型为String
  private native String printJNI(String inputStr);
  }
  复制代码
  

  这一步可以使用eclipse来生成一个App;

  因为eclipse会自动为咱们编译此Java文件,后面要是用到。

  第二步:

  生成共享库的头文件:

  进入到eclipse生成的Android Project中 :/HelloWorld/bin/classes/com/lucyfyr/ 下:

  可以看到里面后很多后缀为.class的文件,就是eclipse为咱们自动编译好了的java文件,其中就有:

  HelloWorld.class文件。

  退回到classes一级目录:/HelloWorld/bin/classes/

  执行如下命令:

  javah com.lucyfyr.HelloWorld

  生成文件:com_lucyfyr_HelloWorld.h

  

  复制代码
  /* DO NOT EDIT THIS FILE - it is machine generated */
  #include
  /* Header for class com_lucyfyr_HelloWorld */
  #ifndef _Included_com_lucyfyr_HelloWorld
  #define _Included_com_lucyfyr_HelloWorld
  #ifdef __cplusplus
  extern "C" {
  #endif
  /*
  * Class: com_lucyfyr_HelloWorld
  * Method: printJNI
  * Signature: (Ljava/lang/String;)Ljava/lang/String;
  */
  JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI
  (JNIEnv *, jobject, jstring);
  #ifdef __cplusplus
  }
  #endif
  #endif
  复制代码
  

  可以看到自动生成对应的函数:Java_com_lucyfyr_HelloWorld_printJNI

  Java_ + 包名(com.lucyfyr) + 类名(HelloWorld) + 接口名(printJNI):必须要按此JNI规范来操作;

  java虚拟机就可以在com.simon.HelloWorld类调用printJNI接口的时候自动找到这个C实现的Native函数调用。

  当然函数名太长,可以在.c文件中通过函数名映射表来实现简化。

  第三步:

  实现JNI原生函数源文件:

  新建com_lucyfyr_HelloWorld.c文件:

  

  复制代码
  #include
  #define LOG_TAG "HelloWorld"
  #include
  /* Native interface, it will be call in java code */
  JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr)
  {
  LOGI("dufresne Hello World From libhelloworld.so!");
  // 从 instring 字符串取得指向字符串 UTF 编码的指针
  const char *str =
  (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE );
  LOGI("dufresne--->%s",(const char *)str);
  // 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。
  (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str );
  return (*env)->NewStringUTF(env, "Hello World! I am Native interface");
  }

  /* This function will be call when the library first be load.
  * You can do some init in the libray. return which version jni it support.
  */
  jint JNI_OnLoad(JavaVM* vm, void* reserved)
  {
  void *venv;
  LOGI("dufresne----->JNI_OnLoad!");
  if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
  LOGE("dufresne--->ERROR: GetEnv failed");
  return -1;
  }
  return JNI_VERSION_1_4;
  }
  复制代码
  

  OnLoadJava_com_lucyfyr_HelloWorld_printJNI

  函数里面做一些log输出 注意JNI中的log输出的不同。

  JNI_OnLoad函数JNI规范定义的,当共享库第一次被加载的时候会被回调,

  这个函数里面可以进行一些初始化工作,比如注册函数映射表,缓存一些变量等,

  最后返回当前环境所支持的JNI环境。这里只是简单的返回当前JNI环境。

  

  第四步:

  编译生成so库

  编译com_lucyfyr_HelloWorld.c成so库可以和app一起编译,也可以都单独编译。

  在当前目录下建立jni文件夹:HelloWorld/jni/

  下建立Android.mk ,并将com_lucyfyr_HelloWorld.c和 com_lucyfyr_HelloWorld.h 拷贝到进去

  编写编译生成so库的Android.mk文件:

  

  复制代码
  LOCAL_PATH:= $(call my-dir)
  # 一个完整模块编译
  include $(CLEAR_VARS)
  LOCAL_SRC_FILES:=com_lucyfyr_HelloWorld.c
  LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
  LOCAL_MODULE := libHelloWorldJni
  LOCAL_SHARED_LIBRARIES := libutils
  LOCAL_PRELINK_MODULE := false
  LOCAL_MODULE_TAGS :=optional
  include $(BUILD_SHARED_LIBRARY)
  复制代码
  

  系统变量解析:

  LOCAL_PATH - 编译时的目录
  $(call 目录,目录….) 目录引入操作符
  如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径

  include $(CLEAR_VARS) -清除之前的一些系统变量
  LOCAL_MODULE - 编译生成的目标对象
  LOCAL_SRC_FILES - 编译的源文件
  LOCAL_C_INCLUDES - 需要包含的头文件目录
  LOCAL_SHARED_LIBRARIES - 链接时需要的外部库
  LOCAL_PRELINK_MODULE - 是否需要prelink处理
  include$(BUILD_SHARED_LIBRARY) - 指明要编译成动态库