相信你们在开发的过程当中,都或多或少的接触过JNI,而后每次要接触JNI的时候,倒吸一口冷气,太难啦!java
只有Java代码和C++代码 还好,在新建项目的时候把那个 "Include C++ support"勾选上,而后一路next,最后finish,一个简单的带有C++代码的Android项目就算完成了,而后在看下CMakeLists.txt怎么写的,照猫画虎就能够了,可是实际开发中,并不简单,由于底层(C++)那里给过来的有多是.a静态库,也有多是.so动态库,而后如何集成进项目里就是:一脸懵逼,二脸茫然,三脸不知所措。因为客观事实只能去百度,而后百度到的全是用Android.mk实现的居多,然而如今都Android Studio 3.1+时代了,还mk?关于CMake的资料又不多,因此就有了本文。android
在去年在公司仍是实习的时候,老大丢给我一个.a静态库(当时并不知道这是一个静态库),非常好奇,很想知道.a是怎么构建出来的,a和so的区别又是什么,总之一大堆疑问,在转正后,也尝试过要构建一个.a静态库,无奈!百度到的都是用mk的咯,找到一篇CMake的,但居然要去Linux下面编译,还要什么自定义工具链,总之,麻烦的一批。c++
因此就有了这一系列,本系列不阐述什么是CMake,什么是NDK,什么是JNI,只是写一下在Android中使用CMake常遇的问题,解决方法是什么。git
本系列涉及到的有:github
这个就很简单啦,新建项目的时候把 include C++ support 勾选上就能够了,但,咱们仍是本身动手来一遍,不依靠IDE,看看可不能够,新建普通项目,名字为:AndCmake,新建完成后以下:数组
新项目,什么都没有,咱们都知道用CMake的话,必需要有CMakeLists.txt(注意名字不能写错),第二个就是须要的cpp资源啦,这就很简单了,在src/main目录下新建就行了,为了整洁在main目录下新建cpp文件夹,且在里面新建CMakeLists.txt和native_hello.cpp文件,而后在去CMakeLists.txt简单的配置下就行了,完成后,以下:架构
../src/main/cpp/native_hello.cpp:app
//
// Created by xong on 2018/9/28.
//
#include<jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL Java_com_xong_andcmake_jni_NativeFun_stringFromJNI(JNIEnv *env, jclass thiz) {
std::string hello = "Hello,I from C++";
return env->NewStringUTF(hello.c_str());
}
复制代码
这里须要说明如下,Java_com_xong_andcmake_jni_是去java的一层一层的包下面去寻找,好比这个就是去com.xong.andcmake.jni下面找,后面的NativeFun_stringFromJNI就是类和方法名了。函数的形参两个必须是这样的,第二个形参能够写成"jobject",我最近写JNI的时候,提示改为"jclass"可是,"jobject"也不能算错,也是对的。函数
../src/main/cpp/CMakeLists.txt工具
# CMake最低版本
cmake_minimum_required(VERSION 3.4.1)
# 将须要打包的资源添加进来
add_library(
# 库名字
native_hello
# 库类型
SHARED
# 包含的cpp
native_hello.cpp
)
# 连接到项目中
target_link_libraries(
native_hello
android
log
)
复制代码
这就把C++部分写好了
修改../app/build.gradle,修改后以下:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
}
...
}
...
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
...
}
复制代码
写对应的Java层代码,在com.xong.andcmake包下新建jni,而后新建NativeFun类,代码以下:
package com.xong.andcmake.jni;
/** * Create by xong on 2018/9/28 */
public class NativeFun {
static {
System.loadLibrary("native_hello");
}
public static native String stringFromJNI();
}
复制代码
调用NativeFun.stringFromJNI(),查看是否有正确的信息输出:这就很简单啦,在Activity中添加一个TextView而后显示下就行了,正确的话,应该会有以下显示:
OK,这样咱们的第一个带有CPP代码的APP就算完成了,而后咱们来思考一下,先来看native_hello.cpp的代码:
//
// Created by xong on 2018/9/28.
//
#include<jni.h>
#include <string>
extern "C" JNIEXPORT
jstring JNICALL
// 这里可不能够优化一下?
Java_com_xong_andcmake_jni_NativeFun_stringFromJNI(JNIEnv *env, jclass thiz)
{
std::string hello = "Hello,I from C++";
// 这里呢?
return env->NewStringUTF(hello.c_str());
}
复制代码
对于咱们来说:
对于上面的函数名来说,Java_com_xong_andcmake_jni_,假如咱们在Java中对应的native类和方法,感受在这个包中不合适,要换下包名,可是我这个类中有不少native方法,只要一换包,那么对应的cpp中的函数名就要挨个改,函数少还好办,假若有1000个,改起来真的是不要太爽,并且容易丢,那么有没有办法来尽可能的减小工做量呢?类和包是相对固定的,那么咱们能不能把包名抽取出来用一个表达式来表示呢?没错!就是宏定义啦!
对于下面返回的那一句,我为啥要这样写呢,由于用Android Studio建立一个带有cpp项目的时候,里面就是这样写的,而后就很天然的抄来了,可是咱们想一下,有这样写的必要吗?能够看到的是在最后返回的时候,string对象又通过了一层转换点进去后能够发现,是将 string对象又转成了char数组(固然在C++中是char*指针仍是const的,这里不须要知道const是啥,只须要知道,c_str()实际上是又将string对象转成了char[]数组),而后咱们这样写是否是就…画蛇添足,让计算机多作了一些事情呢?因此改完后的代码以下:
//
// Created by xong on 2018/9/28.
//
#include<jni.h>
#define XONGFUNC(name)Java_com_xong_andcmake_jni_##name
extern "C" JNIEXPORT jstring JNICALL XONGFUNC(NativeFun_stringFromJNI)(JNIEnv *env, jclass thiz) {
return env->NewStringUTF("Hello,I from C++");
}
复制代码
这样的话,假如NativeFun不在jni包下了,而是在jnia包下,那么咱们只须要将上面的宏定义改下就行了;
由于咱们没有对string作操做,那么咱们就不须要它啦,直接返回就行了,这样计算机就不那么累了。
接触过JNI的同窗应该了解,项目里面添加了cpp代码,apk会变的很大,那么缘由嘞,那么我就来捋一捋,cpp的代码再make project后都发生了什么,都很清楚会生成so,那么…在默认的状况下会生成几个?
打开 ../app/build/intermediates/cmake/debug/obj 目录以下:
在默认的状况下会生成4个,v7a的是最大的,x86_64是最小的,默认状况下,这4个是都会打到apk里面的(由于不清楚究竟是往那台手机上安装呀,只能将全部的资源都打到apk里面了),要想缩小apk,那么就把须要的so打入apk就行了。而后,问题来了,怎么才能生成规定的so库呢?上代码,以下:
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
}
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
...
}
...
}
...
复制代码
在../app/build.gradle中添加ndk标签,且在里面写上须要生成的架构名称就能够了,从新build下项目,以下图:
这样打入apk中的就只有v7a和x86两种架构的so库啦! 下篇咱们来说解一下 现有的cpp代码集成到项目中有几种方式,以及如何操做。
点击跳转到下一篇:如何将现有的cpp代码集成到项目中
Demo连接:UseCmakeBuildLib
END