This article has been published exclusively by guolin_blog, an official wechat account

This article is based on the a static library and so dynamic library constructed in the previous article. If you have a or SO, you can read this article directly. If not, you are advised to go to —- first

  • Use CMake to build a native project for the first time
  • How do I integrate existing CPP code into the project
    • Copy the source code
    • Compile to library files
  • CMake links a static library and so dynamic library and the difference between dynamic and static library

The preparatory work

Put the so dynamic library, a static library, and the corresponding header files into a folder. Because this paper is based on the previous chapter, these files are placed in the following picture:

Libajsoncpp and libsojsoncpp, respectively. In each folder, there is an include folder for the library header files. In lib, there is so and a library files.

Link the so dynamic library

Let’s first link to the more familiar so dynamic library, and then link to the A static library.

  1. The preparatory work

    1. Will be.. The jsoncpp folder in the /app/ SRC /main/ CPP folder was removed in case we were using a library instead of… Source code is available (for those of you who copied the source code into the project as in chapter 2).

    2. Will be.. Delete all contents of cmakelists. TXT in/ app/ SRC /main/ CPP to prevent them from being different from cmakelists. TXT in this article.

    3. Will be.. /app/build.gradle:

      apply plugin: 'com.android.application'
      
      android {
          ...
          defaultConfig {
              ...
              externalNativeBuild {
                  cmake {
                      arguments '-DANDROID_STL=c++_static'
                  }
              }
          }
          buildTypes {
              ...
          }
          sourceSets {
              main {
                  / / specific Settings according to actual condition, due to the objective of the lib on project/export/libsojsoncpp/lib Therefore, so set
                  jniLibs.srcDirs = ['.. /export/libsojsoncpp/lib']
              }
          }
          externalNativeBuild {
              cmake {
                  path 'src/main/cpp/CMakeLists.txt'}}}...Copy the code
  2. Write the app/SRC/main/CPP/CMakeLists TXT file

    cmake_minimum_required(VERSION 3.4.1)
    
    # set a variable to find the directory where the resource is stored, ".." Represents the upper-level directory
    set(export_dir ${CMAKE_SOURCE_DIR}/.. /.. /.. /.. /export)
    
    # add.so dynamic library (jsonCPp)
    add_library(lib_so_jsoncpp SHARED IMPORTED)
    
    # link
    set_target_properties(
    		# library name
            lib_so_jsoncpp
            The directory where the library resides
            PROPERTIES IMPORTED_LOCATION ${export_dir}/libsojsoncpp/lib/${ANDROID_ABI}/libjsoncpp.so)
    
    add_library(
            native_hello
            SHARED
            native_hello.cpp
    )
    
    # link header file
    target_include_directories(
            native_hello
            PRIVATE
            # native_hello required header file
            ${export_dir}/libsojsoncpp/include
    )
    
    # link project
    target_link_libraries(
            native_hello
            android
            log
            # link jsoncpp. So
            lib_so_jsoncpp
    )
    Copy the code

    Well, it looks more configured this time, but… Don’t panic don’t panic it’s ok. Let’s look at them one by one

    1. Cmake_minimum_required (VERSION 3.4.1) cmake_minimum_required(VERSION 3.4.1)

    2. set(….) Considering that the export folder is used more times and is always an absolute path, we set a variable to simplify things. ${CMAKE_SOURCE_DIR} is the path to the current cmakelists. TXT file, and then “.. /” Go to the export folder where we store the resource files.

    3. Add_library (lib_so_jsoncpp SHARED with IMPORTED product); lib_so_jsoncpp; “SHARED” because we’re going to import the so dynamic library, so it’s SHARED; “IMPORTED” and then IMPORTED;

    4. The set_target_properties parameter is a long parameter, so I won’t copy it here. We added the library in the last sentence, but… Imported library is empty, nothing, just a name + type, so I need it to link the name to the imported library, I have put a comment in cmakelists.txt above, ${ANDROID_ABI}; ${ANDROID_ABI}; The above statement concatenates this folder, but I do not have this folder in my real path. /libsojsoncpp/lib/

      Well, that’s the architecture, so… This value represents that (default, all types).

    5. And then there’s another add_library but this one has resources. There’s nothing more to say.

    6. Target_include_directories we had a library directories, but there was no corresponding header.

    7. Target_link_libraries link the required header file to the project.

    Finally, Build/Make Module ‘app’.

  3. The calling code

    1. In fact, the CPP layer code does not need to change, we just use the way we copied the source code last time, but for the convenience of students who read this directly, I will paste the code in native_hello. CPP as follows:

      //
      // Created by xong on 2018/9/28.
      //
      #include<jni.h>
      #include <string>
      #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());
      }
      Copy the code
    2. The corresponding Java layer code is as follows:

      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);
      }
      
      Copy the code
    3. Calling code:

      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"."so");
              String parseJson = NativeFun.parseJsonCode(outPutJson);
              tv_native_content.setText("Generated Json:\n" + outPutJson + "\ n analysis."+ parseJson); }}Copy the code
    4. Results:

      Well! Successful integration, so let’s integrate a static library.

