作者:谭东
时间: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} )
4、编译我们的源码,生成相应的so库。
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 <clocale> /* 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<typename Lconv,bool=(sizeof(Lconv) >= sizeof(char*))> struct Locale { static char decimalPoint() { return '\0'; } }; /// Return decimal_point for the current locale. template<typename Lconv> struct Locale<Lconv,true> { 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<char>(cp); } else if (cp <= 0x7FF) { result.resize(2); result[1] = static_cast<char>(0x80 | (0x3f & cp)); result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6))); } else if (cp <= 0xFFFF) { result.resize(3); result[2] = static_cast<char>(0x80 | (0x3f & cp)); result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12))); } else if (cp <= 0x10FFFF) { result.resize(4); result[3] = static_cast<char>(0x80 | (0x3f & cp)); result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12))); result[0] = static_cast<char>(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<char>(value % 10U + static_cast<unsigned>('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<struct lconv>::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 <jni.h> #include <string> #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; } }
注意jsoncpp不支持long型,支持double等其他类型。
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,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,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} )
然后编写jni的cpp方法:
#include <jni.h> #include <string> #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; } }
再附上build.gradle的配置:
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' }
效果如图:
其他的编写方法一样,敬请关注。