Android Studio中如何编写JNI代码及编译so库

最近项目中需要使用到NDK编译so库,控制硬件。由于不熟悉JNI和NDK,网上搜了一些方法,做个笔记备忘和分享给用需要的朋友。

那如何用Android Studio编写JNI代码,然后生成so库呢?

来看看一个简单的实例: 编写一个native的加法函数,然后在MainActivity中调用并显示结果。

需要准备的事:
1 配置好的JDK环境和Android Studio开发环境、SDK
2 下载好NDK工具 (http://www.androiddevtools.cn/ 网站有很多Android开发需要的工具。)
NDK版本


1 新建Android工程
Android Studio中如何编写JNI代码及编译so库_第1张图片

2 添加一个类,类中包含你想用C/C++语言实现的native方法,只是声明。
MyJni.java


package com.example.hui.myjnitest ;

public class MyJni {

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

    public native int add (int a , int b) ;
}

其中有个静态代码块,在MyJni类加载时,执行块内代码。
System.loadLibrary (“myjni” ) ; 程序运行时,加载库文件,在Android(Linux)中,会搜索并加载名字为libmyjni.so的文件,如果找不到,会报异常。在Windows中会搜索myjni.dll的库

3 调用native方法

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super .onCreate(savedInstanceState) ;
        setContentView(R.layout. activity_main );

        MyJni myJni = new MyJni();
        Toast. makeText( this, "1+3=" + myJni.add( 1 , 3 ) , Toast. LENGTH_LONG).show() ;
    }
}

MyJni myJni = new MyJni(); 获得具有native方法的类对象,然后调用该对象的方法myJni.add( 1 , 3)计算1+3的结果。

4 编译工程
Build–Make Project

5 生成jni的头文件
打开Android Studio的Terminal编辑器,一般在软件底部,如果找不到,可以按快捷键 Alt+F12 打开 (我的AS版本:2.1.1)
Terminal
切换到classes所在目录:
用到的命令: cd 切换路径, dir 显示文件夹内容列表

cd app\build\intermediates\classes\debug

Android Studio中如何编写JNI代码及编译so库_第2张图片

debug目录下的路径跟包名有关,比如本例中的AndroidManifest.xml中声明的包名为

package= "com.example.hui.myjnitest"

那本例中的”.java”文件生成的.class”就在 debug\com\example\hui\myjnitest\下 (切换到该目录下看看)

2016/06/04  19:37               732 BuildConfig.class
2016/06/04  19:37             1,056 MainActivity.class
2016/06/04  19:37               454 MyJni.class
              17 个文件         76,243 字节                                                                                                                                           
               2 个目录 144,080,855,040 可用字节                                                                                                                                     
E:\MyGit\MyJniTest\app\build\intermediates\classes\debug\com\example\hui\myjnitest>

我们只用到MyJni.class文件。

好了,了解这些后,我们应该生成头文件了。(生成头文件干嘛?后面你就知道了)


在app\build\intermediates\classes\debug目录下执行

javah -classpath . -jni com.example.hui.myjnitest.MyJni

javah 用于生成头文件(*.h) (我暂时只了解这个用途)
-classpath . 有个圆点。表示设置class路径为当前路径。如果不在debug这个路径,那这个圆点可以换成具体路径
-jni 生成 JNI 样式的标头文件 (默认值)
com.example.hui.myjnitest.MyJni 包名+类名,注意不要带 .java,否则会报错的。


执行完命令,Terminal没有任何提示,在debug目录下,输入dir命令,可以看到有个 .h 文件,名字好长,其实就是把上面包名+类名中间的点换成下划线了。

来看看这个头文件都写了什么

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_example_hui_myjnitest_MyJni */

#ifndef _Included_com_example_hui_myjnitest_MyJni
#define _Included_com_example_hui_myjnitest_MyJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hui_myjnitest_MyJni
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_hui_myjnitest_MyJni_add
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

这其中的 Java_com_example_hui_myjnitest_MyJni_add 就是我们需要实现的native方法。
其他部分我就不多说了,可以百度一下,google一下。。。C语言的知识。

6 新建一个名为jni的文件夹
在工程目录app\src\main\下,新建一个jni文件夹,把上面步骤的h头文件复制到jni文件夹里。
工程目录更新为:
Android Studio中如何编写JNI代码及编译so库_第3张图片
在jni文件夹中添加一个c语言源代码文件,如myjni.c,并完成其内容。函数名从头文件中复制过来,稍作修改。(知道头文件的作用了吧)

#include "com_example_hui_myjnitest_MyJni.h"

jint JNICALL Java_com_example_hui_myjnitest_MyJni_add
  (JNIEnv *env, jobject obj , jint a , jint b)
{
    return a+b ;
}

7 再次编译工程
Build – Project
这时 Message 窗口会提示需要设置使用当前NDK。打开gradle.properties, 在最下面粘贴上 android.useDeprecatedNdk=true

Error:Execution failed for task ‘:app:compileDebugNdk’.

Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set “android.useDeprecatedNdk=true” in gradle.properties to continue using the current NDK integration.

NDK

Build – Project
提示没有配置NDK选项
Android Studio中如何编写JNI代码及编译so库_第4张图片

8 配置NDK
Android Studio中,菜单 File – Project Structure SDK Location 选择NDK路径
Android Studio中如何编写JNI代码及编译so库_第5张图片

在build.gradle(Module:app)中完成

android{
defaultConfig {
     ndk {
          moduleName "xxx"   // 目标名称,不包括lib   .so
          ldLibs "xx","xx"     // jni代码中引用的库文件,本例没有引用
          abiFilters "", "", "", ""   // 生成不同平台下的so库
     }
   }
}

相当于一个Android.mk文件。
Android Studio中如何编写JNI代码及编译so库_第6张图片

9 再次Build–Rebuild Project
没有报错则生成了so库。这个so库路径在

app\build\intermediates\ndk\debug\lib

这里写图片描述这里写图片描述

10 生成APK,用解压工具查看,apk内部有个lib文件夹,如下图所示。
Android Studio中如何编写JNI代码及编译so库_第7张图片

11 在模拟器中安装并运行。
Toast显示的是正确调用so库运行的结果。
Android Studio中如何编写JNI代码及编译so库_第8张图片

代码地址:https://github.com/BrightLin/MyJniTest
参考播客地址:http://blog.csdn.net/sodino/article/details/41946607 感谢作者 Sodino

提示:
如果编译时报错:需要在jni文件夹中添加一个空文件,名字为util.c

Error:Execution failed for task ‘:app:compileDebugNdk’. > com.android.ide.common.internal.LoggedErrorException: Failed to run command: D:\Mission\adt-bundle-windows\ndk-r10b\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-21 NDK_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a,x86 Error Code: 2 Output: make.exe: * No rule to make target C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni', needed byC:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni\main.o’. Stop.

你可能感兴趣的:(Android,Studio使用技巧分享)