Android使用C語言庫入門

使用Android Studio開發(fā)時使用C庫來實現(xiàn)某些功能绢慢,例如需要使用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中會自動創(chuàng)建對應的方法丰辣,如下


image.png

TODO位置就可以寫c++代碼
使用libmobi庫請直接看第三部分

二撒强、在新建項目中添加

File->new->new Project禽捆,選擇Native C++,然后next->finish


image.png

這樣就直接完成了飘哨,IDE會直接幫你創(chuàng)建一個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格式文件的方法浊服,方法的具體代碼比較多就不發(fā)了,這個方法的主要邏輯是從libmobi的tools文件夾下的mobitool.c中的loadfilename(const char *fullpath)方法中拷貝的胚吁,具體的解析方法可以看mobitool.c的main(int argc, char *argv[])方法牙躺,例如main方法有如下的介紹:

image.png

mobitool.c本身就是一個命令行工具,每個參數(shù)都有自己的作用腕扶,根據(jù)自己的需求找到對應參數(shù)在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半抱,最后執(zhí)行了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判斷時根據(jù)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;
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棉磨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子学辱,更是在濱河造成了極大的恐慌乘瓤,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件策泣,死亡現(xiàn)場離奇詭異衙傀,居然都是意外死亡,警方通過查閱死者的電腦和手機萨咕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門统抬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人危队,你說我怎么就攤上這事聪建。” “怎么了茫陆?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵金麸,是天一觀的道長。 經(jīng)常有香客問我簿盅,道長挥下,這世上最難降的妖魔是什么揍魂? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮棚瘟,結果婚禮上现斋,老公的妹妹穿的比我還像新娘。我一直安慰自己解取,他們只是感情好步责,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禀苦,像睡著了一般蔓肯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上振乏,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天蔗包,我揣著相機與錄音,去河邊找鬼慧邮。 笑死调限,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的误澳。 我是一名探鬼主播耻矮,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忆谓!你這毒婦竟也來了裆装?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤倡缠,失蹤者是張志新(化名)和其女友劉穎哨免,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昙沦,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡琢唾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盾饮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片采桃。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丐谋,靈堂內(nèi)的尸體忽然破棺而出芍碧,到底是詐尸還是另有隱情,我是刑警寧澤号俐,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布泌豆,位于F島的核電站,受9級特大地震影響吏饿,放射性物質發(fā)生泄漏踪危。R本人自食惡果不足惜蔬浙,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贞远。 院中可真熱鬧畴博,春花似錦、人聲如沸蓝仲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袱结。三九已至亮隙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垢夹,已是汗流浹背溢吻。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留果元,地道東北人促王。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像而晒,于是被迫代替她去往敵國和親蝇狼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容