Android使用C语言库入门

使用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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容