1. 创建工程,本次示例采用了默认设置
下一步
下图是生成的Project目录结构
2. 在src/main/java下创建一个Class NdkJniUtils.java,在该类中添加本地Native函数
添加本地native函数:
public class NdkJniUtils {
public static native String getLanguageString();
}
3. 在Activity中初始化一个NdkJniUtils实例,我们调用一下getLanguageString函数来做后续验证,下面是MainActivity.java中的代码,其中的R.id.test请自行添加到activity_main文件中
package com.meituan.jnitest; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)this.findViewById(R.id.test); NdkJniUtils jni = new NdkJniUtils(); mTextView.setText(jni.getLanguageString()); } }
上面代码中有一个R.id.test的资源需要写入Layout文件中
<TextView
android:id="@+id/test"
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
4. 执行Make Project编译出*.class文件
5. 在src/main下创建jni文件夹,我们的C实现均放在该目录下,在刚刚创建的jni目录下新建一个shell文件autoheader.sh,用它来生成C头文件
autoheader.sh文件中的内容如下
#!/bin/sh
BUILD_DEBUG_PATH=../../../build/intermediates/classes/debug/
javah -o NdkJniUtils.h -jni -classpath ${BUILD_DEBUG_PATH} com.meituan.myapplication.NdkJniUtils
右键点击autoheader.sh脚本,运行该脚本会在同级目录下生成一个NdkJniUtils.h头文件。注意在此处是有可能报错的,一个原因是没有配置Ndk路径,解决方法是点击File->Project structure 然后选定NDK的路径,如下图
一个原因是grade.properties少一个配置下,添加下面这一句配置项,即可生成出NdkJniUtils.h头文件
android.useDeprecatedNdk=true
打开NdkJniUtils.h,我们看到一个或几个函数声明,这个函数名是从NdkJniUtils.java中的native函数转过来的
JNIEXPORT jstring JNICALL Java_com_meituan_myapplication_NdkJniUtils_getLanguageString(JNIEnv *, jclass);
6. 接下来我们需要在jni目录下创建一个NdkJniUtils.c文件,并在其中实现头文件中的函数。
#include "NdkJniUtils.h"
/*
* Class: io_github_yanbober_ndkapplication_NdkJniUtils
* Method: getCLanguageString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_meituan_jnitest_NdkJniUtils_getLanguageString
(JNIEnv *env, jobject obj){
return (*env)->NewStringUTF(env,"This just a test for Android Studio NDK JNI developer!");
}
至此需要编译的文件已经创建完成了
7. 接下来创建Android.mk将该C文件编译成so动态库文件,在jni文件下下创建Andriod.mk文件
Android.mk文件内容如下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
TARGET_PLATFORM := android-19
LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := NdkJniUtils.c
LOCAL_LDLIBS := -llog
NDK_APP_DST_DIR := ../jniLibs/$(TARGET_ARCH_ABI)
include $(BUILD_SHARED_LIBRARY)
在该目录下创建一个Application.mk的文件,添加下面的代码,作用是只生成下面指令集的动态库
APP_ABI := armeabi armeabi-v7a x86
8. 至此,我们还需要做一个ndk的配置,刚刚说到android studio可以自己生成Android.mk来编译,这项功能是默认开启的,我们需要禁用它,在module下的build.gradle中添加下面代码段
android{
sourceSets{
main{
jni.srcDirs = []
}
}
}
9. 至此我们就可以开始编译了,在android studio界面的最下面打开Terminal,然后进入app/src/main/jni中,执行ndk-build就会生成动态库文件
10. 最后,我们再NdiJniUtils.java中添加库调用代码
public class NdkJniUtils {
public static native String getLanguageString();
<span style="color:#ff0000;">static{
System.loadLibrary("jnitest");
}</span>
}
11. 现在我们可以编译了,并用虚拟机运行,效果如下