Link a static library

We are still based on the above link so dynamic library modification.

  1. First modify.. /app/build.gradle:

    apply plugin: 'com.android.application'
    
    android {
        ...
        defaultConfig {
    		...
            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_STL=c++_static'}}}...// Delete or comment
    // sourceSets {
    // main {
    // jniLibs.srcDirs = ['../export/libsojsoncpp/lib']
    / /}
    / /}
        externalNativeBuild {
            cmake {
                path 'src/main/cpp/CMakeLists.txt'}}}Copy the code

    Just remove (or comment!) the sourceSets tag that was added when integrating SO. .

  2. Secondly modify.. / app/main/SRC/CPP/CMakeLists. TXT is as follows:

    cmake_minimum_required(VERSION 3.4.1)
    
    # set a variable to find the directory where the resource is stored, ".." Represents the upper-level directory
    set(export_dir ${CMAKE_SOURCE_DIR}/.. /.. /.. /.. /export)
    
    # add.so dynamic library (jsonCPp)
    # add_library(lib_so_jsoncpp SHARED IMPORTED)
    add_library(lib_a_jsoncpp STATIC IMPORTED)
    
    # link
    #set_target_properties(
    # lib_so_jsoncpp
    # PROPERTIES IMPORTED_LOCATION ${export_dir}/libsojsoncpp/lib/${ANDROID_ABI}/libjsoncpp.so)
    
    set_target_properties(
            lib_a_jsoncpp
            PROPERTIES IMPORTED_LOCATION ${export_dir}/libajsoncpp/lib/${ANDROID_ABI}/libjsoncpp.a)
    
    add_library(
            native_hello
            SHARED
            native_hello.cpp
    )
    
    # link header file
    #target_include_directories(
    # native_hello
    # PRIVATE
    # # native_hello required header file
    # ${export_dir}/libsojsoncpp/include
    #)
    target_include_directories(
            native_hello
            PRIVATE
            # native_hello required header file
            ${export_dir}/libajsoncpp/include
    )
    
    # link project
    target_link_libraries(
            native_hello
            android
            log
            # link jsoncpp. So
    # lib_so_jsoncpp
            lib_a_jsoncpp
    )
    Copy the code

    Modify the configuration of the last integration SO, as above, the modified places are all corresponding, basically no difference with the integration SO.

  3. call

    The Java layer does not need to be modified.

    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"."a");
            String parseJson = NativeFun.parseJsonCode(outPutJson);
            tv_native_content.setText("Generated Json:\n" + outPutJson + "\ n analysis."+ parseJson); }}Copy the code
  4. Results:

    You can see that type changes to “A”, in which case the static library is successfully integrated.

