环境:
linux:Deepin15.4rc
java:openJDK1.8
至添加一次即可,即下面语句使用一次之后以后就不用再使用了
echo 'export PATH=~/Desktop/Android/android-sdk-linux/ndk-bundle/:$PATH' >> ~/.bashrc
echo ‘export PATH=你的具体ndk路径/ndk-bundle/:$PATH’ >> ~/.bashrc
让环境变量生效的方法:
完成以上操作你就可以随处使用NDK-build这个命令了
建议单独写一个native的java类,理论上MainActivity上写也是可以的,但是每次改动,你都要重新生成。
新建一个native包,里面放native的java类
package com.example.myapplication.natives;
/**
* Created by hui on 17-4-4.
*/
public class Test {
static {
System.loadLibrary("test");
}
public native String get();
public native void set(String str);
}
现在你肯定看到get和set方法红色吧。因为还没生成对应的.h头文件
这步必须做,.class文件,在后面生成.h头文件要用到
对着那个Test.java,右键show in file manager,自动打开文件管理器
javac Test.java
这个头文件是自动生成的,你可以看出上面声明包package com.example.myapplication.natives;这句与那个.h文件的命名规则了么?
在文件管理器中,我们回退到这个java目录下
看到这个包声明的打头目录,就在这里打开命令行
javah com.example.myapplication.natives.Test
格式:javah 包声明.Test
因为javah要从包声明最顶层寻找Test.class,所以要在这个相对路径使用命令
android.useDeprecatedNdk=true这个很重要!!!
此时我们写的Test.java没有红色提示了。
左边还多了两个红绿箭头。恩,Android Studio找到了.h头文件了。
重点是那个.h的命名方式
test.cpp和test.c的实现很类似,但是它们对env的操作方式有所不同,因此用C++和C来实现同一个JNI方法,它们的区别主要集中在对env的操作上,其他都是类似的,如下所示。
C++: env->NewStringUTF(“Hello from JNI !”);
C:(*env)->NewStringUTF(env,”Hello from JNI !”);
摘自Android开发艺术探索
这里我们编写c语言
#include "com_example_myapplication_natives_Test.h"
#include
JNIEXPORT jstring JNICALL Java_com_example_myapplication_natives_Test_get
(JNIEnv *env, jobject thiz){
printf("start c get ");
return (*env)->NewStringUTF(env,"hello from jni");
}
JNIEXPORT void JNICALL Java_com_example_myapplication_natives_Test_set
(JNIEnv * env, jobject thiz, jstring string){
printf("start c set");
char * str = (char *)(*env)->GetStringUTFChars(env , string , NULL);
printf("%s\n",str);
(*env)->ReleaseStringUTFChars(env , string ,str);
}
- JNIEnv *: 表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法
- jobject: 表示java对象中的this
- JNIEXPORT和JNICALL: 它们是JNI中所定义的宏,可以在jni.h这个头文件中找到
摘自Android开发艺术探索
编译前的准备:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := test.c
include $(BUILD_SHARED_LIBRARY)
其中
LOCAL_MODULE表示模块名称
LOCAL_SRC_FILES表示需要参与编译的源文件
除了这两个,其他照搬即可
APP_ABI := armeabi
用作配置要编译的CPU架构平台的类型
PS:如果看不懂我括号写的意思,直接跳过这个3.1就好了,3.2也是生成so的方法
切换到jni文件夹中,命令行使用命令
小插曲:
hui@hui-PC:~/AndroidStudioProjects/MyApplication/myapplication2/src/main/jni$ gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -fPIC test.c -o libtest.so
In file included from com_example_myapplication_natives_Test.h:2:0,
from test.c:1:
/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/jni.h:45:20: fatal error: jni_md.h: 没有那个文件或目录
#include "jni_md.h"
^
compilation terminated.
找不到jni_md.h,文件管理器,一找,jni_md.h在下一级目录的linux文件夹中,所以正确命令是
gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux/ -fPIC test.c -o libtest.so
在jni的上一级目录,使用命令
hui@hui-PC:~/AndroidStudioProjects/MyApplication/myapplication2/src/main$ ndk-build
[armeabi] Compile thumb : test <= test.c
[armeabi] SharedLibrary : libtest.so
[armeabi] Install : libtest.so => libs/armeabi/libtest.so
自动生成了,libs目录
新建一个jniLibs,将libs下的文件夹全部复制到jniLibs
并在build.gradle(记得这是module的,不是project的)下添加如下
android {
...
sourceSets{
main{
//jniLibs.srcDirs = ['src/main/jniLibs']
jni.srcDirs = []
}
}
}
新版的gradle语法变成sourceSets{main{ }}了,不是以前的sourceSets.main{ }了。
其实网上都有,感觉自己写得复杂了。
重点有三个吧。
android {
...
sourceSets{
main{
//jniLibs.srcDirs = ['src/main/jniLibs']
jni.srcDirs = []
}
}
}
不这么做会提示找不到so,奇怪了,so在jniLibs下面,却要写jni.srcDirs
PS:如果出现奇怪的错误,那么clear一下project
最后简单调用即可,这里简单的在onreate里面调用toast就算了
Toast.makeText(MainActivity.this , "" + new Test().get(), Toast.LENGTH_SHORT).show();