關於在Android中使用CMake你所須要瞭解的一切(二)

雖然本篇和上篇沒很大的關係,但…仍是建議先去看下上篇----初次使用CMake構建native項目java


如何將現有的cpp代碼集成到項目中

​ 這個在寫JNI的時候就很常見了,好比json庫,C++本身是沒有提供json庫的,而後咱們在寫JNI的時候,一般須要和上層交互數據,較簡單的就是json了,那麼就拿json來作講解吧。首先來找一個json庫啦!android

jsoncpp本篇就用這個json庫來作講解吧,首先把代碼clone下來。c++

源碼集成進項目中

  1. 將include裏的json文件夾拷貝到../app/src/main/cpp目錄下;git

  2. 將src/lib_json裏面的文件除去 CMakeLists.txt拷貝到../app/src/main/cpp目錄下;github

  3. 最終以下:json

  4. 修改../app/src/main/cpp/CMakeLists.txt以下:app

    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
    )
    複製代碼
  5. make build一下ide

    What Are you!!!出錯了,告訴在json_tool.h的第10行出錯了,那行吧,點進去瞅一眼,以下:工具

    哦,include錯了,應該改成 #include 「json/config.h」 那就改吧!cv過來的全部文件都得去查看一下(試想一下,如果cv過來的文件有1W個,咋辦…,改完這個,未來別的地方在引用,又忘了那個曾經是改過的了,停!stop,我眼疼!)。post

編寫測試代碼

  1. 打開../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());
    }
    複製代碼
  2. 修改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);
    }
    
    複製代碼
  3. 測試:

    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打成庫呢?那麼咱們就要考慮以下:

  1. 必須使用CMake,不使用編寫mk的方式;
  2. 在任何系統上均可以,不可在編譯庫的時候切換到其餘系統;

好吧,基於以上兩點,百度搜了一波,發現…GG,沒有符合的哎,用CMake就得去Linux下面,且須要本身構建工具鏈,要否則就是…mk…。再去Google搜一搜,發現仍是這樣的,難道,不存在?再去GitHub搜,發現沒有相關的,迫不得已,去Google提供的sample中找一找吧,哈!還真有發現,連接:hello-libs

編譯so動態庫

  1. 修改cpp目錄爲:

  2. 修改../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"
    
            )
    
    複製代碼
  3. 修改 ../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)
    
    複製代碼
  4. 修改 ../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靜態庫,以及動態庫和靜態庫的區別;點擊調轉到下一篇:

CMake連接a靜態庫以及so動態庫及動態庫和靜態庫的區別

Demo連接:UseCmakeBuildLib


END

相關文章
相關標籤/搜索