Jni实战
以下所有代码,我都提交到我的github上面了
生成CPP的头文件
首先让我们从最简单的开始
- 新建
TestJni.java文件
package com.tele.jni; //放置在这个目录底下
public class TestJni{
public native static long _get_UID();
}
-
然后在根目录执行
javac com/tele/jni/TestJni.java会生成com/tele/jni/TestJni.class文件 -
然后再执行
javah -jni -classpath . com.tele.jni.TestJni,就会在执行目录生成com_tele_jni_TestJni.h注意
- 这里不能有 .class 后缀,
- 必须用
.来拼接到最终的class文件,要不然会报下面的错误 - 必须在根目录下执行
Exception in thread "main" java.lang.IllegalArgumentException: Not a valid class name: TestJni.class
at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:129)
at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:107)
at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:64)
at com.sun.tools.javah.JavahTask.run(JavahTask.java:503)
at com.sun.tools.javah.JavahTask.run(JavahTask.java:329)
at com.sun.tools.javah.Main.main(Main.java:46)
//or
错误: 找不到 'TestJni' 的类文件。
- 移动文件
com_tele_jni_TestJni.h到com/tele/jni/里面,新建 TestJni.cpp
#include "com_tele_jni_TestJni.h"
JNIEXPORT jlong JNICALL Java_com_tele_jni_TestJni__1get_1UID(JNIEnv *, jclass){
return 100;
}
至此准备工作做完了,接下来就是编译成Android可以运行的so
这里提供两种方式生成so,
第一种: 通过android.mk 【以下采用这种方式的生成】
这里需要注意,android.mk 在不改变默认行为以前,需要放置在jni目录下,然后再jni同级目录执行ndk-build 就可以。在这里就是在目录 com/tele 这个目录底下执行,会生成 libs obj 两个文件夹
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := TestJni.cpp
LOCAL_CFLAGS += -fPIC
LOCAL_CPP_INCLUDES := .
LOCAL_MODULE:= testJni
include $(BUILD_SHARED_LIBRARY)
第二种: 通过cmake
如果看不懂cmake的,可以查看我之前的文章。
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project (testJni)
add_library(${PROJECT_NAME} SHARED testJni.cpp)
这里写完,可以需要在工程项目d的 build.gradle里面添加 如下代码
externalNativeBuild {
cmake {
path "src\\main\\java\\com\\tele\\jni\\CMakeLists.txt"
}
}
新建Android项目
-
使用Androidstudio 新建一个EmptyActivity的项目,
-
将我们刚才生成的文件拷贝进去,【当然以后你可以去掉某些文件】
如果你按照笔者说的步骤,到这里,你应该会是这样的目录层级【当然我去掉了AS自动生成的一些文件】
>tree /f
com
├─tele
│ ├─jni
│ │ Android.mk
│ │ com_tele_jni_TestJni.h
│ │ TestJni.class
│ │ TestJni.cpp
│ │ TestJni.java
│ │
│ ├─libs
│ │ └─armeabi
│ │ libtestJni.so
│ │
│ └─obj
│ └─local
│ └─armeabi
│ │ libstdc++.a
│ │ libtestJni.so
│ │
│ └─objs
│ └─testJni
│ TestJni.o
│ TestJni.o.d
│
└─txznet
└─testjniapp
MainActivity.java
-
需要把自动生成的
/libs/armeabi/libtestJni.so整个拷贝到工程项目里面的libs所在的目录 -
需要补充代码:
你要装载so库,才可以执行相应的方法:java中采用 System.loadLibrary("testJni");的方法加载,其实内部就是dlopen
//TestJni.java
public class TestJni{
static {
System.loadLibrary("testJni");
}
public native static long _get_UID();
}
- 我们在界面随便拖动一个按钮,设置点击事件,运行。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void clicktestjni(View view) {
//点击事件,打印出从cpp中获取的值
Toast.makeText(this, "hello :"+TestJni._get_UID(), Toast.LENGTH_SHORT).show();
}
}
运行:报错找不到库。
Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.txznet.testjniapp-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libtestJni.so"
- 因为我们还没有在 工程的
build.gradle中增加库查找的路径
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
- 连接夜神模拟器。我这里使用了夜神模拟器,你也可以使用别的模拟器,都是可以的。
adb connect 127.0.0.1:62001
就可以运行了。
- 运行,你就会发现成功执行了,附图

说在最后
如果是cmake 的话 需要在gradle中增加如下代码:
android {
compileSdkVersion 28
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
如果是ndk编译的话,则需要在gradle中增加如下代码
android {
defaultConfig {
ndk {
//指定abi的版本,可以不加
abiFilters "armeabi-v7a"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
如果你正在编译我的项目的话,请注意查看 build.gradle 里面是使用cmake 还是使用ndk.
感谢您关注我的公众号:
