写在前面
刚入职那会,经理就布置了一道题目,由于涉及到NDK的知识,所以还是显得有些不知所措,但经理说不难,下载官方的Demo做些修改就可以搞定了。所以通过经理的一些提示和资料的查找,也算在周末里完成了,做了之后才发现这题确实不难……
同样地,这一篇也还是直接地写实现方法,然后也要其它时间写一些原理性地东西。另外由于经理对结构有一些要求,所以这道题目里也做了一些调整,可能会被看作是“过度设计”。
另外这里用的是Android Studio 2.2后使用的CMake,所以应该是跟之前需要的使用mk的方法不一样,当时不了解,看了一些文章比较旧,所以有些概念混淆了,是经理提醒了才明白。
结构和流程
同样地先看下结构
这个project里包含了两个module,app和library,library是个依赖库,用于提供C++的方法。
主要的实现流程:
实现
library
1. 添加CMake
在创建library后需要在build.gradle里添加CMake的使用:
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
2. 编写C(C++)代码
在main目录下创建cpp文件夹,在这里进行C代码的编写。本来只有一个native-lib.cpp,但后来我还是做了拆分。
calculateBeerResult.cpp和calculateBeerResult.h是对计算方法的封装,把.cpp里的方法在.h头部文件里进行声明,这样我们在native-lib.cpp里引用calculateBeerResult.h的时候就能使用该方法了。
如下:
calculateBeerResult.cpp
#include "calculateBeerResult.h"
/**
* 返回一个int数组
* 第一个数表示已经喝的瓶数
* 第二个数表示剩下的空瓶子
* 第三个数表示剩下的盖子
*/
int* calculateBeerResult(int money){
//总价格
int allMoney = money;
//已经喝的瓶数
int drunkBottle = allMoney / 2;
//剩下的空瓶子
int emptyBottle = drunkBottle;
//剩下的瓶盖
int top = drunkBottle;
int size = 3;
int *result = new int[size];
while (emptyBottle >= 2 || top >= 4) {
int tmp = emptyBottle / 2 + top / 4;
drunkBottle = drunkBottle + tmp;
emptyBottle = tmp + emptyBottle % 2;
top = tmp + top % 4;
}
result[0] = drunkBottle;
result[1] = emptyBottle;
result[2] = top;
return result;
}
calculateBeerResult.h
#ifndef DRINKBEER_CALCULATEBEERRESULT_H
#define DRINKBEER_CALCULATEBEERRESULT_H
//声明我们写的计算方法
int* calculateBeerResult(int money);
#endif //DRINKBEER_CALCULATEBEERRESULT_H
native-lib.cpp
#include <jni.h>
#include "calculateBeerResult.h" //引入计算方法的头部
extern "C"
JNIEXPORT jintArray JNICALL
Java_message_license_szca_com_drinkbeer_presenter_CalculateBeerPresenter_getCalculateResult(
JNIEnv *env,
jobject /* this */,
int money//java传进来的金额) {
//因为返回的结果是个数组,所以这里查阅后使用了指针这种方法
int *result = calculateBeerResult(money);
int size = sizeof(result);
jintArray arry = env->NewIntArray(size);
env->SetIntArrayRegion(arry, 0, size, result);
return arry;
}
这样我们就可以看出来,在native-lib.cpp里,主要是接收java传进来的金额,然后通过计算后,返回一个数组结果给java即可。
3. CMakeLists文件里添加C依赖
在当前module目录下的CmakeLists.txt里添加我们用到的C文件:
# 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.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
)
add_library(
calculateBeerResult SHARED src/main/cpp/calculateBeerResult.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
calculateBeerResult
# Links the target library to the log library
# included in the NDK.
${log-lib} )
app
1. app使用编写好的C
在app这个module里我们要做的就只是对我们编写的C的引用,传入数据和获取结果即可:
public class CalculateBeerPresenter {
//1.声明我们要引入的cpp文件
static {
System.loadLibrary("native-lib");
}
//2.声明我们要使用该cpp文件里的哪个方法
public native int[] getCalculateResult(int money);
private Activity mContext;
private ICalculateBeerView mICalculateBeerView;
public CalculateBeerPresenter(Activity context , ICalculateBeerView iCalculateBeerView){
this.mContext = context;
this.mICalculateBeerView = iCalculateBeerView;
}
/**
* 计算结果
* @param str
*/
public void calculateBeer(String str) {
String input = str;
if (TextUtils.isEmpty(input)) {
mICalculateBeerView.calculateFailed("输入金额");
return;
}
//3.调用该方法
int result[] = getCalculateResult(Integer.valueOf(input));
mICalculateBeerView.calculateSuccess(result);
}
}
至此,我们就完成了一个简单的NDK的使用。