1 JNI开发基本步骤
①编写.java类
②生成.class文件
③生成.h文件
③查阅.h文件
JNIEXPORT 和 JNICALL 是jni的宏
函数前的注释Signature: ()Ljava/lang/String;中的括号含义为:表示函数的参数为空,Ljava/lang/String表示函数的返回值是java的String对象
④编写.c/cpp文件
⑤创建 Android.mk文件(和.c文件同级目录)
⑥编译生成.so文件
2 实例:JniTest
①在module中,编写JniTest.java类,源码如下
package test.com.MyJni; /** * 类描述: * * Created by sf on 2016/5/19. */ public class JniTest { //使用JNI的关键字native //这个关键字决定我们那些方法能在我们的C文件中使用 //只须声明,不必实现 public native void yu(); public native double sum(double x,double y); }
新建一个java文件,可以看出第二行有个native,说明这个函数是java和其他语言(c/c++)协作时用的,并用其他语言实现。
②编译生成TestJNI.class文件
编译方法:
方法1:执行Build->Make Project
这一步骤执行一下,验证工程中并无其它错误,并对工程进行了编译,生成了.class文件.
方法2:运行cmd,定位到工程目录,用javac命令执行即可
③编译生成test_com_MyJni_JniTest.h文件
利用javah -jni -classpath . HelloWorld生成对应C/C++的HelloWorld.h文件,设置classpath的目的在于告诉java环境,在哪些目录下可以找到所要执行的java程序所需要的类或者包,此处不用声明.class文件名,在对应目录下编译器会自动找到
3.1 点击"View->Tool Windows->Terminal"
在Studio中进行终端命令行工具.执行如下命令生成c语言头文件。
3.2 进入当前文件夹 保证文件输出到可视位置
3.3 命令javah -d jni -classpath <SDK_android.jar>;<class所在文件夹路径> class包名+全名
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class test_com_MyJni_JniTest */ #ifndef _Included_test_com_MyJni_JniTest #define _Included_test_com_MyJni_JniTest #ifdef __cplusplus extern "C" { #endif /* * Class: test_com_MyJni_JniTest * Method: yu * Signature: ()V */ JNIEXPORT void JNICALL Java_test_com_MyJni_JniTest_yu (JNIEnv *, jobject); /* * Class: test_com_MyJni_JniTest * Method: sum * Signature: (DD)D */ JNIEXPORT jdouble JNICALL Java_test_com_MyJni_JniTest_sum (JNIEnv *, jobject, jdouble, jdouble); #ifdef __cplusplus } #endif #endif
native方法名称转换详解:
英语原文 | 我的理解… | 例子 |
the prefix | 转换后的native方法全部以Java_为前缀 | Java_kg_tom_MyJni_display |
a mangled fully-qualified class name | 接着以类的全名标识(包名+类名) | Java_kg_tom_MyJni_display |
| 以下划线为分隔符(“_”) | Java_kg_tom_MyJni_display |
mangled method name | 最后就是方法的名称 | Java_kg_tom_MyJni_display |
| 这个理解不能:直译的话就是对已经读取过的native方法,用双下划线(“__”)标注 | 没找到 |
④编写.c文件。
可以直接用记事本编写,当然如果你的Eclipse环境已集成c/c++开发环境了(方法见【Android學習專題】搭建Android NDK环境 ),可以直接在Eclipse中编写
代码如下所示,描述见注释:
//必须的头文件jni.h #include <jni.h> //导入我们需要实现的本地方法 #include "test_com_MyJni_JniTest.h" #include <stdio.h> /* * Class: test_com_MyJni_JniTest * Method: yu * Signature: ()V */ JNIEXPORT void JNICALL Java_test_com_MyJni_JniTest_yu (JNIEnv *env, jobject obj){ LOGE("log string from ndk."); printf("Hello World tom!!"); return; } /* * Class: test_com_MyJni_JniTest * Method: sum * Signature: (DD)D */ JNIEXPORT jdouble JNICALL Java_test_com_MyJni_JniTest_sum(JNIEnv *env, jobject obj, jdouble a, jdouble b) return a+b; }
⑤创建 Android.mk文件(和.c文件同级目录) 或配置grable
5.1 MK文件 用于表示so生成规则 [eclipse需手动配置,gradle自动生成]
我们实现好.c或者.cpp文件后,编写Android.mk文件,来生成动态库,一般使用NDK工具进行生成,首先是下载ndk包,然后设计全局变量,进入Android.mk文件夹中执行ndk编译命令即可。
ndk编译命令使用参考:
http://www.cnblogs.com/lipeil/archive/2012/08/27/2659378.html
http://blog.csdn.net/laczff21/article/details/7542236
我们进入.c,.h, Android.mk所在的文件下面,然后执行ndk编译命令:
ndk-build
信息如下:
abc@abc:~/workspace/NativeTest/jni$ndk-build
AndroidNDK: WARNING: APP_PLATFORM android-19 is larger thanandroid:minSdkVersion 8 in/home/archermind/workspace/NativeTest/AndroidManifest.xml
[armeabi]Compile++ thumb: NativeClassJni <=com_example_nativetest_NativeClass.cpp
[armeabi]StaticLibrary : libstdc++.a
[armeabi]SharedLibrary : libNativeClassJni.so
[armeabi]Install : libNativeClassJni.so =>libs/armeabi/libNativeClassJni.so
5.2 gradle
- Error:Execution failed for task ':app:compileDebugNdk'.
- > NDK not configured.
- Download the NDK from http://developer.android.com/tools/sdk/ndk/.Then add ndk.dir=path/to/ndk in local.properties.
- (On Windows, make sure you escape backslashes, e.g. C:\\ndk rather than C:\ndk)
5.2.1 配置NDK
5.2.2 修改build.gradle配置
在 local.properties 文件中设置ndk的路径,一般会自动生成android.useDeprecatedNdk=true
5.2.3 修改build.gradle配置
- sourceSets.main {
jni.srcDirs = [] - // 默认情况下,你需要把C/C++源代码放在[module]/src/main/jni/路径下
- //自定义源代码路径
- jni.srcDirs 'src/main/java/test/com/MyJni'
- //设置你的.so文件的实际路径,用于调用System.loadLibrary( libName );
//默认是再app/src/main/jniLibs中 - jniLibs.srcDir "src/main/libs"
}
-
- ndk {
- moduleName "JniTest" //生成的so名字
- ldLibs "log", "z", "m"
- abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。目前可有可无。
- }
5.2.4 这时,再执行"Build->Rebuild Project",就可以编译出so文件了。
- 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 by `C:\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.
⑥编译生成libtestjni.so文件
第五:使用
5.1 使用预编译好的.so动态库
默认路径如图所示的jniLibs
现在就可以在代码中直接载入此库了,如下:
public class JniTest { //加载so库 static { System.loadLibrary( "MyJniTest" ); } //使用JNI的关键字native //这个关键字决定我们那些方法能在我们的C文件中使用 //只须声明,不必实现 public native void yu(); public native double sum(double x,double y); }
import test.com.myjni.JniTest; public class MainActivity extends AppCompatActivity { TextView tv; JniTest jniUtils=new JniTest(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv=(TextView)findViewById(R.id.tv); double a=1; double b=2; double sum=jniUtils.sum(a,b); tv.setText(String.valueOf(sum)); } }
如果你不想把.so放在上面的默认路径,可以在buid.gradle中进行如下配置:
android {
// .. android settings ..
sourceSets.main {
jniLibs.srcDir 'src/main/myCppLibraries' // <-- 你的.so库的实际路径
}
}