转-NDK SO 库开发与使用中的 ABI 构架选择;NDK和项目依赖

http://www.cnblogs.com/Bugtags2015/p/5578541.html

 

NDK SO 库开发与使用中的 ABI 构架选择

Bugtags V1.2.7 引入了 NDK SO 库,在集成的时候,遇到不同的 SO 库打包到 APK 时,安装在某些机器上,出现 java.lang.UnsatisfiedLinkError 加载失败。

为此,深究了一下原理,和给出了解决方案。

原理

Android 系统本质是一个经过改造的 Linux 系统。最早,Android 系统只支持 ARMv5 的 CPU 构架,随着 Android 系统的发展,又加入了 ARMv7 (2010), x86 (2011), MIPS (2012), ARMv8, MIPS64 和 x86_64 (2014)。

每一种 CPU 构架,都定义了一种 ABI(Application Binary Interface),ABI 决定了二进制文件如何与系统进行交互。

一般情况下,你不需要关注这些。当你的 APP 中用到了些包含 SO 库第三方库,或者自己使用 NDK 来实现了某些功能,你就需要认真阅读接下来的教程。

NDK SO 支持不同的 CPU 构架

在使用 NDK 开发包含 c/c++ 代码的 SO 库的时候,你可以选择输出支持如下 ABI CPU 构架:

armeabi
armeabi­v7a
arm64­v8a
x86
x86_64
mips
mips64

Bugtags 的 NDK 库支持如上所有的 CPU 构架:

但不是所有人的开发者提供的 NDK 库都支持所有的 CPU 构架:

上面的这个开发者提供的库,就只支持 armeabi。

其实一般情况下,是没有问题的,x86 的设备,也会兼容 armeabi 的 SO 库。

合并打包到 APK 中

如果不做任何设置,Android 的构建系统会把这些来自不同开发者的 SO 库都合并在一起,打进 APK 压缩包中。

├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│       └── libBugtags.so
├── res

系统安装 APK

根据官方 ndk-abi 文档, Android 系统在安装一个 APK 的时候,会考虑当前的设备的 CPU 构架和配置(称为所谓的 primary-abi 和 secondary-abi),去该 APK 文件的对应文件夹去寻找 SO 库。

假设当前设备是 x86 机器,会优先去 lib/x86 文件夹下寻找 SO 库:

lib/<primary-abi>/lib<name>.so

如果找不到,同时定义了 secondary-abi,则去如下文件夹寻找:

lib/<secondary-abi>/lib<name>.so

如果找到了,就将文件拷贝到 APK 的安装目录的如下文件夹中:

 /lib/lib<name>.so

找不到对应的 SO,安装正常,但是当这个 SO 在运行时被使用时,会崩溃。

问题来了

可能你已经发现问题了,当一个 APK 是这种情况:

├── AndroidManifest.xml
├── classes.dex
├── lib
│   ├── arm64-v8a
│   │   └── libBugtags.so
│   ├── armeabi
│   │   ├── libhyphenate.so
│   │   └── libBugtags.so
│   ├── armeabi-v7a
│   │   └── libBugtags.so
│   ├── mips
│   │   └── libBugtags.so
│   ├── mips64
│   │   └── libBugtags.so
│   ├── x86
│   │   └── libBugtags.so
│   └── x86_64
│       └── libBugtags.so
├── res

同时 APK 被安装到一个 x86 的设备上的时候,以上的寻找过程,将会失败,运行时,将出现如下报错:

D/xxx   (10674): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx-2/base.apk"],nativeLibraryDirectories=[/data/app/xxx-2/lib/x86, /vendor/lib, /system/lib]]] couldn't find "libirdna_sdk.so"
D/xxx   (10674):     at java.lang.Runtime.loadLibrary(Runtime.java:366)

此处,笔者有点费解,既然在 x86 文件夹中找不到,应该去 armeabi 文件夹中自动寻找啊,此处留一个 TODO,需要接下来去确认是否是某些机器的原因。

解决方案

准则

NDK SO 开发者应该遵循一个准则:支持所有的平台,否则将会搞砸你的用户。

NDK SO 使用者应该遵循一个准则:要么支持所有平台,要么都不支持。

