JNI就是java调用本地方法的技术,最简单的来说,java运行一个程序需要要和不同的系统平台打交道,在windows里就是和windows平台底层打交道,mac就是要和mac打交道,jvm就是通过大量的jni技术使得java能够在不同平台上运行。而使用了这技术的一个标志就是native,如果一个类里的一个方法被native修饰,那就说明这个方法是jni来实现的,他是通过本地系统api里的方法来实现的。当然这个本地方法可能是C或者C++,当然也可能是别的语言。jni是java跨平台的基础,jvm通过在不同系统上调用不同的本地方法使得jvm可以在不同平台间移植。当前你自己也可以用jni来写一些程序,这种基本上是你以前使用了其他语言完成了一些功能,但是你有要用java来重复这些功能的时候,就可以使用jni来完成了。不过带来的问题就是,如果你的那个本地方法是依托于本地操作系统的话,那就意味着你的java程序也只能在这一个系统上运行了。所以jni就使得java很容易限定在了一个系统平台上,而jdk的作用在于他提供一个规范,这个规范就是包含了很多native方法,这些方法都是要本地操作系统来实现的,而实现了这些本地方法的操作系统就可以移植java平台了。
Java属于解释型语言, 跨平台, 编译运行效率相对较低,高级语言
C语言属于编译型语言, 本地语言, 无法跨平台,性能卓越,低级语言
上面的高级和低级没有优差之分,只是说明了语言和底层机器的距离
市场上面,大多高级工程师,都要求会使用
效率优先的场合, 需要用到C语言
C语言有一些优秀的类库, 在做程序的时候需要用到(WebKit, FFMPEG)**
操作硬件一般使用C语言, 需要使用Java调用C来控制硬件
安全性优先, 保密性优先的场合, 需要用到C语言, 因为C语言是几乎无法编译的
首先要懂的Java代码
然后还要懂得C语言,C++语言
熟悉JNI接口规范,要懂得查看
双击Path进行编辑,我的NDK Build是下载到了D:\Android_Studio\SDK\ndk-bundle文件夹下面,所以我在Path的后面
添加 ;D:\Android_Studio\SDK\ndk-bundle
package com.shi.androidstudy.ndk;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,sayHello(),Toast.LENGTH_LONG).show();
}
});
}
}).start();
}
public native String sayHello();
static {
// 导入动态库
System.loadLibrary("hello");
}
}
下面是具体的C语言代码
#include
/*
* 对应的native方法, 需要一个string的返回值
* jstring 对应Java中的String
* Java_com_shi_androidstudy_myjni_MainActivity_sayHello 方法名
* Java_包名(.改成_)_类名_方法名
*
* JNIEnv* Java当前虚拟机运行环境
* jobject 当前调用这个函数的Java对象
*/
jstring Java_com_shi_androidstudy_ndk_MainActivity_sayHello(JNIEnv* env, jobject obj) {
char* pc = "Hello from c";
// jstring返回值 (*NewStringUTF)(JNIEnv* java虚拟机运行环境, const char* C语言中的字符串);
jstring str = (**env).NewStringUTF(env, pc);
return str;
}
这个文件主要是为了告诉ndk-build我们想要生成的库文件名和需要编译的C或者C++文件名,在这里我想要编辑的C文件名
叫做hello.c,我想要使用的库文件名叫做hello
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)
这个文件主要是为了告诉ndk-build我们想要生成适用于那些CPU指令集的库文件,=all就是编译生成所有CPU指令集的库文件
APP_ABI := all
我们需要修改build.gradle文件,否次运行程序,会提示findLibrary returned null之类的错误信息
android {
sourceSets {
main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
}
加载动态库出错
java.lang.UnsatisfiedLinkError: Couldn’t load hello123 from loader dalvik.system.PathClassLoader
[dexPath=/data/app/apk名称,libraryPath=/data/app-lib/apk名称]: findLibrary returned null
解决之道:
1、写对库文件名字
2、加载时候的名字: 库文件去掉.so, 去掉前面的lib
3、按照文章的第五步修改gradle文件
C函数名写错
java.lang.UnsatisfiedLinkError: Native method not found:
com.shi.androidstudy.myjni.MainActivity.sayHello:()Ljava/lang/String;
解决之道:
函数名Java_包名(.改成)类名_native方法名
在不支持的平台上运行(无法安装的问题)
Installation error: INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
解决之道: