有效果的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的代码相关部分本文并不给出,需要读者自行搜索了。希望此文对你有所帮助。
如有问题和错误的地方,欢迎留言提出!
原创文章,转载请附上

你可能感兴趣的:(有效果的Android编译so文件及使用)