为了学习JNI,先做一个小demo来练手和入门。
再结合自己的实际情况和遇到的问题。(问题挺多的)
步骤3详解
步骤5详解
相关环境
- ubuntn14.04 (64位)
- Android studio2.1.3
- Jdk1.8
- Sdk(android-23)
- Ndk(r10,android-L)
JNI是什么?
Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。
JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。(抄的)
怎么调用?
这不是一句话能说的清楚的,得写个demo。不过用一句话来讲,就是架起java和c的一座桥梁。(我就叫它jc桥梁)
提前配置
首先把配置说一下,后面的步骤就可以一直走了。
ndk配置:
下载:
- http://www.androiddevtools.cn/(需翻墙,不过有最新的r12);
- http://blog.csdn.net/zhanghuoding/article/details/51345256(我在这下了r10的);
- ndk环境配置跟jdk类似,解压后(我解压到/opt),在/etc/profile文件下添加ndk路径到PATH(把根目录添进去就行);
- 需要注意的是:确保ndk目录下的platforms目录的版本跟sdk目录下的plarforms版本都对得上,这样编译不会出错;
- 之前我的ndk为r9,最高只有19的platform,而sdk最高到24,这里被坑了好多,编译老是出错。
- 后来改成sdk编译版本23的,因为下了一个ndk r10的版本刚好有Android-L的platform。
相关文件配置:
- gradle.properties文件末尾添加
android.useDeprecatedNdk=true
- local.properties文件末尾,查看时候有sdk.dir和ndk.dir出现;如果没有,通过file->project structure来设置。
步骤简介(后边有详解)
- 在Android Studio先建立一个project(project名:NdkJniDemo),带有默认的MainActivity
- 新建一个java类(JniUtils.java,跟MainActivity同路径),在里边声明一个native函数(也可以直接在MainActivity里声明):getStringFormC()
- 用javah -jni 命令编译这个类,会生成一个头文件。该头文件就是jc桥梁。期间一个小问题,参考着:http://bbs.csdn.net/topics/370236601
- 将上一步产生的头文件复制到src/main的jni文件夹(自己新建)下。
- 在jni文件夹下新建一个c文件去实现头文件的函数。(这里需要ndk的环境的配置了,后面再讲)
- 配置app目录下的build.gradle文件,指定编译输出的so文件的文件名:NdkJniDemo
- 接着,在新建的java类(JniUtils.java),加入代码:static { System.loadLibrary("NdkJniDemo");//之前在build.gradle里面设置的so 名字,必须一致}
- 最后在MainActivity里面就可以调用了:JniUtils.getStringFormC()。
步骤1详解 略
步骤2详解
- 代码如下:
public class JniUtils {
public static native String getStringFormC();
}
步骤3详解
- 进入下面的路径(可能版本不同略有差异),可以并找到JniUtils类:(NdkJniDemo为我的project名)
NdkJniDemo/app/build/intermediates/classes/debug/com/topwise/ndkjnidemo
- 在NdkJniDemo/app/build/intermediates/classes/debug目录下,打开终端,键入命令:
javah -jni com.topwise.ndkjnidemo.JniUtils
- 可能会遇到“无法访问android.app.Activity”的错误。
- 具体参考:http://bbs.csdn.net/topics/370236601 的11,12楼
个人认为可能是找不到你的Android.jar包,把你sdk目录的platform路径配到CLASSPATH去试试。
如果还是不行,试试这个命令:【命令模板 :javah
-classpath bin/classes -bootclasspath {sdk_home}/platforms/android-xx/android.jar -d jni com.xxx.xxxActivity】
【我的这个命令(去掉了
-classpath bin/classes)成功了:javah
-bootclasspath /opt/android-sdk-linux/platforms/android-23/android.jar -d jni com.topwise.ndkjnidemo.JniUtils】
- 如果顺利,就会得到一个头文件,在NdkJniDemo/app/build/intermediates/classes/debug目录下。我的头文件名为:com_topwise_ndkjnidemo_JniUtils.h
该头文件里面就会有一个函数:,跟JniUtils类里的函数对应。
步骤4详解
- 将上一步产生的头文件复制到src/main的jni文件夹(自己新建)下。
- c文件名随便取,我的c文件名为:jctest.c
- 然后去实现上述头文件里面的函数,添加的代码如下:
#include "com_topwise_ndkjnidemo_JniUtils.h"
/* Class: Java_com_wobiancao_ndkjnidemo_ndk_JniUtils
* Method: getStringFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_topwise_ndkjnidemo_JniUtils_getStringFormC
(JNIEnv *env, jobject obj)
{
return (*env)->NewStringUTF(env,"这里是来自c的string");
}
步骤6详解
- 注意是app目录下的build.gradle, 在android节点的defaultConfig{ ..... }里面加入:
ndk {
moduleName "NdkJniModule" //生成的so名字
abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。
}
步骤7详解
- 代码如下:
public class JniUtils {public static native String getStringFormC();
static {
System.loadLibrary("NdkJniModule");//之前在build.gradle里面设置的so名字,必须一致
}
}
- 这里要注意,static{.......}语句如果放在声明函数的语句前面,会报错。(后来就没有这个问题了。我猜测static语句放哪里都可以,没报错可能是因为ndk换了r10的)
步骤8详解
- MainActivity代码如下,(自己去布局文件添加一个id)
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.test);
textView.setText(JniUtils.getStringFormC());
}
}
- 结果成功,略过,真机懒得截图了哈。