The difference between dynamic and static libraries

Some people will say, you are using JSON, and return type is you pass in, you integrate so and pass a, then integrate A?

How do we know if the apK is so dynamic library or A static library? Android only supports the call so dynamic library, does not support a static library, so this… Integrating a static library is not bullshit?

OK, now to explain this series of problems, first we need to know what a static library is and what a dynamic library is.

Refer to libraries in Linux

Extract the main:

  • Static library

    Link time: static library code is loaded into the program during compilation;

    Link mode: the object code uses any function in the library, to integrate the function related data into the object code;

    Advantages: the execution program after compilation does not need external function library support;

    Disadvantages: The program must be recompiled if the static library used is updated.

  • The dynamic library

    Link time: dynamic libraries are not compiled into object code at compile time, but are called when functions in the library are used at runtime.

    Link mode: dynamic link, when using the library function to load the library;

    Advantages: Dynamic library changes do not affect the program, that is, do not need to recompile;

    Disadvantages: Because the function library is not integrated into the program, the program’s runtime environment must depend on the library file.

A little more concise:

A static library is a pile of CPP files that need to be compiled to run each time. If you want to use one of these CPP files in your own code, you can compile the one you need from this pile.

A dynamic library is a collection of CPP files that are compiled and run without recompiling the CPP. When a function in the library is needed, the library is loaded. When it is not needed, the library is not loaded.

So, we can answer the above question, yes, yes, Android can only call the so dynamic library, our integrated A static library, when using the static library function, it will go to the static library to get the corresponding metadata, and then the data into the so dynamic library that we will eventually call, In this case, native_hello.so.

Then we integrate the SO dynamic library in.. /app/build.gradle adds a tag like this:

    sourceSets {
        main {
            jniLibs.srcDirs = ['.. /export/libsojsoncpp/lib']}}Copy the code

After the above explanation, it is not difficult to understand the sentence, which has been said:

When a function in the library is needed, the library is loaded in, and when it’s not needed, the library must exist.

So, native_hello.so depends on jsoncpp.so, so jsoncpp.so must exist, so this means that jsoncpp.so is inserted into the APK. We can use JADX to check the apK of the above integrated SO dynamic library, as follows:

Jsoncpp. So native_hello.so;

So let’s look at the integration of a static library! As follows:

That’s the difference!

conclusion

So, as long as you have it in your code, it’s going to be there, even if you just call it and don’t use it later, it’s going to be there.

The A way, you just keep it there while you’re coding, just take a couple of functions, just take a couple of functions from A.

question

jstring name = "xong";
const char* ccp_name = env->GetStringUTFChars(name, NULL);
env->ReleaseStringUTFChars(name, ccp_name);
Copy the code

Phenomenon: If you use GetStringUTFChars you have to call ReleaseStringUTFChars to release the resource, However, when ReleaseStringUTFChars is called, ccp_name is still accessible, that is, the resource has not been freed.

Question:

  1. Does calling GetStringUTFChars without ReleaseStringUTFChars cause a memory leak and crash?
  2. Whether ccp_name should be accessed after ReleaseStringUTFChars is called;
  3. When ReleaseStringUTFChars should be used;

Feel free to discuss it in the comments below.


other

I originally wanted to write this article into three parts, but after some thought, Android development, there is no need to know so much about Native, so I compressed and compressed it into one part.

Cmakelists.txt is not that cumbersome, and much of it is repetitive and can be generated from scripts, such as the resource files added to add_library, as well as others. So this cmakelists. TXT can write a small script? I feel ok.

Android_ndk = android_ndk = android_ndk = android_ndk = android_ndk = android_ndk = android_ndk = android_ndk = android_ndk I don’t know what to say, but I’d better read more about Google Github. Hahaha ~

Finally, the source code covered in all three articles has been uploaded to GitHub at UseCmakeBuildLib


END