作者:谭东
时间:2017年9月21日
环境:Windows 8.1
NDK版本:android-ndk-r14b
工具:Android Studio2.3.3
有的时候我们需要在封装的so库里把要获取到的信息转为json格式输出,或者解析外部传入进来的json数据,那么C++里一般使用jsoncpp库来实现。
jsoncpp 是比较出名的 C++ JSON 解析库。在 JSON官网也是首推的。源码下载链接:http://jsoncpp.sourceforge.net/
下面就给大家讲解jsoncpp的编译和简单使用。编译出的so库可以供Android使用。其他平台大同小异。
1、下载jsoncpp源码。可以在github上下载到:https://github.com/open-source-parsers/jsoncpp
下载后,jsoncpp的大致目录如下图。
主要关注include和src目录下的lib_json这两个文件夹。
include里是.h头文件,src目录下的lib_json里是.cpp源文件。
2、用Android Studio新建一个支持c++的项目。在项目的libs目录下新建libjson文件夹,将include里的json文件夹里的.h头文件拷贝进去,并将src/lib_json下的.cpp和.inl及.h文件也拷贝进去。
导入后可能会报错,引入的头文件路径不对,那么就按照提示进行修改和导入相应的头文件即可。
3、接下来,我们需要把我的源文件加入到列表,供CMake编译。编辑我们的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( json
SHARED
libs/include/libjson/json_reader.cpp
libs/include/libjson/json_value.cpp
libs/include/libjson/json_writer.cpp)
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 )
# 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 )
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
# 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
json
# Links the target library to the log library
# included in the NDK.
${log-lib} )
5、编译好后,我们就可以查看到so库了。
如果编译中遇到这种错误提示,我们只需把include的头文件的<>改成“”即可。
注意,这里的json_tool.h要改下,正确的为:
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#include
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
/// Fallback for decimal_point on android, where the lconv is an empty struct.
template= sizeof(char*))>
struct Locale {
static char decimalPoint() {
return '\0';
}
};
/// Return decimal_point for the current locale.
template
struct Locale {
static char decimalPoint() {
Lconv* lc = localeconv();
if (lc == NULL) {
return '\0';
}
return *(lc->decimal_point);
}
};
/// Converts a unicode code-point to UTF-8.
static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {
JSONCPP_STRING result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast(0x80 | (0x3f & cp));
result[0] = static_cast(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast(0x80 | (0x3f & cp));
result[1] = static_cast(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast(0x80 | (0x3f & cp));
result[2] = static_cast(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
/// Returns true if ch is a control character (in range [1,31]).
static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
typedef char UIntToStringBuffer[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned interger to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast(value % 10U + static_cast('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
static inline void fixNumericLocale(char* begin, char* end) {
while (begin < end) {
if (*begin == ',') {
*begin = '.';
}
++begin;
}
}
static inline void fixNumericLocaleInput(char* begin, char* end) {
char decimalPoint = Locale::decimalPoint();
if (decimalPoint != '\0' && decimalPoint != '.') {
while (begin < end) {
if (*begin == '.') {
*begin = decimalPoint;
}
++begin;
}
}
}
} // namespace Json {
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
修改方案看这里:https://github.com/open-source-parsers/jsoncpp/blob/52cfe5ae889adf0c4099b16ddeb4b529b1f09007/src/lib_json/json_tool.h
编译后的so库如下图所示:
6、简单的使用方法:
#include
#include
#include "../../../libs/include/libjson/value.h"
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_tandong_jsoncpp_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jstring JNICALL
Java_com_tandong_jsoncpp_MainActivity_stringFromJNI2(
JNIEnv *env,
jobject /* this */) {
Json::Value video;
video["id"] = 1;
video["name"] = "名字";
const char *json_str = video.toStyledString().c_str();
jstring result = env->NewStringUTF(json_str);
return result;
}
}
7、如果我想把libjson.so放在其他项目引用使用怎么办?请往下看。假如我们新建个项目,需要用到libjson.so,并且重新编译使用了libjson.so库的so库。没听懂?看下图:
先把上面编译后的libjson.so拷贝到新项目src/main/jniLibs/armeabi 里,即对应的so库文件夹里。
然后把jsoncpp源码include里的.h文件拷贝到新项目的libs/include/libjson/下面。
如下图所示:
接下来编写配置CMakeList.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.
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
add_library( json
SHARED
IMPORTED )
set_target_properties( json
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libjson.so )
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 )
# 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.
include_directories(libs/include)
target_link_libraries( # Specifies the target library.
native-lib
json
# Links the target library to the log library
# included in the NDK.
${log-lib} )
#include
#include
#include "../../../libs/include/libjson/value.h"
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_tandong_jsoncppdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jstring JNICALL
Java_com_tandong_jsoncppdemo_MainActivity_stringFromJNI2(
JNIEnv *env,
jobject /* this */) {
Json::Value video;
video["id"] = 1;
video["name"] = "名字";
const char *json_str = video.toStyledString().c_str();
jstring result = env->NewStringUTF(json_str);
return result;
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "com.tandong.jsoncppdemo"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk{
abiFilters 'armeabi'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
其他的编写方法一样,敬请关注。