然而,事与愿违,因为种种原因(遗留 SO、芯片市场占有率、APK 包大小等),并不是所有人都遵循这样的原则。

折中方案

Android Studio

  • Android Gradle 插件中,可以使用如下方式对 abi 进行过滤:
android {
    ...

    defaultConfig {    
        ...
        ndk {
            // 设置支持的 SO 库构架,注意这里要根据你的实际情况来设置
            abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
        }
    }

}

关键行:

abiFilters 'armeabi'// 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'

根据你的 APP 中使用的 SO 库所支持的构架具体情况,你可以进行具体设置。最终输出的 apk 中,将会包含你所选择的 abi。

像前面举出的例子,就应该只允许 armeabi。

  • 如果在添加 “abiFilter” 之后 Android Studio 出现以下提示:
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin

则在项目根目录的 gradle.properties 文件中添加:

    android.useDeprecatedNdk=true

Eclipse

Eclipse 中,你需要手动控制你的工程中的这个文件夹里面的内容:

以达到上述的原则,使得在不同的构架的设备上运转正常。

参考文献

What you should know about .so files

关于Android的.so文件你所需要知道的)

ABI Management

 

http://www.cnblogs.com/wisemen/p/5821209.html

 

NDK和项目依赖

NDK使用

  • JDK:Java Development Kit
  • SDK:Software Development Kit
  • NDK:Native Development Kit
  • JNI: Java Nattive Interface

NDK开发流程:

  • 下载NDK并配置NDK路径
  • 新建项目
  • 修改build.gradle文件

    替换classpath:com.android.tools.build:gradle-experimental:0.4.0

    替换apply plugin:com.android.model.application

    添加moudel{}至android{},即:model{android{}}minSdkverion 15 targetSdkVersion 23

      versionCode 1versionName "1.o"
      替换为
      minSdkVersion.apiLevel=4
      targetSdkVersion.apilevel=23
  • 添加android.ndk{moduleName=”onlystar-android-jni-demo”}
  • 参考实例Demo的build.gradle
apply plugin: 'com.android.model.application'

    model {
    android {
    compileSdkVersion = 23
    buildToolsVersion = "23.0.2"

    defaultConfig.with {
    applicationId = "com.example.hellojni"
    minSdkVersion.apiLevel = 15
    targetSdkVersion.apiLevel = 23
    }
    }

    compileOptions.with {
    sourceCompatibility=JavaVersion.VERSION_1_7
    targetCompatibility=JavaVersion.VERSION_1_7
    }

/*
 * native build settings
 */
    android.ndk {
    moduleName = "geekband-hello-jni-demo"/*
     * Other ndk flags configurable here are
     * cppFlags.add("-fno-rtti")
     * cppFlags.add("-fno-exceptions")
     * ldLibs.addAll(["android", "log"])
     * stl       = "system"
     */
    }
    android.buildTypes {
    release {
    minifyEnabled = false
    proguardFiles.add(file('proguard-rules.txt'))
    }
    }
    android.productFlavors {
    // for detailed abiFilter descriptions, refer to "Supported ABIs" @ // https://developer.android.com/ndk/guides/abis.html#sacreate("arm") {
    ndk.abiFilters.add("armeabi")
    }
    create("arm7") {
    ndk.abiFilters.add("armeabi-v7a")
    }
    create("arm8") {
    ndk.abiFilters.add("arm64-v8a")
    }
    create("x86") {
    ndk.abiFilters.add("x86")
    }
    create("x86-64") {
    ndk.abiFilters.add("x86_64")
    }
    create("mips") {
    ndk.abiFilters.add("mips")
    }
    create("mips-64") {
    ndk.abiFilters.add("mips64")
    }
    // To include all cpu architectures, leaves abiFilters emptycreate("all")
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
    }

项目依赖

引入aar文件—-两种方法
1.在build.gradle中添加
allprogects{
    responsitores{
        jcenter()
        flatDir{
            dirs  'libs'
       }
   }
}
 在dependencies中添加
     compile(name:‘aar_filename’,ext:‘aar’)
2.新建module,选择 Import .JAR/.AAR Package,导入aar文件