我们知道Android很多性能底层都是要求用jni去实现的,而实现java和c++之间的大门,就是搭建jni环境的搭建。 我用的Android Studio是3.0的版本,这篇文章主要讲解jni环境从零开始的搭建。
首先是需要在android studio里面安装最基本的环境。
打开Default Preference里面查看SDK Tool选项。
CMake、LLDB、NDK这三个勾选上去,然后安装。
安装好了以后,在File-》Project Structure的SDK Location里面应该可以看到Android NDK Location的地址。
如下图所示。
新建一个Android Studio项目,注意到我这里新建项目的时候并没有什么include C++ 的勾选项目,Android Studio也不会在项目自动生成后帮忙生成cmakelist.txt, 这个没什么关系,只要后面通过gradle的配置指定cmakelist.txt就行。
首先新建一个JNITest类,代码如下:
package jnifive.preqel.com.jnifive;
public class JNITest {
static {
System.loadLibrary("jnilib");
}
public native String getString();
}
这个JNITest就是我们要使用jni的类,jnilib代表的就是你的jni库名。声明native关键字的方法,就是要去调用jni的方法。这时候编译器会提示有错误,不用管。
首先配置好你的jdk环境变量
,即在termianla终端里面可以正常使用javac和javah。(这里就不讲解jdk环境的配置,如果还不会配置环境变量的可以去查一下)
然后需要到JNITest同级目录下面用javac 生成class文件。
具体操作:用终端进入到JNITest.java所在目录,执行 javac JNITest.java
然后在终端里面用命令行退到java文件目录下面,
用javah生成c++头文件
。
终端输入:javah -jni jni.example.com.testjni6.JNITest
注意:请用cd命令退到工程文件目录的java同级目录下面执行,否则会提示找不到文件
成功后会生成
然后右击app目录,新建Folder-》Jni Folder 建立jni目录
点击下一步后看见android studio在工程下面帮我们生成了一个cpp的文件夹,并没有看到所谓的jni文件夹? 这里需要把Android studio的视图切换化为Project,找到app目录,然后在main下面找到jni目录。如以下图所示
然后把刚才的jni_example_com_testjni6_JNITest.h文件拷贝进来。
在这个jni目录下面创建一个新的c/c++ source file。
名字就叫main.cpp好了。里面代码如下:
#include "jni_example_com_testjni6_JNITest.h"
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
JNICALL Java_jni_example_com_testjni6_JNITest_getString(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
注意这里的包名都要对应上。
这时候build一下工程你会发现
这时候不用配什么android.useDeprecatedNdk=true ,这个东西在3.0以后好像是被取消了,配了只会走错路。(表示折腾了很久。。。)
正确的做法是直接用cmakelist.txt配置环境。在工程app/java/目录(并非一定要这个地方,但是最后在build.gradle里面应正确关联)下面直接新建一个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(VERSION 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.
jnilib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
../jni/native-lib.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.
jnilib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
带#号全是注释,所以这里有几个重要的属性要配的。
add_library 下面首先指明库的名称,这里是jnilib,Provides a relative path to your source file(s),这里要用
相对路径
指到cpp文件所在的路径。cmakelist的配置,这个配置一定要配对,否则会抱什么
CMake Error: CMake can not determine linker language for target: 的错误。
然后在app的build.gradle下面新增一些语句。
在defaultConfig下面新增
ndk {
abiFilters 'armeabi', 'armeabi-v7a','x86'
}
externalNativeBuild {
cmake {
cppFlags ""
//生成多个版本的so文件
abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'
}
}
这里表示支持的abi版本,如果设备的abi版本不在这个列表中是安装不了apk的。
注意,这一步很关键,在build.gradle的android属性下面增加下面这段代码,也就是与defaultConfig平级地增加一个externalNativeBuild,里面有个cmake属性,里面path指定了cmakelist.txt的相对路径
externalNativeBuild {
cmake {
path "src/main/java/CMakeLists.txt" // 设置所要编写的c源码位置,以及编译后so文件的名字
}
}
好了,大功告成,基本上的配置就到这里了。
最后在MainActivity里面调用一下这个JNITest的getString()函数,测试一下环境搭建的是否正确,关键代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.textview);
JNITest ndkTest = new JNITest();
try {
tv.setText(ndkTest.getString());
}catch (Exception e){
e.printStackTrace();
}
}
如有需要,请点击下面链接:
项目github源码地址