Android studio下JNI(NDK)开发
2016-10-14 09:55
405 查看
jni开发也常叫做NDK开发,原理在于直接去调用C/C++代码,而不走虚拟机,这样在代码执行效率上提升是非常大的;那有的童鞋可能就会说:那咱们干嘛不都用C/C++来开发Android算了?首先用C/C++开发难度加大,Google选择java就是看中了它用来开发的简单快捷(这在移动应用这种需要快速开发迭代并需要跨平台上来说是很好的选择);其次C/C++语言跟硬件底层打交道太多,在移动设备花样百出的情况下简直是噩梦,通过虚拟机将底层与应用层隔离开或许是最优解了;
一不小心哔哔了这么多,总之Android开发还是用java语言来得好,它足够满足一般的应用,而在某些需要大量、复杂的计算或保护核心代码(C语言的反编译比java难很多)的时候就采用jni的方式来开发;
下面正式开始;
第一步:声明本地方法。通常为了结构清晰我们会新建一个类,里边声明我们要调用的C/C++方法,跟定义接口一样一样的(当然不是接口也不能是接口,也可以理解为用java语言写的头文件),所有方法用“native”修饰表明此方法为本地动态库的方法(就是C/C++去实现的方法),实现代码如下;
[java] view
plain copy
/**
* 这个类用来当作java与jni的接口
* Created by zchao on 2016/10/11.
*/
public class JniInterface{
//静态代码块会直接初始化。此代码意思就是将ndktestLib.so动态库加载到虚拟机中;
static {
System.loadLibrary("ndktestLib");
}
//声明的本地方法;也就是需要用c代码实现的方法,此处只作声明
public static native int add(int a, int b);
}
代码解释:如上;
第二步:生成C/C++需要的*.h(头文件);
这一步相当于把我们创建的JniInterface类转化为C/C++语言里的头文件(C语言里规范的头文件通常也只有方法声明);控制台cd到工程里边JniInterface.java所在的文件夹,直接执行javac JniInterface.java,编译成功后里边会有一个JniInterface.class文件(或则直接执行Build->MakeProject;然后查看工程的F:\NDKTest\app\build\intermediates\classes\debug com.example.zchao.ndktest.JniInterface.class,然后拷贝到JniInterface.java所在位置);然后在控制台cd到工程的app\src\main\java目录下,执行javah
com.example.zchao.ndktest.JniInterface;如果正确直接生成一个com_example_zchao_ndktest_JniInterface.h(这就是C语言的头文件了)的文件。然后在main目录下新建一个jni文件夹将com_example_zchao_ndktest_JniInterface.h拷贝过去,这步就算大功告成了;当然网上还有很多获得C语言头文件的方法,大家自行百度。当然如果觉得过程太繁琐,可以直接手写一个头文件也行(当然不是随意写的,要注意很多地方是包名+类名);
最后头文件里边代码如下:
[java] view
plain copy
/* DONOT EDIT THIS FILE - it is machine generated */
#include<jni.h>
/*Header for class com_example_zchao_ndktest_JniInterface */
#ifndef_Included_com_example_zchao_ndktest_JniInterface
#define_Included_com_example_zchao_ndktest_JniInterface
#ifdef__cplusplus
extern"C" {
#endif
/*
* Class: com_example_zchao_ndktest_JniInterface
* Method: add
* Signature: (II)I
*/
JNIEXPORTjint JNICALL Java_com_example_zchao_ndktest_JniInterface_add
(JNIEnv *, jclass, jint, jint);
#ifdef__cplusplus
}
#endif
#endif
代码解释: 没啥好解释的,里边就是方法的声明(可以直接拷贝过去修改修改就不用第二步这么多鸟事儿了);
第三步:实现上一步.h头文件中的方法
C语言中的.h文件只是声明(虽然一般C编译器并不care后缀,也就是说其实可以在.h文件里直接实现方法,但是不建议这么做),在jni文件夹下新建一个C/C++ source file,名字随意;里边代码如下:
[java] view
plain copy
#include"com_example_zchao_ndktest_JniInterface.h"
/*
* Class: com_example_zchao_ndktest_JniInterface
* Method: add
* Signature: (II)I
*/
JNIEXPORTjint JNICALL Java_com_example_zchao_ndktest_JniInterface_add
(JNIEnv *env, jclass jobj, jint a, jint b){
return (a+b);
};
代码解释:第一行将生成的.h文件include进来(原因在于C语言需要先声明再使用,包含.h就是声明一下而已);然后第六行就是方法具体实现了,这儿就是一个简单的求和公式;
第四步:生成动态库(.so文件)
有了c语言的源码,需要打包成.so库文件,这样调用时就可以直接调.so库中的代码了;
在app的build.gradle中添加如下代码(注意位置不要错);
代码解释:
[java] view
plain copy
ndk{
moduleName"ndktestLib"
abiFilters"armeabi","armeabi-v7a","x86"
}
moduleName中ndktestLib也就是最终我们生成的.so库的名字;也就是JniInterface.java中执行
System.loadLibrary("ndktestLib")这儿的动态库;
动态库生成了当然需要一起打包进apk中,所以上图第二个红框处就是gradle提供的自动将jni文件一起打包的代码(注意:gradle版本低了有可能不支持此打包方法),
完成以上步骤后就可以调用了,代码如下;
[java] view
plain copy
package com.example.zchao.ndktest;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView mText;
private JniInterface jniAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById(R.id.text);
jniAdapter = new JniInterface();
mText.setText( "结果是:" + jniAdapter.add(5, 4));
}
}
然后直接发布到手机或模拟器,正常就会显示“结果是:9”;最后看下工程文件结构如下图;
最后补充一些常见的问题;
Q : 'javac' 不是内部或外部命令,也不是可运行的程序或批处理文件。
A:命令行出现这个,第一种可能是没装jdk;第二种可能是没配置好环境变量;
解决:第一种安装jdk;第二种配置环境变量,变量名“JAVA_HOME”变量值“C:\ProgramFiles\Java\jdk1.7.0_71“(值为自己JDK安装位置);配置"CLASS_PATH",值为“.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;”(注意最前方的".;");
在Path里配置“%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;”
Q:生成不了.h文件
A:在第二步里边执行javac的位置是在JniInterface.java所在文件夹中;而javah是要在工
程的src/main/java文件下的;实在不行了将我的代码拷贝过去,替换类名与方法名为自己的(注意要用包名+类名);
Q:始终无法生存.so文件;运行就报“…couldn't find"libndktestLib.so"这样的错误;
A:出现这样的错误就是没生成.so文件或则没打包进APK包里边;先看app的build.gradle文件里是不是像第四步里那样配置的,注意ndk{…}是在defauleconfig{}内部;sourceSets{}是在android{}内部;还有个就是代码里jni文件夹是在main文件夹下(跟java文件夹平级的);
另外一定要注意修改了JniInterface.java中的代码后同时需要去修改.h和.c文件里边的代码,或重新走一遍流程。
一不小心哔哔了这么多,总之Android开发还是用java语言来得好,它足够满足一般的应用,而在某些需要大量、复杂的计算或保护核心代码(C语言的反编译比java难很多)的时候就采用jni的方式来开发;
下面正式开始;
第一步:声明本地方法。通常为了结构清晰我们会新建一个类,里边声明我们要调用的C/C++方法,跟定义接口一样一样的(当然不是接口也不能是接口,也可以理解为用java语言写的头文件),所有方法用“native”修饰表明此方法为本地动态库的方法(就是C/C++去实现的方法),实现代码如下;
[java] view
plain copy
/**
* 这个类用来当作java与jni的接口
* Created by zchao on 2016/10/11.
*/
public class JniInterface{
//静态代码块会直接初始化。此代码意思就是将ndktestLib.so动态库加载到虚拟机中;
static {
System.loadLibrary("ndktestLib");
}
//声明的本地方法;也就是需要用c代码实现的方法,此处只作声明
public static native int add(int a, int b);
}
代码解释:如上;
第二步:生成C/C++需要的*.h(头文件);
这一步相当于把我们创建的JniInterface类转化为C/C++语言里的头文件(C语言里规范的头文件通常也只有方法声明);控制台cd到工程里边JniInterface.java所在的文件夹,直接执行javac JniInterface.java,编译成功后里边会有一个JniInterface.class文件(或则直接执行Build->MakeProject;然后查看工程的F:\NDKTest\app\build\intermediates\classes\debug com.example.zchao.ndktest.JniInterface.class,然后拷贝到JniInterface.java所在位置);然后在控制台cd到工程的app\src\main\java目录下,执行javah
com.example.zchao.ndktest.JniInterface;如果正确直接生成一个com_example_zchao_ndktest_JniInterface.h(这就是C语言的头文件了)的文件。然后在main目录下新建一个jni文件夹将com_example_zchao_ndktest_JniInterface.h拷贝过去,这步就算大功告成了;当然网上还有很多获得C语言头文件的方法,大家自行百度。当然如果觉得过程太繁琐,可以直接手写一个头文件也行(当然不是随意写的,要注意很多地方是包名+类名);
最后头文件里边代码如下:
[java] view
plain copy
/* DONOT EDIT THIS FILE - it is machine generated */
#include<jni.h>
/*Header for class com_example_zchao_ndktest_JniInterface */
#ifndef_Included_com_example_zchao_ndktest_JniInterface
#define_Included_com_example_zchao_ndktest_JniInterface
#ifdef__cplusplus
extern"C" {
#endif
/*
* Class: com_example_zchao_ndktest_JniInterface
* Method: add
* Signature: (II)I
*/
JNIEXPORTjint JNICALL Java_com_example_zchao_ndktest_JniInterface_add
(JNIEnv *, jclass, jint, jint);
#ifdef__cplusplus
}
#endif
#endif
代码解释: 没啥好解释的,里边就是方法的声明(可以直接拷贝过去修改修改就不用第二步这么多鸟事儿了);
第三步:实现上一步.h头文件中的方法
C语言中的.h文件只是声明(虽然一般C编译器并不care后缀,也就是说其实可以在.h文件里直接实现方法,但是不建议这么做),在jni文件夹下新建一个C/C++ source file,名字随意;里边代码如下:
[java] view
plain copy
#include"com_example_zchao_ndktest_JniInterface.h"
/*
* Class: com_example_zchao_ndktest_JniInterface
* Method: add
* Signature: (II)I
*/
JNIEXPORTjint JNICALL Java_com_example_zchao_ndktest_JniInterface_add
(JNIEnv *env, jclass jobj, jint a, jint b){
return (a+b);
};
代码解释:第一行将生成的.h文件include进来(原因在于C语言需要先声明再使用,包含.h就是声明一下而已);然后第六行就是方法具体实现了,这儿就是一个简单的求和公式;
第四步:生成动态库(.so文件)
有了c语言的源码,需要打包成.so库文件,这样调用时就可以直接调.so库中的代码了;
在app的build.gradle中添加如下代码(注意位置不要错);
代码解释:
[java] view
plain copy
ndk{
moduleName"ndktestLib"
abiFilters"armeabi","armeabi-v7a","x86"
}
moduleName中ndktestLib也就是最终我们生成的.so库的名字;也就是JniInterface.java中执行
System.loadLibrary("ndktestLib")这儿的动态库;
动态库生成了当然需要一起打包进apk中,所以上图第二个红框处就是gradle提供的自动将jni文件一起打包的代码(注意:gradle版本低了有可能不支持此打包方法),
完成以上步骤后就可以调用了,代码如下;
[java] view
plain copy
package com.example.zchao.ndktest;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
private TextView mText;
private JniInterface jniAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById(R.id.text);
jniAdapter = new JniInterface();
mText.setText( "结果是:" + jniAdapter.add(5, 4));
}
}
然后直接发布到手机或模拟器,正常就会显示“结果是:9”;最后看下工程文件结构如下图;
最后补充一些常见的问题;
Q : 'javac' 不是内部或外部命令,也不是可运行的程序或批处理文件。
A:命令行出现这个,第一种可能是没装jdk;第二种可能是没配置好环境变量;
解决:第一种安装jdk;第二种配置环境变量,变量名“JAVA_HOME”变量值“C:\ProgramFiles\Java\jdk1.7.0_71“(值为自己JDK安装位置);配置"CLASS_PATH",值为“.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;”(注意最前方的".;");
在Path里配置“%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;”
Q:生成不了.h文件
A:在第二步里边执行javac的位置是在JniInterface.java所在文件夹中;而javah是要在工
程的src/main/java文件下的;实在不行了将我的代码拷贝过去,替换类名与方法名为自己的(注意要用包名+类名);
Q:始终无法生存.so文件;运行就报“…couldn't find"libndktestLib.so"这样的错误;
A:出现这样的错误就是没生成.so文件或则没打包进APK包里边;先看app的build.gradle文件里是不是像第四步里那样配置的,注意ndk{…}是在defauleconfig{}内部;sourceSets{}是在android{}内部;还有个就是代码里jni文件夹是在main文件夹下(跟java文件夹平级的);
另外一定要注意修改了JniInterface.java中的代码后同时需要去修改.h和.c文件里边的代码,或重新走一遍流程。
相关文章推荐
- Android Studio下的NDK-JNI开发总结
- 【android 开 发 】 - Android studio 下 NDK Jni 开发 简单例子
- Android Studio中JNI NDK开发(三)
- Android Studio 中 NDK 开发之JNI开发规范
- Android Studio 2.3.3 图解配置NDK开发环境以及Hello Word To jni~
- Android Studio上NDK/JNI开发环境问题
- Android studio下JNI(NDK)开发
- Android Studio开发NDK JNI
- android studio2.2初探ndk jni开发
- Android Studio中NDK开发傻瓜教程(JNI)
- Android Studio - Gradle项目 - NDK开发 - JNI编译中引用本地so - 记录
- NDK开发——Android Studio实现JNI
- Android studio环境下的 NDK(jni)开发
- android studio 配置NDK环境进行JNI开发
- Android-使用Android studio 进行NDK-JNI开发实例(一)
- 【android 开 发 】 - Android studio 下 NDK Jni 开发 简单例子
- Android Studio使用JNI和NDK进行开发
- android studio 2.2 ndk jni 开发 调试 hello world
- Android NDK开发之旅(2):一篇文章搞定Android Studio中使用CMake进行NDK/JNI开发
- Android NDK开发之旅(2):Android Studio中使用CMake进行NDK/JNI开发(初级)