社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
这里假设读者是一位有一定经验的android开发者,对Android开发环境的搭建有一定的经验,为此简单的说一下IDE的配置。在AndroidStudio开发工具的基础上,下载 NDK 和工具:
可以使用 SDK 管理器安装这些组件,在打开的项目中,从主菜单选择 Tools > SDK Manager > Android SDK > SDK Tools,选中相应的工具,点击apply即可下载。
因为使用 NDK 构建代码主要有二种方法:ndk-build和CMake。因此本文分别就这两种方式展开下文。
在现有版本的androidStudio上,此种方式较为简单,直接New > New Project,在Phone and Tablet选择 Native C++ 项目,如图
然后一路Next,到C++ Standard选择 Toolchain Default,再点击finish,
至此,一个采用CMake方式构建的Jni项目就已经建好了,可以直接run到手机,配置文件都是默认的。
java代码如下
package com.hai.cmake;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String callJni();
/**
* 被JNI调用的方法
*/
public String stringFromJava() {
return "JNIGETSTRINGFROMJAVA";
}
相应的Jni实现,就一个native-lib.cpp文件,没有.h文件,采用Cmake方式就是如此简单
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_hai_cmake_MainActivity_stringFromJNI(//注意方法名和MainActivity的包名的关系,JNI方法名要求如此
JNIEnv *env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
/**
* jni调用java方法
* @param env
* @param thiz
* @return
*/
jstring stringFromJava(JNIEnv *env, jobject /* this */thiz) {
jclass cls = env->GetObjectClass(thiz);
jmethodID methodId = env->GetMethodID(cls, "stringFromJava", "()Ljava/lang/String;");
jobject ob = env->AllocObject(cls);
jstring str = static_cast<jstring>(env->CallObjectMethod(ob, methodId));
return str;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_hai_cmake_MainActivity_callJni(JNIEnv *env, jobject instance) {
return stringFromJava(env, instance);
}
app module的build.gradle部分配置
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {//此处配置可能会因gradle版本不同而有差异
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
CMakeLists.txt的内容如下
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSI ON 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp#此处可以写入多个cpp文件,以逗号分隔)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib})
到这,CMake方式构建的Jni项目就已经讲解完毕了。写的比较简单,因为这种方式的项目是android Studio里面默认自带的Jni Template Project。
为什么要写这种方式呢?个人认为,这种方式从最基本的声明JNI方法、生成.h头文件、编写cpp方法实现,到最后编写Android.mk文件,一路下来能让JNI初学者由浅入深一步一步的了解JNI项目的开发过程,从而掌握JNI项目开发。
下面也是从New > New Project开始,选择新建一个普通的Android项目即可。新建项目的流程简单,直接滤过。项目中就一个java文件:MainActivity.java,在其中写一个native方法,
此时native方法为红色,不用管。
下一步利用JDK自带的javah工具生成.h头文件,比如我的java源文件目录为E:workspace_AsNdkBuildappsrcmainjava,因为javah参数必须要包含包路径,cmd模式下进入E:workspace_AsNdkBuildappsrcmainjava,然后执行命令:
E:workspace_AsNdkBuildappsrcmainjava>
E:workspace_AsNdkBuildappsrcmainjava>javah -jni com.hai.ndkbuild.MainActivity
就会在当前目录下生成.h头文件,比如我的是:com_hai_ndkbuild_MainActivity.h,内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_hai_ndkbuild_MainActivity */
#ifndef _Included_com_hai_ndkbuild_MainActivity
#define _Included_com_hai_ndkbuild_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_hai_ndkbuild_MainActivity
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_hai_ndkbuild_MainActivity_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
如果生成.h头文件失败,注意检查包名、文件路径是否正确,或是clean项目然后build一下试试。
这是根据java文件生成.h头文件的一种方法,还有另一种方法是根据.class文件生成.h头文件。
比如我写了一个JniUtil.java,
进入到java的编译产物目录,也就是.class目录,比如我的:E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses,然后:
E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses>
E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses>javah -jni com.hai.ndkbuild.JniUtil
这样也是可以生成com_hai_ndkbuild_JniUtil.h文件的,且内容都是一样。
下一步就是新建一个文件夹用来存放.h头文件和cpp文件,以及nkd构建文件。
项目切换到project模式,右击main > New> Folder > Jni Folder,之后会弹出对话框让选择文件位置,IDE默认给的路径是 src/main/jni/,因为JNI文件夹一般是cpp,所以这里我们也改为cpp,
点击finish新建cpp文件夹,然后把上一步生成的com_hai_ndkbuild_MainActivity.h移动到cpp目录,复制com_hai_ndkbuild_MainActivity.h文件并且修改文件名为com_hai_ndkbuild_MainActivity.cpp,就可以编写方法实现了。
此时IDE可能还不知道此项目为JNI项目,为了让IDE能支持C++语法方便编写cpp,我们先建立JNI配置文件:
右击cpp > New > File
生成一个空的Android.mk文件,Android.mk内容如下
#固定写法
LOCAL_PATH:=$(call my-dir)
#固定写法
include $(CLEAR_VARS)
#生成so名称
LOCAL_MODULE := JniHelper
LOCAL_SRC_FILES := com_hai_ndkbuild_MainActivity.cpp
#固定写法
include $(BUILD_SHARED_LIBRARY)
此时build 项目应该会报错,不用管。接下来右击app module
build System选择nkd-build,Project Path选择Android.mk的路径,
ok之后,项目被自动build一次,且被IDE识别为JNI项目了。app module的build.gradle文件自动新增externalNativeBuild配置:
android {
sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }
externalNativeBuild {//此处配置可能会因gradle版本不同而有差异
ndkBuild {
path file('src/main/cpp/Android.mk')
}
}
}
因为IDE支持C、C++语法,项目也被识别为JNI项目了,此时修改com_hai_ndkbuild_MainActivity.cpp的内容就方便多了,修改com_hai_ndkbuild_MainActivity.cpp内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_hai_ndkbuild_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_hai_ndkbuild_MainActivity_stringFromJNI
(JNIEnv *env, jobject) { //此处自己补了个env
return env->NewStringUTF("I am From Native C");
};
到这,基本完成,最后就是使用了,很简单:
public class MainActivity extends AppCompatActivity {
static {//加载so
System.loadLibrary("JniHelper");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.tv)).setText(stringFromJNI());
}
public native String stringFromJNI();
}
大家都知道,现在网络上关于JNI开发的文章一搜一大把,简单的复杂的都有。但我为什么还有写一篇呢,网上搜不就行了吗?原因有二,其一:网上的文章描述的不大符合我的要求,有的缺少不要的环境说明,大家都知道开发环境不同相应的配置也可能不同;其二:自己从未记录过JNI开发的步骤,写此博文也是为了加深自己对JNI开发的理解,怕以后忘了熟悉起来也方便。
Thanks!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!