Android项目开发填坑记-so文件引发的攻坚战
故事的最初
我负责的项目A要求有播放在线视频的功能,当时从别人的聊天记录的一瞥中发现百度有相关的SDK,当时找到的是Baidu-T5Player-SDK-Android-1.4s
,项目中Demo的so
库只有armeabi-v7a
版的,由于需要使用的时候拷贝一些界面和图片资源到现有的项目中,所以我就索性直接打包成了aar
,经过一番修改就上线了。
Bug出现的那晚
播放在线视频的功能应要求被保留到新的一个项目B中,由于从开始一直都没有相关的需求出来,也就没有测试,毕竟项目A一直也在迭代更新,视频播放功能也一直很正常。
然后项目经理突发奇想(请允许我这样描述),要求我加上录视频和发视频的功能,我想这应该不难。很快录视频和上传搞定了,我欢喜的开始测试刚刚发布的视频,高潮来了:
点击视频播放,画面一闪而过(不敢相信):
//这里用<package-name>代表我的包名
W/System.err: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/<package-name>-1/base.apk"],nativeLibraryDirectories=[/data/app/<package-name>-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn't find "libcyberplayer-core.so"
W/System.err: at java.lang.Runtime.loadLibrary(Runtime.java:366)
W/System.err: at java.lang.System.loadLibrary(System.java:988)
W/System.err: at com.baidu.cyberplayer.core.CyberPlayerCore.a(SourceFile:288)
W/System.err: at com.baidu.cyberplayer.core.CyberPlayer.<init>(SourceFile:389)
W/System.err: at com.baidu.cyberplayer.core.b.<init>(SourceFile:169)
W/System.err: at com.baidu.cyberplayer.core.BVideoView$1.handleMessage(SourceFile:594)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err: at android.os.Looper.loop(Looper.java:135)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5295)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:910)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:705)
日志的意思是在项目的lib/arm64
里找不到libcyberplayer-core.so
文件了。
漫漫DeBug路
网上搜索了一会儿 能找到相关错误的信息不多:
搜索关键字:java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader
、couldn't find "libcyberplayer-core.so"
等等
尝试的解决方案:
1.在gradle
里指定包含的so文件
android {
...
packagingOptions {
exclude "libs/armeabi-v7a/libcyberplayer.so"
exclude "libs/armeabi-v7a/libcyberplayer-core.so"
}
}
2.在libs
里复制一份so文件:
在libs
下新建一个armeabi-v7a
,然后将libcyberplayer-core.so
和libcyberplayer.so
复制一份到该文件夹。如果这个不行,那么新建一个armeabi
文件夹再放入libcyberplayer-core.so
和libcyberplayer.so
就可以了。
3.在src/main/
里复制一份so文件:
在src/main/
里创建一个jniLibs
文件夹,然后再建一个armeabi-v7a
文件夹,最后把libcyberplayer-core.so
和libcyberplayer.so
复制一份到armeabi-v7a
文件夹中
其它的都类似就不一一举例了。在尝试的过程中我发现方案2
没任何作用,复制文件之后程序没有进行重新编译,但是方案3
会导致项目重新打包编译。
随着查的资料越来越多(三个小时),发现arm64
对应的文件夹是arm64-v8a
,既然方案3
起作用,那就结合一下试试:
src/main/jniLibs/
--armeabi-v7a/
----libcyberplayer-core.so
----libcyberplayer.so
--arm64-v8a/
----libcyberplayer-core.so
----libcyberplayer.so
怀着期待的心情我再次运行了程序:
arm64/libcyberplayer-core.so is 32bit not 64bit
顿时我意识到这so库根本没有编译64bit的版本啊。
然而,为什么项目A就没有这样的问题呢?
然而,为什么项目A就没有这样的问题呢?
然而,为什么项目A就没有这样的问题呢?
解决Bug的突破点
在大约三个半小时的搜索和尝试之后,我搜索到一个知识点:
安装包在只编译了armeabi,没有x86、arm64-v8a,是如何运行在各种处理器的手机上的?
https://www.zhihu.com/question/36893314/answer/69467752arm64-v8a是可以向下兼容的,但前提是你的项目里面没有arm64-v8a的文件夹,如果你有两个文件夹armeabi和arm64-v8a,两个文件夹,armeabi里面有a.so 和 b.so,arm64-v8a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有arm64-v8a的文件夹,发现里面没有b.so,就报错了,所以这个时候删掉arm64-v8a文件夹,这个时候手机发现没有适配arm64-v8a,就会直接去找armeabi的so库,所以要么你别加arm64-v8a,要么armeabi里面有的so库,arm64-v8a里面也必须有
于是我使用ROOT之后的手机,去/data/app/<package-name>-x
的lib
和安装包里证实了一下:
项目A中的lib/arm64/
里是空的,所以没有报错。
项目B中的lib/arm64/
里有libpl-droidsonroids_gif.so
和libpl-droidsonroids_gif_surface.so
两个文件。
也就是说,因为使用的某个第三方库里编译了arm64
的so库,导致没有编译相应arm64
的库无法正常使用了。
当时想的解决方案是自己编译Baidu-T5Player-SDK-Android-1.4s
的arm64
的so文件,虽然想着官方可能会有更新的版本里已经编译了,不过没有找到(github和旧的百度开发文档1.4还是最新版),最后在百度开发者论坛里有人说Baidu-T5Player-SDK-Android-1.10s
版本快出了,最终在最新的百度云开发者中新下载了最新版1.13的sdk,里面的确有arm64
的so文件,问题终于解决了。
总结和提醒
so库是旧版本兼容高版本(低版本的可以在高版本没有的情况下正常运行),也就是后兼容,前提是高版本的文件夹为空。
这里就要求我们在开发的过程中要特别注意使用的第三方库是否使用了jni
库,其编译了那些版本的so文件,一旦其中一个arm64
有so文件,就要求其他库也必须编译了arm64
的so文件。
一句话就是:要有大家都得有,否则都不能有。
整个Bug解决的过程,让我意识到,要多了解一下整个开发的相关基础知识点,就算现在还没用到,比如Jni
甚至ndk
的基础知识点。
Android项目开发填坑记-so文件引发的攻坚战的更多相关文章
- Android项目开发填坑记-Fragment的onBackPressed
Github版 CSDN版 知识背景 Fragment在当前的Android开发中,有两种引用方式,一个是 Android 3.0 时加入的,一个是supportV4包中的.这里简称为Fragment ...
- Android项目开发填坑记-Fragment的onAttach
背景 现在Android开发多使用一个Activity管理多个Fragment进行开发,不免需要两者相互传递数据,一般是给Fragment添加回调接口,让Activity继承并实现. 回调接口一般都写 ...
- Android项目开发填坑记-9patchPng报错
如果阅读体验不佳,请使用–> Github版 背景 之前写了一篇文章Android必知必会–NinePatch图片制作详细介绍了Android 9Patch图片的制作和一些Demo展示,这次说明 ...
- Java web 开发填坑记 2 -如何正确的创建一个Java Web 项目
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72566261 本文出自[赵彦军的博客] Java web 开发填坑记 1-如何正确 ...
- Java Web 开发填坑记- 如何正确的下载 Eclipse
一直以来,做 Java web 开发都是用 eclipse , 可是到 eclipse 官网一看,我的天 http://www.eclipse.org/downloads/eclipse-packag ...
- UiAutomator2.0升级填坑记
UiAutomator2.0升级填坑记 SkySeraph May. 28th 2017 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph个人站点:www.sk ...
- Appium+python自动化(十三)- 输入中文 - 一次填坑记(超详解)
简介 无论你在哪里,在做什么都会遇到很多坑,这些坑有些事别人挖的,有些是自己挖的.别人挖的叫坑人,自己挖的叫自杀,儿子挖的叫坑爹.因此在做app自动化道路上也不会是一帆风顺的,你会踩很多坑,这些坑和你 ...
- Android项目开发全程(四)-- 将网络返回的json字符串轻松转换成listview列表
前面几篇博文介绍了从项目搭建到获取网络字符串,对一个项目的前期整体工作进行了详细的介绍,本篇接着上篇介绍一下怎么样优雅将网络返回的json字符串轻松转换成listview列表. 先上图,看一下效果. ...
- Android项目开发全程(三)-- 项目的前期搭建、网络请求封装是怎样实现的
在前两篇博文中已经做了铺垫,下面咱们就可以用前面介绍过的内容开始做一个小项目了(项目中会用到Afinal框架,不会用Afinal的童鞋可以先看一下上一篇博文),正所谓麻雀虽小,五脏俱全,这在里我会尽量 ...
随机推荐
- ●BZOJ 4516 [Sdoi2016]生成魔咒
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题解: 把串反过来后,问题变为求每个后缀的互不相同的子串个数.首先用倍增算法求出 sa ...
- 2015 多校联赛 ——HDU5375(dp)
Sample Input 2 00?0 1 2 4 8 ???? 1 2 4 8 Sample Output Case #1: 12 Case #2: 15 ?部分可以是0 or 1,将二进制 ...
- Spring学习笔记6——注解方式测试
需要下载junit-4.12.jar和hamcrest-all-1.3.jar,将下载好的包导入到项目当中. 修改TestSpring, 并运行1. @RunWith(SpringJUnit4Clas ...
- glusterfs 4.0.1 rpc 分析笔记1
Jimmy的文档:Glusterfs的rpc模块分析 第一节.rpc服务器端实现原理及代码分析 第二节.rpc客户端实现原理及代码分析 第三节.rpc通信过程分析 经过阅读源码对比之前提及的文档,我个 ...
- Java8——快速入门手册(学习笔记)
github博文传送门 Java8特性学习笔记 Java8中新增了许多的新特性,在这里本人研究学习了几个较为常用的特性,在这里与大家进行分享.(这里推荐深入理解Java 8用于理解基础知识)本文分为以 ...
- 628. Maximum Product of Three Numbers
Given an integer array, find three numbers whose product is maximum and output the maximum product. ...
- python socket网络编程之粘包问题详解
一,粘包问题详情 1,只有TCP有粘包现象,UDP永远不会粘包 你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用 ...
- 匿名函数lambda
匿名函数的定义 在python中,匿名函数的定义如下: func =lambda x:x+1 #定义匿名函数,x为传参,x+1为返回值,func为函数名 res = func(10) #执行匿名函数 ...
- ACM Sudoku
Sudoku是一个非常简单的任务. 具有9行9列的方形表被划分为9个较小的正方形3x3,如图所示. 在一些单元格中写入从1到9的十进制数字.其他单元格为空. 目标是填充空单元格,其中十进制数字从1到9 ...
- Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制. error类型是一个接口类型,这是它的定义: type error interface { Error() string } 我们可以在编码 ...