I believe that you have been exposed to JNI more or less in the development process, and every time you want to contact JNI, gasp, it is too difficult!

Only Java code and C++ code are good. Check “Include C++ support” when creating a new project, then go all the way to next and finish. A simple Android project with C++ code is complete. Then look at the cmakelists.txt how to write, but the actual development, it is not simple, because the bottom (C++) there may be a static library, also may be. So dynamic library, and then how to integrate into the project is: a face confused, two faces at a loss, three faces at a loss. Due to the objective facts can only go to Baidu, and then Baidu to all use Android. Mk to achieve the majority, but now all Android Studio 3.1+ era, mk? There is very little information about CMake, so this article is here.

Last year was an internship in the company, the boss gave me a. A static library (did not know this is a static library), and is very curious, very want to know. How a is constructed, what is the difference between a and so, all in all a lot of questions, after confirmation, also tried to build a. A static library, helpless! Baidu to the role of mk, find a CMake, but unexpectedly to Linux compiled below, but also what custom tool chain, in short, a batch of trouble.

This series will not explain what CMake is, what NDK is, and what JNI is. It will just write about common problems with CMake on Android and how to solve them.

This series covers:

  • 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

Use CMake to build a native project for the first time

Include C++ support = include C++ support = include C++ support = include C++ support

New project, nothing, we all know that to use CMake, you must have cmakelists. TXT, the second is the required CPP resources, this is very simple, SRC /main directory to create a new CPP folder, to make the main directory clean, Cmakelists. TXT and native_hello. CPP file, and then go to cmakelists. TXT under simple configuration, as follows:

  1. ../src/main/cpp/native_hello.cpp:

    //
    // Created by xong on 2018/9/28.
    //
    #include<jni.h>
    #include <string>
    
    extern "C" JNIEXPORT
    jstring JNICALL
    Java_com_xong_andcmake_jni_NativeFun_stringFromJNI(JNIEnv *env, jclass thiz)
    {
        std: :string hello = "Hello,I from C++";
        return env->NewStringUTF(hello.c_str());
    }
    Copy the code

    Java_com_xong_andcmake_jni_ is used to look for layers of Java packages, such as com.xong.andcmake.jni. NativeFun_stringFromJNI is the class and method name. The second parameter could be “jobject”, and when I wrote JNI recently, I was prompted to change it to “jclass”, but “jobject” is correct, it’s correct.

  2. ../src/main/cpp/CMakeLists.txt

    # CMake minimum version
    cmake_minimum_required(VERSION 3.4.1)
    Add the resources that need to be packaged
    add_library(
    		# library name
            native_hello
            # library type
            SHARED
            # Included CPP
            native_hello.cpp
    )
    # Link to the project
    target_link_libraries(
            native_hello
            android
            log
    )
    Copy the code

    So that’s the C++ part

  3. Change.. /app/build.gradle

    android {
        ...
        defaultConfig {
            ...
            externalNativeBuild {
                cmake {
                    arguments '-DANDROID_STL=c++_static'}}... }... externalNativeBuild { cmake { path'src/main/cpp/CMakeLists.txt'}}... }Copy the code
  4. Write the corresponding Java layer code, create jNI under com.xong.andcmake package, and then create NativeFun class, the 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 stringFromJNI(a);
    }
    
    Copy the code
  5. Call nativeFun.stringFromJni () to see if it has the correct output: This is easy, just add a TextView to the Activity and display it. If correct, it should display:

OK, so we are done with our first APP with CPP code. Then let’s think about it. Let’s look at the code for native_hello.cpp:

//
// Created by xong on 2018/9/28.
//
#include<jni.h>
#include <string>

extern "C" JNIEXPORT
jstring JNICALL
// Can we optimize this?
Java_com_xong_andcmake_jni_NativeFun_stringFromJNI(JNIEnv *env, jclass thiz)
{
    std: :string hello = "Hello,I from C++";
    // What about here?
    return env->NewStringUTF(hello.c_str());
}
Copy the code

For us:

  1. Try not to write duplicate code
  2. Code efficiently

For the above function name, Java_com_xong_andcmake_jni_, if we feel that the corresponding native class and method in Java are not suitable in this package, we need to change the package name, but there are many native methods in my class, as long as we change the package, Then the corresponding CPP function name should be changed one by one, less function is better, if there are 1000, it is really not too cool to change, and easy to lose, so there is no way to reduce the workload as far as possible? Classes and packages are relatively fixed, so can we extract the package name into an expression? That’s right! That’s the macro definition!

When creating a project with CPP in Android Studio, it is written in this way, and then it is natural to copy it. What you can see is that at the end of the return, the string has gone through another layer of conversion points and you can see that the string has been converted into a char array. C_str () converts a string into a char[] array. Superfluous, let the computer do more things? So the modified code is as follows:

//
// Created by xong on 2018/9/28.
//
#include<jni.h>
#define XONGFUNC(name)Java_com_xong_andcmake_jni_##name

extern "C" JNIEXPORT
jstring JNICALL
XONGFUNC(NativeFun_stringFromJNI)(JNIEnv *env, jclass thiz)
{
    return env->NewStringUTF("Hello,I from C++");
}
Copy the code

In this case, if NativeFun is not under the JNI package, but under the JNIA package, then we just need to change the above macro definition;

Since we’re not doing anything to string, we don’t need it, we just return it, so the computer doesn’t get so tired.


What happens after make Project?

For those of you who are familiar with JNI, apK will become very large when CPP code is added to the project, so why, so LET me go over what happens after CPP code and make project, it is clear that so will be generated. How many are generated by default?

Open.. / app/build/intermediates/cmake/debug/obj is as follows:

By default generates four, is one of the biggest v7a, x86_64 is the smallest, by default, this is four will hit the inside of the apk (because is not clear exactly to the cell phone installed ah, can only be all resources into the apk type), in order to narrow apk, so then the need into the apk. Then, the question arises, how do you generate the prescribed SO library? The code above is as follows:

apply plugin: 'com.android.application'

android {
	...
    defaultConfig {
    	...
        externalNativeBuild {
            cmake {
                arguments '-DANDROID_STL=c++_static'
            }
            ndk {
                abiFilters "armeabi-v7a"."x86"}}... }... }...Copy the code

In.. /app/build.gradle add the NDK tag and write the name of the architecture you want to build.

Thus, only v7A and x86 so libraries are infiltrated into APK. In the next article, we’ll look at several ways existing CPP code can be integrated into a project and how to do it.

Click to next: How to integrate existing CPP code into a project

The Demo link: UseCmakeBuildLib


END