美文网首页
Android使用C语言库入门

Android使用C语言库入门

作者: 风雪守候 | 来源:发表于2021-11-04 15:28 被阅读0次

    使用Android Studio开发时使用C库来实现某些功能,例如需要使用libmobi库来解析mobi格式的文件

    一、在已有项目中添加

    1.在项目的app(或module)目录下的src/main新建cpp文件夹(在java同级)
    2.在cpp目录下添加CMakeLists.txt文件

    CMakeLists.txt

    # Sets the minimum version of CMake required to build your native library.
    # This ensures that a certain set of CMake features is available to
    # your build.
    
    cmake_minimum_required(VERSION 3.10.2)
    
    # Specifies a library name, specifies whether the library is STATIC or
    # SHARED, and provides relative paths to the source code. You can
    # define multiple libraries by adding multiple add.library() commands,
    # and CMake builds them for you. When you build your app, Gradle
    # automatically packages shared libraries with your APK.
    
    #定义一个变量
    set(LIBRARY_NAME parser)
    
    #设置要编译的库
    add_library( # Specifies the name of the library.
                 ${LIBRARY_NAME}
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 )
    
    # Specifies a path to native header files.
    # include_directories(src/main/cpp/include/)
    
    #主要是找NDK中的库
    find_library( # Defines the name of the path variable that stores the
                  # location of the NDK library.
                  log-lib
    
                  # Specifies the name of the NDK library that
                  # CMake needs to locate.
                  log )
    
    # Links your native library against one or more other native libraries.
    #将本机库链接到一个或多个其他本机库(其实就是将这些库链接起来,不然不能调方法)
    target_link_libraries( # Specifies the target library.
                           ${LIBRARY_NAME}
    
                           # Links the log library to the target library.
                           ${log-lib} )
    
    image.png
    3.在app目录下的build.gradle中添加以下代码
    android {
       ...
        defaultConfig {
            ...
            externalNativeBuild {
                cmake {
                    cppFlags ''
                }
            }
        }
        ...
        externalNativeBuild {
            cmake {
                path file('src/main/cpp/CMakeLists.txt')
                version '3.10.2'
            }
        }
        ndkVersion "16.1.4479499"
    }
    

    ndkVersion "16.1.4479499" 指定ndk的版本,可以改成你需要的版本

    4.在cpp目录下新建temp.cpp和temp.h并在CMakeLists.txt的add_library中添加
    image.png
    image.png

    cpp文件可添加多个,如下:


    image.png
    5.在需要使用的地方先loadLibrary,然后定义jni方法
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
        }
    
        private external fun stringFromJNI(): String
        companion object {
            init {
                System.loadLibrary("parser")
            }
        }
    }
    

    在stringFromJNI方法上使用快捷提示option+enter (Mac os),并选择Create Jni function


    image.png

    然后会在temp.cpp中会自动创建对应的方法,如下


    image.png
    TODO位置就可以写c++代码
    使用libmobi库请直接看第三部分

    二、在新建项目中添加

    File->new->new Project,选择Native C++,然后next->finish


    image.png

    这样就直接完成了,IDE会直接帮你创建一个native-lib.cpp


    image.png

    如果要指定ndk版本,如下

    android {
      ...
      externalNativeBuild {
            cmake {
                path file('src/main/cpp/CMakeLists.txt')
                version '3.10.2'
            }
        }
        ndkVersion "16.1.4479499"
    }
    

    三、使用libmobi库

    1.进入libmobi GitHub仓库libmobi
    2.复制libmobi中src目录下所有文件
    3.在自己的项目的cpp文件夹下新建libmobi文件夹并粘贴刚才复制的所有文件
    image.png
    4.在CMakeLists.txt中添加
    image.png

    由于libmobi使用了NDK的zlib,所以需要在CMakeLists.txt有如下修改


    image.png
    5.在temp.cpp的jni方法里写c++代码调用libmobi的解析方法
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_test_temp_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
        jstring file_path = (jstring) "you_file_path";
        jstring parser_out_path = (jstring)"parser_out_path";
        //parserMobi方法是从mobitool.c的loadfilename(const char *fullpath)中拷贝的
        return parserMobi(env, Jstring2CStr(env, file_path),parser_out_path);
    }
    

    Jstring2CStr方法放在最后了
    parserMobi()就是自定义解析mobi格式文件的方法,方法的具体代码比较多就不发了,这个方法的主要逻辑是从libmobi的tools文件夹下的mobitool.c中的loadfilename(const char *fullpath)方法中拷贝的,具体的解析方法可以看mobitool.c的main(int argc, char *argv[])方法,例如main方法有如下的介绍:

    image.png
    mobitool.c本身就是一个命令行工具,每个参数都有自己的作用,根据自己的需求找到对应参数在mian方法中的处理然后参考。例如’-s‘就是解析源文件,到mobitool.c的main方法找到如下代码
    /**
     @brief Main
     */
    int main(int argc, char *argv[]) {
        ...
        int c;
        while((c = getopt(argc, argv, "cd" PRINT_EPUB_ARG "imo:" PRINT_ENC_ARG "rs" PRINT_RUSAGE_ARG "vx7")) != -1)
            switch(c) {
                ...
                case 'r':
                    dump_rec_opt = 1;
                    break;
                case 's':
                    dump_parts_opt = 1;
                    break;
                ...
                default:
                    exit_with_usage(argv[0]);
            }
        ...
        ret = loadfilename(filename);
        ...
        return ret;
    }
    

    可知是将dump_parts_opt赋值为1,最后执行了loadfilename(filename)方法,再到loadfilename方法中找到dump_parts_opt相关的地方

    /**
     @brief Main routine that calls optional subroutines
     @param[in] fullpath Full file path
     */
    int loadfilename(const char *fullpath) {
        ...
        } else if (dump_parts_opt || create_epub_opt) {
            printf("\nReconstructing source resources...\n");
            /* Initialize MOBIRawml structure */
            /* This structure will be filled with parsed records data */
            MOBIRawml *rawml = mobi_init_rawml(m);
            if (rawml == NULL) {
                printf("Memory allocation failed\n");
                mobi_free(m);
                return ERROR;
            }
    
            /* Parse rawml text and other data held in MOBIData structure into MOBIRawml structure */
            mobi_ret = mobi_parse_rawml(rawml, m);
            if (mobi_ret != MOBI_SUCCESS) {
                printf("Parsing rawml failed (%s)\n", libmobi_msg(mobi_ret));
                mobi_free(m);
                mobi_free_rawml(rawml);
                return ERROR;
            }
            if (create_epub_opt && !dump_parts_opt) {
            ...
            } else {
                printf("\nDumping resources...\n");
                /* Save parts to files */
                ret = dump_rawml_parts(rawml, fullpath);
                if (ret != SUCCESS) {
                    printf("Dumping parts failed\n");
                }
            }
           /* Free MOBIRawml structure */
            mobi_free_rawml(rawml);
        }
        ...
        return ret;
    }
    

    代码比较长大部分都省略了,关键就在这两个if判断时根据dump_parts_opt值做了什么逻辑,然后就可以参考这部分代码自定义自己的解析方法,(是不是可以考虑将整个mobitool.c拷贝到项目,然后将main方法改造成可调用的方法?注:不修改是不行的,有main方法的话无法编译)

    Jstring2CStr方法

    char *Jstring2CStr(JNIEnv *env, jstring jstr) {
        char *rtn = NULL;
        jclass clsstring = env->FindClass("java/lang/String");
        jstring strencode = env->NewStringUTF("GB2312");
        jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
        jsize alen = env->GetArrayLength(barr);
        jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
        if (alen > 0) {
            rtn = (char *) malloc(alen + 1); //new char[alen+1];
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        env->ReleaseByteArrayElements(barr, ba, 0);
        return rtn;
    }
    

    相关文章

      网友评论

          本文标题:Android使用C语言库入门

          本文链接:https://www.haomeiwen.com/subject/pyahzltx.html