雖然本篇和上篇沒很大的關係,但…還是建議先去看下上篇----初次使用CMake構建native專案
- 初次使用CMake構建native專案
- 如何將現有的cpp程式碼整合到專案中
- 拷貝原始碼
- 編譯成庫檔案
- CMake連結a靜態庫以及so動態庫及動態庫和靜態庫的區別
如何將現有的cpp程式碼整合到專案中
這個在寫JNI的時候就很常見了,比如json庫,C++自己是沒有提供json庫的,然後我們在寫JNI的時候,通常需要和上層互動資料,較簡單的就是json了,那麼就拿json來做講解吧。首先來找一個json庫啦!
jsoncpp本篇就用這個json庫來做講解吧,首先把程式碼clone下來。
原始碼整合進專案中
-
將include裡的json資料夾拷貝到../app/src/main/cpp目錄下;
-
將src/lib_json裡面的檔案除去 CMakeLists.txt拷貝到../app/src/main/cpp目錄下;
-
最終如下:
-
修改../app/src/main/cpp/CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.4.1) add_library( native_hello SHARED json_tool.h json_reader.cpp json_valueiterator.inl json_value.cpp json_writer.cpp version.h.in # 下面的cpp若是沒有則新建一個,本文基於上篇文章 native_hello.cpp ) target_link_libraries( native_hello android log ) 複製程式碼
-
make build一下
What Are you!!!出錯了,告訴在json_tool.h的第10行出錯了,那行吧,點進去瞅一眼,如下:
哦,include錯了,應該改為 #include “json/config.h” 那就改吧!cv過來的所有檔案都得去檢視一下(試想一下,若是cv過來的檔案有1W個,咋辦…,改完這個,將來別的地方在引用,又忘了那個曾經是改過的了,停!stop,我眼疼!)。
編寫測試程式碼
-
開啟../app/src/main/cpp/native_hello.cpp 更改如下:
// // Created by xong on 2018/9/28. // #include<jni.h> #include "json/json.h" #define XONGFUNC(name)Java_com_xong_andcmake_jni_##name extern "C" JNIEXPORT jstring JNICALL XONGFUNC(NativeFun_outputJsonCode)(JNIEnv *env, jclass thiz, jstring jname, jstring jage, jstring jsex, jstring jtype) { Json::Value root; const char *name = env->GetStringUTFChars(jname, NULL); const char *age = env->GetStringUTFChars(jage, NULL); const char *sex = env->GetStringUTFChars(jsex, NULL); const char *type = env->GetStringUTFChars(jtype, NULL); root["name"] = name; root["age"] = age; root["sex"] = sex; root["type"] = type; env->ReleaseStringUTFChars(jname, name); env->ReleaseStringUTFChars(jage, age); env->ReleaseStringUTFChars(jsex, sex); env->ReleaseStringUTFChars(jtype, type); return env->NewStringUTF(root.toStyledString().c_str()); } extern "C" JNIEXPORT jstring JNICALL XONGFUNC(NativeFun_parseJsonCode)(JNIEnv *env, jclass thiz, jstring jjson) { const char *json_str = env->GetStringUTFChars(jjson, NULL); std::string out_str; Json::CharReaderBuilder b; Json::CharReader *reader(b.newCharReader()); Json::Value root; JSONCPP_STRING errs; bool ok = reader->parse(json_str, json_str + std::strlen(json_str), &root, &errs); if (ok && errs.size() == 0) { std::string name = root["name"].asString(); std::string age = root["age"].asString(); std::string sex = root["sex"].asString(); std::string type = root["type"].asString(); out_str = "name: " + name + "\nage: " + age + "\nsex:" + sex + "\ntype: " + type + "\n"; } env->ReleaseStringUTFChars(jjson, json_str); return env->NewStringUTF(out_str.c_str()); } 複製程式碼
-
修改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 outputJsonCode(String name, String age, String sex, String type); public static native String parseJsonCode(String json_str); } 複製程式碼
-
測試:
package com.xong.andcmake; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; import com.xong.andcmake.jni.NativeFun; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv_native_content = findViewById(R.id.tv_native_content); String outPutJson = NativeFun.outputJsonCode("xong", "21", "man", "code"); String parseJson = NativeFun.parseJsonCode(outPutJson); tv_native_content.setText("生成的Json:\n" + outPutJson + "\n解析:" + parseJson); } } 複製程式碼
結果如下圖:
編譯成庫檔案
OK,整合成功,但是太複雜,太麻煩了,每次要寫那麼一堆配置檔案,少一個都不行,還要挨個的去改 include 想想都可怕,那麼可不可以把這個jsoncpp打成庫呢?那麼我們就要考慮如下:
- 必須使用CMake,不使用編寫mk的方式;
- 在任何系統上都可以,不可在編譯庫的時候切換到其他系統;
好吧,基於以上兩點,百度搜了一波,發現…GG,沒有符合的哎,用CMake就得去Linux下面,且需要自己構建工具鏈,要不然就是…mk…。再去Google搜一搜,發現還是這樣的,難道,不存在?再去GitHub搜,發現沒有相關的,無可奈何,去Google提供的sample中找一找吧,哈!還真有發現,連結:hello-libs
編譯so動態庫
-
修改cpp目錄為:
-
修改../cpp/jsoncpp/目錄中的CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.4.1) set(CMAKE_VERBOSE_MAKEFILE on) add_library( # 庫名字 jsoncpp # 庫型別 SHARED # 庫包含的資源 src/json_tool.h src/json_reader.cpp src/json_valueiterator.inl src/json_value.cpp src/json_writer.cpp src/version.h.in) # 匯出目錄 此處的設定 匯出主目錄在 Project/export資料夾內。 set(export_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../export) set_target_properties( # 庫名字 jsoncpp # 設定輸出.so動態庫的路徑 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${export_dir}/libsojsoncpp/lib/${ANDROID_ABI}") add_custom_command( # POST_BUILD 處 有三個值可選 # 分別是: # PRE_BUILD:在 hello 執行其他規則前執行 # PRE_LINK:在編譯原始檔之後但在 連結其他二進位制檔案 或 執行靜態庫的庫管理器 或 歸檔工具 之前執行 # POST_BUILD:最後執行 TARGET jsoncpp POST_BUILD # 拷貝命令 將 ${CMAKE_CURRENT_SOURCE_DIR}/src/json/allocator.h 檔案拷貝到 ${export_dir}/libsojsoncpp/include/json/ 資料夾內 且名字和之前的相同 COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/allocator.h" "${export_dir}/libsojsoncpp/include/json/allocator.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/config.h" "${export_dir}/libsojsoncpp/include/json/config.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/forwards.h" "${export_dir}/libsojsoncpp/include/json/forwards.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/features.h" "${export_dir}/libsojsoncpp/include/json/features.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/value.h" "${export_dir}/libsojsoncpp/include/json/value.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/reader.h" "${export_dir}/libsojsoncpp/include/json/reader.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/writer.h" "${export_dir}/libsojsoncpp/include/json/writer.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/assertions.h" "${export_dir}/libsojsoncpp/include/json/assertions.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/autolink.h" "${export_dir}/libsojsoncpp/include/json/autolink.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/json.h" "${export_dir}/libsojsoncpp/include/json/json.h" COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/json/version.h" "${export_dir}/libsojsoncpp/include/json/version.h" ) 複製程式碼
-
修改 ../cpp/CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.4.1) set(CMAKE_VERBOSE_MAKEFILE on) # 設定資源主目錄 CMAKE_CURRENT_SOURCE_DIR 代表當前CMakeLists.txt 所在的目錄 set(lib_src_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # 設定CMake編譯後檔案的存放的臨時目錄 set(lib_build_DIR $ENV{HOME}/tmp) # 將生成的臨時檔案放在 lib_build_DIR 中 file(MAKE_DIRECTORY ${lib_build_DIR}) # 新增子專案 add_subdirectory(${lib_src_DIR}/jsoncpp ${lib_build_DIR}/jsoncpp) 複製程式碼
-
修改 ../app/build.gradle如下:
apply plugin: 'com.android.application' android { ... defaultConfig { ... externalNativeBuild { cmake { // 這裡的名字最好和 ../cpp/jsoncpp/CMakeLists.txt 中設定的名字相同 targets 'jsoncpp' } ... } } ... externalNativeBuild { cmake { path 'src/main/cpp/CMakeLists.txt' } } } 複製程式碼
說明:點選Build/Make Project(或者 Make Module 'app') 會在 專案根目錄下 新建 export 資料夾 在裡面會存放 庫所需的標頭檔案和so動態庫。編譯後如下:
so和標頭檔案都生成了,但是我們在寫../cpp/jsoncpp/CMakeLists.txt 檔案時,也發現了,將所需的標頭檔案匯出到指定目錄時有點兒費勁,只是名字換了下,若是用程式碼來寫的話,一個for迴圈就可以了,那麼在CMakeLists.txt中,可不可以實現類似的呢?哈哈,那當然是肯定的了,最終修改如下:
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
add_library(
jsoncpp
SHARED
src/json_tool.h
src/json_reader.cpp
src/json_valueiterator.inl
src/json_value.cpp
src/json_writer.cpp
src/version.h.in)
set(export_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../export)
set_target_properties(
jsoncpp
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${export_dir}/libsojsoncpp/lib/${ANDROID_ABI}")
add_custom_command(
TARGET jsoncpp POST_BUILD
# 將 ${CMAKE_CURRENT_SOURCE_DIR}/src/json 資料夾下的檔案 匯出到 ${export_dir}/libajsoncpp/include/json/ 資料夾內
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/json" "${export_dir}/libsojsoncpp/include/json/")
複製程式碼
將之前生成的export資料夾刪除,重新build發現是可以的。
OK,動態庫可以編譯成功,那麼講道理,靜態庫也是一樣的,來嘗試下編譯靜態庫庫。
編譯a靜態庫
在以上編譯so動態庫的前提下,修改../cpp/jsoncpp/CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
add_library(
jsoncpp
# 將 庫 型別 由 SHARED 修改為 STATIC
STATIC
src/json_tool.h
src/json_reader.cpp
src/json_valueiterator.inl
src/json_value.cpp
src/json_writer.cpp
src/version.h.in)
set(export_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../export)
set_target_properties(
jsoncpp
# 將 LIBRARY_OUTPUT_DIRECTORY 修改為 ARCHIVE_OUTPUT_DIRECTORY
# 方便檢視 生成的a檔案目錄修改一下 即 將 libsojsoncpp 修改為 libajsoncpp
PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${export_dir}/libajsoncpp/lib/${ANDROID_ABI}")
add_custom_command(
TARGET jsoncpp POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/json" "${export_dir}/libajsoncpp/include/json/"
)
複製程式碼
修改完成後,Build/Make Project(或者 Make Module 'app') 會在 Project/export目錄下生成:
這樣編譯.a靜態庫和.so動態庫就完成了。
下篇我們講如何連結 生成的so動態庫和a靜態庫,以及動態庫和靜態庫的區別;點選調轉到下一篇:
Demo連結:UseCmakeBuildLib
END