AndroidStudio下通过JNI实现java和c互调

来源:互联网 发布:js name选择器 编辑:程序博客网 时间:2024/05/17 08:26

  首先,自己也是刚刚开始学习在Android中使用JNI来进行Java和C语言之间的调用,对于一些细节包括一些中间的过程理解都不是很透彻,下面将通过一个简单的例子来进行实现。
  
1.配置NDK

  (1)下载NDK的压缩包:下载地址
  (2)将下载完成的NDK压缩包进行解压,需要注意的是解压到的目录路径中不要包含中文字符。
  (3)在环境变量中进行配置路径,配置的路径应该到ndk-build所在的目录下,例如:C:\Users\Windows10\Downloads\android-ndk-r13b-windows-x86_64\android-ndk-r13b;这里需要根据自己的安装位置进行配置。
  
2.配置AndroidStudio
  
  (1)在AndroidStudio中,需要使用到JNI的project中,修改其配置文件local.properties。在其中添加如下代码:
  ndk.dir=C:\Users\Windows10\Downloads\android-ndk-r13b-windows-x86_64\android-ndk-r13b
  这一行的代码的意思是指定ndk的安装目录,当然安装路径应该和你自身的安装路径相一致。
  (2)在需要使用到JNI的project中,修改其gradle.properties文件,在其中添加如下代码:
  android.useDeprecatedNdk=true (这一行代码如果不添加在编译的时候会报错,意思大概是允许使用什么方法,具体不太清楚)
  
3.在module中新建一个普通的类,并在类中定义native方法。

public class JNIS {//类似于一个抽象的方法,返回值为一个字符串,这时候会报红,不用理他public native String callCDemo();

}

4.生成.h文件

在cmd控制台中进入当前moudule的java文件夹下,例如路径(一定是进入java目录下):E:\Archive\Archive\NoWorkDemo\app\src\main\java;在控制台中输入:javah com.example.windows10.noworkdemo.JNIS。这里的javah后面跟的是上面新建的JNIS类的全类名,然后回车,此时在你的包下会自动生成一个.h为后缀的文件。

5.新建jni文件夹

在当前moudle下的main文件夹下右击鼠标,选择new->Folder->JNI Folder,并点击finish。这时系统会创建一个jni的文件夹。此时,将上一步中自动生成的.h文件拖拽到jni文件夹下。

6.在jni文件夹下新建.c文件

在jni文件夹下右击鼠标->new->C/C++ Source File,把创建头文件的选项去掉,然后将类型选择为.c类型的文件并输入名称,以名称为test为例,点击确定完成创建。需要注意的是,jni文件夹下要有至少两个.c的文件(如果只需要一个,另一个随便创建一个就可以,里面什么都不用写),否则会出现编译错误。(我没试过,有兴趣的可以尝试一下。。)

7.补全新建的名为test的c文件

(1)导包,在顶部添加如下代码:

    //导入的包即为前面生成的.h文件    #include "com_example_windows10_noworkdemo_JNIS.h"

(2)在之前生成的.h文件中找到自动生成的与JNIS类中对应的方法,例如:

JNIEXPORT jstring JNICALL Java_com_example_windows10_noworkdemo_JNIS_callCDemo  (JNIEnv *, jobject);

将以上代码复制到test.c的文件下,并补全方法,例如:

    //需要补全的地方有,参数中的变量名,以及补全方法体(参数1是C中定义的一个东西,不知道具体叫啥;参数2是调用这个方法的对象,即为java中的对象。方法体中的内容为C的程序)    JNIEXPORT jstring JNICALL Java_com_example_windows10_noworkdemo_JNIS_callCDemo    (JNIEnv *env, jobject jobj) {return (*env)->NewStringUTF(env, "hello from C");};

8.在当前moudle的build.gradle中修改jni资源文件的名称
  这一步不是必要进行的,旨在为了形成一个良好的习惯。
在build.gradle的defaultConfig的大括号内添加以下代码:

    ndk{        moduleName "jnis"        abiFilters "armeabi" ,"armeabi-v7a","x86"    }

  这里改动的moduleName是修改在当前moudle下的build->intermediates->ndk->lib 再下级的目录中生成的.so文件的名称。
  .so文件名称的生成规则是默认为libapp.so。修改以后的名称为libjnis.so。下面的sbiFilters 是管理在某种型号的cpu下生成的文件(这里理解的不是很清楚)。

9.在最开始创建的JNIS类中导入生成的.so库文件
  在JNIS类中添加静态代码块:
 //在System.loadLibrary()中的字符串为生成的.so文件去掉lib和.so的名称。比如,你如果在第8步中修改了ndk的moduleName,那这里需要填入的就是你的moduleName的名称,如果你没有修改的话,那默认的应该载入名称为app。

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

10.开始调用
  以上的工作完成了,下面就是该调用c了,比如:在我启动的Activity下的onCreate()方法中调用。

    protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    //new一个刚才创建的类的对象    JNIS jnis = new JNIS();    //用new出来的对象调用其中的native方法,并接收返回值    String s = jnis.callCDemo();    //打印接收到的返回值    Log.e("TAG", s);}

结语:到此为止Java调用C就基本结束了,在最后的打印中可以看到来自c中的代码,”hello from c”;至于利用c调java,因为本人还没有亲自尝试过,所以现在思路不是很清晰,留待明天自己尝试过以后再进行补全。当然,本人尝试的都是一些简单的调用,旨在给一些和我一样刚刚接触JNI的同学们一些简单的启发。同样的,由于水平有限,如果文中有什么明显的错误,欢迎大牛们予以指正。

0 0