有效果的Android编译so文件及使用

0.前言

根据不成文的规则,这里还是要废话几句的。
看标题,我特意加上了“有效果”三字,没错,今天我来给大伙填坑了。
因公司业务需求,与第三方商家合作,需要调用商家提供的so库,对此知识一无所知的我踏上了一条踩坑之路。

1.导入so库

废话不多说,直接切入正题。
网上的方法,只要新建jni包然后将so库放入其中即可使用。但是,本文说的是编译,如果你要编译一个so库,这个库又需要引用本地so库,这个方法是不行的

2.编译so库

编译so库的方法主要分为mk编译和cmake编译这两种方式。

(1)mk方式编译独立的so库

先来看看mk编译的方式。
首先要在gradle.properties文件里面添加

android.useDeprecatedNdk=true

然后在build之中添加代码:

android{
` ` `
sourceSets {
        main{
            jni.srcDirs=[]; //禁用as自动生成mk
            jniLibs.srcDirs = ['src/main/jniLibs']//设置目标的so存放路径
        }
    }
 //设置编译任务,编译ndkBuild
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn 'ndkBuild'
    }
` ` `
}
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
    //ndk路径,一定要加上这部分
    commandLine "C:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle\\ndk-build.cmd",
            'NDK_PROJECT_PATH=build/intermediates/ndk',
            'NDK_LIBS_OUT=src/main/jniLibs',
            'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
            'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}

然后新建一个jni目录(相信大伙都懂,这里就不罗嗦怎么建立),在jni目录下添加Android.mk和Application.mk这两个文件,编写代码如下

  • Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := dp
LOCAL_SRC_FILES := dp.cpp
LOCAL_LDLIBS :=-llog
include $(BUILD_SHARED_LIBRARY)

dp即自己编写的cpp文件,位于jni目录下,名字可以随意取,但要对得上。
然在Application.mk文件里写明要编译的平台。比如

#模拟器是 x86_64 的
APP_ABI := x86_64

这里的x86_64指编译出x86_64平台的so库,如果选择all,即编译出所有的平台的so库。
然后运行一下,即可编译出对应的libdp.so库文件,放在['src/main/jniLibs']目录下。
这里特别指出重点:

task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
    //ndk路径,一定要加上这部分
    commandLine "C:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle\\ndk-build.cmd",
            'NDK_PROJECT_PATH=build/intermediates/ndk',
            'NDK_LIBS_OUT=src/main/jniLibs',
            'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
            'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}

这段代码一定要加上去,不如编译不过。博主曾在网上搜到过片段代码与此相似,奈何其博主没有详细说明也没有完整说明,无法理解。此处给出完整例子希望有所帮助。(至于dp.cpp的代码,先不给出,这不是本文重点)

(2)mk方式引入第三方so库

为了便于说明,这里给第三方库取个名,就叫sb库好了,完整名是libsb.so

导入第三方so库,必须要注意他支持的平台,需要与你的Application.mk中设置的相对应(如果对不上,改为相对应的),否则会编译不通过的(重点,小编在这问题上被坑得死死的,希望读者能吸取小编的教训)。
接下来讲重点:

  • 1.将libsb.so库放在jni目录下,然后重新编写Android.mk文件代码:
LOCAL_PATH := $(call my-dir)
#引入第三方 so
include $(CLEAR_VARS)
LOCAL_MODULE    := sb
LOCAL_SRC_FILES := sb.so
LOCAL_EXPORT_C_INCLUDES := include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := dp
LOCAL_SRC_FILES := dp.cpp
LOCAL_LDLIBS :=-llog

#引入第三方编译模块
LOCAL_SHARED_LIBRARIES := \sb
include $(BUILD_SHARED_LIBRARY)

