最近因公司业务需要,将一部分逻辑代码从java移入JNI中。本着现学现用的精神,终于在deadline前完成交付。现记下此文作为我学习JNI过程中的点滴。
废话不多说,直接开干
搭建环境
我用的开发工具是MAC,AS。关于安装NDK网上有很多方法,我用的是最懒的方法,如图
因为我这已经下载好了NDK,所以没有显示。
未下载NDK时会提示,是否下载,我是直接点击 load ndk让AS帮我下载好的。
友情提示:此法下载需要科学上网
编写java文件
我新建了两个java文件,JNIFragment用于调用底层方法并显示处理结果,JNICalc 声明了一个简单的计算方法。
JNIFragment.java
public class JNIFragment extends BaseFragment {
@Override
public String getTAG() {
return JNIFragment.class.getSimpleName();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
LinearLayout linearLayout = new LinearLayout(getActivity());
linearLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
Button button = new Button(getActivity());
button.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
button.setText("32+44=?");
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogDetails.i(JNICalc.calc(32,44));
showToastShort(JNICalc.calc(32,44)+"");
}
});
linearLayout.addView(button);
return linearLayout;
}
}
JNICalc.java
public class JNICalc {
public static native int calc(int a,int b);
static {
System.loadLibrary("jniDemo"); // 标记1
}
}
生成.h文件
依次点击 Build->clean project->rebuild project运行结束后, 找到class文件所在路径app/build/intermediates/classes/tt/blue/com/bruce/demo/studydata/fragments/jnidemo/JNICalc.class
打开终端输入命令
javah -jni 包名JNICalc
如果包名不正确就会报错
Exception in thread “main” java.lang.IllegalArgumentException: Not a valid class name
或者
error occurred during initialization of VM java.lang.Error: Properties init: Could not determine current working directory. at java.lang.System.initProperties(Native Method) at java.lang.System.initializeSystemClass(System.java:1163)
或者
javah -jni com.bruce.demo.studydata.fragments.jnidemo.JNICalc 错误: 找不到 com.bruce.demo.studydata.fragments.jnidemo.JNICalc 的类文件。
对于我的demo中的正确的输入方法如下
进入JNICalc.class文件所在路径的根目录
cd app/build/intermediates/classes/tt/blue/
编译生成.h文件,文件路径app/build/intermediates/classes/tt/blue/
javah -jni com.bruce.demo.studydata.fragments.jnidemo.JNICalc
编写native文件
在main目录下新建jni文件夹,并将上面生成的.h文件copy到这个文件夹下,新建 cppcalc.cpp文件
#include <com_bruce_demo_studydata_fragments_jnidemo_JNICalc.h>
JNIEXPORT jint JNICALL Java_com_bruce_demo_studydata_fragments_jnidemo_JNICalc_calc
(JNIEnv *, jclass, jint a, jint b){
return a+b;
}
编写.mk
编写Android.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jniDemo
LOCAL_SRC_FILES := cppcalc.cpp
include $(BUILD_SHARED_LIBRARY)
这里需要注意的地方有两处:
LOCAL_MODULE 填写的jniDemo必须与 标记1相同
LOCAL_SRC_FILES 填写的 cppcalc.cpp就是上面的cpp文件名称必须 一 一对应
生成.so
输入 ndk-build 生成.so
这里也要注意
可能输入完这条命令会出现以下错误
>
1.
Android NDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it. /Users/XXX/Library/Android/sdk/ndk-bundle/build/core/build-local.mk:151: Android NDK: Aborting . Stop.
2.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: app/src/jni/Android.mk/jni/Android.mk
/Users/XXX/Library/Android/sdk/ndk-bundle/build/core/add-application.mk:198: * Android NDK: Aborting… . Stop.
解决办法是进入到当前工程所在目录,build时指明源文件所在路径的父路径
cd asstudydemo
ndk-build NDK_PROJECT_PATH=app/src/main/
这样就生成了若干个.so包
在工程上使用.so
生成的.so包考入main包下的jniLibs中或者lib中都可以,打包安装,测试。 此过程中可能会出现
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set “Android.useDeprecatedNdk=true” in gradle.properties to continue using the current NDK integration.
的错误,则需要修改 gradle.properties文件增加 android.useDeprecatedNdk=true 即可
demo
参考资料:
NDK integration is deprecated解决方法
https://developer.android.com/ndk/guides/android_mk.html