Android 5.0 到 Android 6.0 + 的深坑之一 之 .so 动态库的适配

(原创:http://www.cnblogs.com/linguanh)html

 

目录:程序员

  前序golang

  一,问题描述编程

  二,为什么会如此"无情"?api

  三,目前存在该问题的知名SDK函数

  四,解决方案,1 对 N工具

 

前序:学习

  嫌无聊的请跳过。上次发博文是同年8月,时光荏苒,空闲时间少,如今都接近年关了,其实这4个月学了不少,接触了IM(非第三方),学习了 golang 并采用它成功完成仿朋友圈页面的服务端api,等等..等等..,因为这个问题的确是业界超级"毒瘤",趁午休时间,尽我程序员的"干货"思惟,少废话,尽通俗。spa

    码字发文。操作系统

 

一,问题描述

  先简单阐述下几个概念,这些不是重点。

  1,Android 编译时候的 api 版本,指的是你要生成的这个 apk 所依赖的 sdk 版本,例如 api 23 便是 Android 6.0 ;

 

  2,.so 动态连接库,为 Linux 下的库文件,Windows 是 .dll,Android 是基于 Linux 内核的,因此使用的是 .so,在安卓上面,通常由 C/C++ 语言进行 Jni 编程后,采用 NDK 工具编译后所生成的,能够参考下我以前的一篇教程博文:http://www.cnblogs.com/linguanh/p/4624768.html

 

  3,.so的做用,主要是提供系统底层函数,供应用层使用。不用它行不?能够,在Android已经提供了的状况下,你不须要再本身添加,例如一个 View 的绘制,里面都有不少 Native 关键词的函数,这个就是底层函数,Android api 对应的是它已经提供了。那么若是,你老板要去实现,语音,图像,视频处理等系统没有的功能,你就只能本身写 .so 来供调用了。

  问题来了:

    发生的环境:此类问题通常发生在 Android 6.0 及其以上的系统,具体也存在于其余的 api 版本,主要集中在 api >=23;

    具体表现是:同一个 APP 在 api <=22 的 sdk 状况下编译,能够运行正常,不存在闪退或者 .so 库加载失败的状况,当你采用 api >=23 的sdk 编译的时候,安装到 Android 6.0 及其以上的手机的时候,大范围出现崩溃 或者 .so 库加载失败,而在 6.0 如下的手机却正常;

    Catch的信息:dlopen failed: cannot locate symbol "XXXX" xxxx.so, XX 是泛配,此类崩溃信息,你彻底能够对号入我"座"。

 

二,为什么如此无情?

   若是只为解决问题,能够不看这部分。

  如今我用一句话说白它,就是:不一样连接方式时,dlopen会打开指定的系统中(手机中)提供的动态库,并使用 dlsym 获取符号地址,也就是说,若是,在此时的手机中若是找不到,那么就会出问题,通常和 API 有关系。

  人为因素就是,编译这个 .so 库的人,他在编译的时候没考虑到下面这些状况,致使提供给别人用的时候,或者本身用的时候在高 API 版本手机出现问题。

  感兴趣的就接着看下面详解吧!上面问题描述的第二点提到 .so 是运行在 Linux 环境下的,并且在 Android 里面通常由 NDK 编译,编译的时候,咱们能够指明一种文件叫作 Application.mk,里面有一行 APP_STL := XXX 指明库的连接方式,默认是静态,STL的取值:

    1)system,默认的值,最危险方式,直接和手机系统版本挂钩,采用手机最小版本的.so库连接

    2)gabi++_static

    3)gabi++_shared

    4)stlport_static

    5)stlport_shared

    6)gnustl_static

    7)gnustl_shared

  若是不特别定义的话,“system”运行时库是默认的值。除此以外,凡是后面带“_static”的,表示其是一个静态连接的运行时库(运行时库的代码包含在编译后的程序中);而凡是后面带“_shared”的,表示其是一个动态连接的运行时库(运行时库在程序运行时被动态加载进来)。若是去除动态或静态连接的因素,则除了默认的“system”运行时库以外,还有所谓的“gabi++”运行时库、“stlport”运行时库和“gunstl”运行时库。若是想支持C++异常的话,必需要使用gunstl运行时库。

  主要是两种,静态连接动态连接

    动态连接,是指在生成可执行文件时不将全部程序用到的函数连接到一个文件,由于有许多函数在操做系统带的dll文件中,当程序运行时直接从操做系统中找。

    静态连接,是把全部用到的函数所有连接到 .so 文件中;

  重点来了,上面说到了,静态连接是会把所须要的都搞到exe中,其实否则,这个说法是早期的了,对于如今的 Android 发展来讲,为了使程序方便扩展,具有通用性,已经采用插件形式来连接动态库,编译时的静态和动态连接仅仅是程度问题。插件加载形式有:

    1)dlopen

    2)dlsym

    3)dlclose

  dlopen打开指定的系统中(手机中)动态库。并使用 dlsym 获取符号地址,也就是说,若是,在此时的手机中若是找不到,那么就会出问题,通常和 API 有关系。

 

三,目前存在该问题的知名SDK

  根据我所了解到的,存在这类问题的 SDK 有,百度地图、环信、高德地图、语音库 speex, 不知道修复没有,这些 SDK 一但在你的 APK 编译版中中设置 API >=23 就会出现各类问题,闪退或者抛出异常。

  

四,解决方案,1 对 N

  主要有两种:

    1- 委曲求全,指标不治本,把你的 APK target API 先下降到 23如下,若不行再把 编译时 API 下降到 23 如下,还出问题就继续下降,这意味着,你不少 Android Sdk 的新控件用不了;

    2- 在 Application.mk 中修改 APP_STL,从新编译 .so ,若是,我说若是你没有源码,那么悲剧了,要么等他们解决,要么采用第一种,建议尝试,APP_STL := gnustl_shared,

  这种方式,对于所须要的外部动态连接函数、符号,在 NDK 13b 中都会独立生成一份,所有引用就解决此类问题,例如

1 private void load() {
2       try {
3           System.loadLibrary("gnustl_shared"); // 也能够不写这一句,可是要保证 gnustl_shared.so 放置在 libs 里面
4           System.loadLibrary("speex");
5       } catch (Throwable e) {
6           Log.d("zzzzz","加载语音库异常 :"+e.toString());
7       }
8 }

    3- 若是须要 libgnustl_shared.so 的,留邮箱,我发一份你。

 

  至此,基本讲完了,下期 开源二次开发的IM 服务端系统。

相关文章
相关标签/搜索