这样一来(只有这样),你就可以在dp.cpp代码里面编写调用libsb.so库里面的方法了。如果你只是单纯把它放到jniLibs目录下,那是不行的。

  • 2.你还是得老老实实的把它放到jniLibs目录下,因为你的业务代码里面要用到它必然需要把它导进去,需要用到哪些库就放到jniLibs目录下,别忘了在这些so的上一层加上他的平台文件包
    如此一来,再run一下就没问题了。
(3)cmake方式编译

使用cmake方式编译,那就真的是太即便简便太简单了。我这么说读者可能会不服气,眼见为实吧。

  • 1.编写cpp文件
    正常操作,编写cpp文件,相信这一步不是问题,这里就取名为mb.cpp吧,此处就放在main/cpp/目录下好了。然后再编写cmake的代码(语法在你新建支持c++文件的项目时产生的cmakeLists.txt中有介绍)
    这里直接给出代码:
add_library(mb SHARED src/main/cpp/mb.cpp)
target_link_libraries( # Specifies the target library
                        mb                  
                       ${log-lib} )

先用add_library将mb添加进去,再在target_link_libraries里关联即可。
如果你的mb.cpp文件里面include了其他头文件,你还需加上

include_directories(scr/main/cpp/include/ )

声明头文件的部分,否则会编译失败找不到头文件的(坑)。
注意:习惯使用mk编译的童鞋这里注意了,使用cmake的方式编译是不会生成so文件的(坑),所以你不用去找libmb.so文件了=-=。
但是你还是要在你的代码里面导入库时把它倒进去:

System.loadLibrary("mb");
(4)cmake引入第三方so库

这里就给第三方so取名为nb好了(具体的当然要与读者的相同啦),完整名字libnb.so。
不废话,上代码:

#导入第三方so包
#IMPORTED 指只是把 so 导入到项目中
add_library( nb
             SHARED
             IMPORTED )
#指明 so 库的路径
set_target_properties( # Specifies the target library.
            nb
            # Specifies the parameter you want to define.
            PROPERTIES IMPORTED_LOCATION
            # Provides the path to the library you want to import.
       ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libvvw.so )

#头文件路径
include_directories(scr/main/cpp/include/ )
add_library(mb SHARED src/main/cpp/mb.cpp)
target_link_libraries( # Specifies the target library.
                        mb
                        #关联第三方 so
                        nb
                       ${log-lib} )

ANDROID_ABI指平台,同样,你需要将你的第三方so库放入到可识别的路径文件下(这里放在jniLibs目录下),上一层需加上平台的文件夹,比如/jniLibs/x86/libnb.so这样子,如果你这里选ANDROID_ABI,则会默认编译你在build中设定的平台,比如在build中加入:

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"
    defaultConfig {
        applicationId "com.zhengsr.jnidemo_camke"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-frtti -fexceptions"
            }
        }
        ndk{
            abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'//重点
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

}

重点看:

ndk{
            abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'//重点
        }

那他就会默认编译 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'平台的第三方so,如果你在这里使用了ANDROID_ABI,而你的jniLibs路径下恰好没有以上全部平台的libnb.so库,则会编译失败,这时你就需要将ANDROID_ABI改为你所拥有的平台或补充相应的so文件。

说到底,这一步,路径很重要,必须配置正确,否则会有一堆问题,然后网上一堆无关说法、解决方法等你去踩坑.

通过以上配置,就可以愉快的引用你的so库了,想怎么用就怎么用,不怕报在cpp文件里面找不到库里面的方法(某方法未声明)的错误

总结还是得要有的

3.总结

不管是mk编译方法还是cmake编译方法,都是可取的方法。但是相较而言,cmake编译的方法明显胜于mk编译的方法,不用生成so库文件即可使用。
二者的使用方法上有共通之处,需要注意的是路径一定要配置正确
由于cpp的文件书写以及utils的代码相关部分本文并不给出,需要读者自行搜索了。希望此文对你有所帮助。
如有问题和错误的地方,欢迎留言提出!
原创文章,转载请附上

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