欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android studio 下JNI编程实例并生成so库的实现代码

程序员文章站 2023-12-19 20:56:16
最近需要使用jni编程,学了下jni,并且在android studio下实现了一个小demo。这期间有一些坑,还好都解决了,想分享出来,希望大家少走弯路。本文中采用的平台...

最近需要使用jni编程,学了下jni,并且在android studio下实现了一个小demo。这期间有一些坑,还好都解决了,想分享出来,希望大家少走弯路。本文中采用的平台是windows,ndk环境已经搭建好,这方面资料很多,大家可以自行百度。

本文分为两个部分:

1.如何通过编写jni实现native方法的调用。

2.怎样生成.so动态库提供给第三方使用。

以下是正文:

一,编写jni文件,实现本地方法

1,建立一个新工程,只有一个mainactivity,里面加载库文件并且调用若干本地方法,然后通过android studio里的build-makeproject生成class文件。

public class mainactivity extends activity {
  private final string tag = "jnitest"

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    string s=returnstring();
    log.d(tag,s);
    int a=1000;
    log.d(tag,sayhello(a));
  }
  //加载jni
  static {
    system.loadlibrary("nativetest");
  }
  //声明native方法
  private native int sayhello(int t);
  private native string returnstring();

}

2,生成.h头文件,里面有android工程里本地方法的声明。这个文件可以自己写,但是推荐用javah自动生成。生成方法为:在控制台或者android studio自带的控制台使用javah命令将上一步make之后生成的class文件生成.h头文件,这里在用javah的时候有可能会出问题,比如我第一次就出现了找不到app.activity ,即找不到类文件,这种问题一般是没有理解javah的用法造成的。我当时采用以下两种方法:

方法1: cd到 e:\shijue\jnihello\app\src\main

然后输入 javah -d jni -classpath i:\andriod\androidsdk\platforms\android-15\android.jar;
e:\shijue\jnihello\app\build\intermediates\classes\debug com.example.machenike_pc.jnihello.mainactivity

说明:javah是生成头文件的命令,深绿色为生成文件夹jni,紫红色为android.jar所在的位置,浅绿色为class文件的路径+类全名(路径最后一个文件夹是debug之后空格+类全名)

(这里补充下-classpath的含义:javah操作是针对类文件,-bootclasspath和-classpath就是指定在哪里进行类文件搜索。jdk搜索类文件先后顺序如下:bootstrap classes,user classes。bootstrap默认的是jdk自带的jar或zip文件,它包括jre\lib下rt.jar等文件,jdk首先搜索这些文件.可以通过-bootclasspath来设置它。文件之间用分号";"进行分割。user classes搜索顺序为当前目录、环境变量 classpath、-classpath。它们用于告知jdk搜索类文件根目录名、jar文档名、zip文档名,用分号";"进行分隔。)

方法2: cd到e:\shijue\jnihello\app\build\intermediates\classes\debug目录下,直接javah -d jni com.example.machnike_pc.jnihello.mainactivity 即可

3,在生成的jni目录下写一个c或者c++文件,文件名随意,实现本地方法 ,之后需要在该路径下再加一个空的cpp或c文件(估计是软件的bug,不加的话很可能出ndk错误),比如我加了个util.cpp的文件,里面什么都不写。

下面是我的c++代码

#include<jni.h>
#include<stdio.h>
#include<com_example_machenike_pc_jnitest2_mainactivity.h>

#ifdef __cplusplus
extern "c" {
#endif
jniexport jint jnicall java_com_example_machenike_1pc_jnitest2_mainactivity_sayhello
 (jnienv *, jobject, jint);

jniexport jstring jnicall java_com_example_machenike_1pc_jnitest2_mainactivity_returnstring
 (jnienv *, jobject);

#ifdef __cplusplus
}
#endif


jniexport jint jnicall java_com_example_machenike_1pc_jnitest2_mainactivity_sayhello
  (jnienv * env, jobject jobj, jint jnumber)
  {
    int modify=jnumber+1;
    return modify;


  }
jniexport jstring jnicall java_com_example_machenike_1pc_jnitest2_mainactivity_returnstring
  (jnienv *env, jobject jobj)
  {
    return env->newstringutf("i'm comes from to native function!");
   }

4,如果ndk版本不是最新的,需要在gradle.properties文件下加入:

android.usedeprecatedndk=true

5,配置ndk路径,这里也可以在as的设置里面配置。我采用的方法是在local.properties文件最后一行加入:

ndk.dir=i\:\\andriod\\ndk\\android-ndk-r10b

6,build.gradle(app下):文件下加入:(defaultconfig里面)

ndk{
modulename "nativetest"
}

此时运行程序已经可以实现本地方法了,之后可以再生成so库文件,方便使用。

二,生成.so动态库

(这里说一下,貌似android studio已经写好了.mk文件,上面的步骤完成后,直接rebuild一下就自动生成为了.so动态库,下面的方法也能生成,可以看一下,很有用)

1,在jni文件夹下新建android.mk文件,写入以下内容:

local_path := $(call my-dir)           //固定写法,把路径赋给local_path变量
include $(clear_vars)                  //清除其他local变量
local_module := nativetest             //这个模块的名字,最后生成的.so的名字就是它,要跟java里面的loadlibray的名字一样。
local_src_files := nativetest.cpp\     //这里是要编译的文件,\ 符号是换行
            util.cpp
include $(build_shared_library)        //shared_library就是动态库,即.so文件

 这里的写法是最简单的一个例子,用的时候把注释去掉。每一行都是很关键,不能省略。至于makefile怎么编写内容比较多,此处不赘述。

2,在工程根目录下新建application.make文件,写入以下内容:

app_project_path := $(call my-dir)
app_modules := nativetest
3,在命令行下,cd到jni目录(就是之前javah -d jni生成的那个文件夹)下,输入指令: ndk-build,等一会即可生成.so文件。位于lib目录下,将其放到app/src/main/jnilibs目录下就能用了。

faq:

1,生成的so文件在使用时需要注意:包名不能变,拿上文举例,本地方法位于com_example_machenike_pc_jnitest2_mainactivity这个类下,如果在别的地方用,需要完整的建立这个包名和类。
2,c和cpp文件均可以用来写jni,写法上略有不同。
3,需要注意java里面成员方法和静态方法通过javah生成的头文件略有不同,一个参数是jclass,另一个是jobject。
4,不用javah生成头文件也行,推荐第一次写的时候用javah生成,后面修改的时候(比如参数改变)可以直接在c文件里手动修改。

补充:

sourcepath: d:\work\androidstudio\visualrecognition\app\src\main\java (绝对路径)

targetpath: d:\work\androidstudio\visualrecognition\visual\src\main\jni (绝对路径)

targetclassname: com.yf.visualrecognition.unityplayeractivity (你的包名+类名)

格式: javah -d ${sourcefile} -classpath ${targetpath} ${targetclassname}

生成.h文件:javah -d e:\androidproject\githubproject\opencv\opencv\app\src\main\jni -classpath e:\androidproject\githubproject\opencv\opencv\app\src\main\java com.cosco.opencv.opencvhelper

上一篇:

下一篇: