不可能,绝对不可能,minSdkVersion设置23,App咋就崩溃了?

现象

在Android6.0手机上,打开反向传屏的瞬间就崩溃了,哎…最不让人省心的6.0

崩溃日志:

Caused by: java.lang.IllegalArgumentException: Unable to find native library: cast
at android.app.NativeActivity.onCreate(NativeActivity.java:170)
at com.hikvision.frame.view.MvpNativeActivity.onCreate(MvpNativeActivity.kt:25)
at com.hikvision.frame.act.BaseMvpNativeActivity.onCreate(BaseMvpNativeActivity.kt:24)
at com.hikvision.cast.reverse.view.act.ReverseCastActivity.onCreate(ReverseCastActivity.kt:69)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)

崩溃直接原因:so没找到

现象分析

  1. libcast.so在启动时就加载且成功了,那说明正常的System.loadLibrary是没问题的
  2. 具体是NativeActivity.onCreate()出异常,那肯定是NativeActivity里的实现有问题。
    最可疑的地方在:NativeActivity.loadNativeCode(path)

题外话:为什么需要使用NativeActivity?
NativeActivity是用C/C++开发Android的方案。在实现反向传屏时,可以直接在C++层采集触控事件,实现反向控制,减少不必要的反射调用(频率还挺高的),降低CPU和延时。

回归正题,继续分析:
看一下loadNativeCode的参数:

mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(), getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()), getAbsolutePath(getExternalFilesDir(null)), Build.VERSION.SDK_INT, getAssets(), nativeSavedState);

先看第一个参数path:

File libraryFile = new File(ai.applicationInfo.nativeLibraryDir,
                System.mapLibraryName(libname));
if (libraryFile.exists()) {
    path = libraryFile.getPath();
}

非常可疑啊,这小东西,nativeLibraryDir,打印一下看看:
1.11-15 17:53:08.574 19968-19968/com.hikvision.cast.android.debug D/Cast.J.NativeLoad: native find dir = /data/app/com.hikvision.cast.android.debug-2/lib/arm

设置不同的minSdkVersion,看一下安装后的目录结构:
【minSdkVersion不为23】
minSdkVersion不为23
【minSdkVersion为23】minSdkVersion为23
这不就真相大白了,NativeActivity的loadNativeCode传入了个非正确的加载native库path,但又不能去掉NativeActivity(虽然后面兼容到5.0了,可以不用解决这个问题),但还是继续挖掘下根本原因好了。

根因挖掘

导致的直接原因是:ActivityInfo.ApplicationInfo.nativeLibraryDir,可以Hook替换NativeActivity,但风险太大不考虑。

那!必须看看nativeLibraryDir是怎么赋值的了。

顺便一提:
1.System.loadLibrary正常的原因是,它加载的是一个目录列表。
2.使用System.load也需注意,之前为了方便和SDK以及研究院调试,降低跨组和跨部门开发的成本,实现链接库的动态加载,也是经历过从System.load方案到System.loadLibrary的演进(前者是拷贝和重定向,后者是Hook),后续有机会再介绍。

继续分析原因
从ApplicationInfo跟踪到LoadedApk,最后跟踪到PackageParser.parseApkLite():

extractNativeLibs = attrs.getAttributeBooleanValue(i, true)

所以,是AndroidManifest.xml里的配置决定的,查看了下编译出来的application节点,确实是:android:extractNativeLibs=”false”。
minSdkVersion为23的manifest
明明没有设置extractNativeLibs,为什么打包出来的manifest不对?

真相只有一个,Android Studio偷偷加的呗

查一下Android开发者文档:

1.android:extractNativeLibs
2.Whether or not the package installer extracts native libraries from the APK to the filesystem. If set to “false”, then your native libraries must be page aligned and stored uncompressed in the APK. Although your APK might be larger, your application should load faster because the libraries are directly loaded from the APK at runtime. On the other hand, if set to “true”, native libraries in the APK can be compressed. During installation, the installer decompresses the libraries, and the linker loads the decompressed libraries at runtime; in this case, the APK would be smaller, but installation time might be slightly longer.
3.The default value is “true” if extractNativeLibs is not configured in AndroidManifest.xml. However, when building your app using Android Gradle plugin 3.6.0 or higher, this property is reset to “false” if it is NOT configured in AndroidManifest.xml; so if your native libraries in the APK are compressed, you must explicitly set it to “true” in AndroidManifest.xml.

大致意思是:
如果extractNativeLibs为false时,应用的so文件不解压而且页面对齐;如果设置为true时,系统安装服务会把so文件解压到系统目录。extractNativeLibs默认值是true,但是在使用Android Gradle plugin 3.6.0 及以上,没有配置extractNativeLibs时,会把此属性重置为false。

【摘自Android minSdkVersion大于等于23时,应用找不到so文件

当然还一个隐藏条件,minSdkVersion为23以上。

题外话
android:extractNativeLibs=false:安装时不需要解压,安装速度比较快
android:extractNativeLibs=true:安装变慢,但so会进行压缩,apk大小减小

另外解压后,如果在代码里使用nativeLibraryDir来加载也没问题,像NativeActivity出现的这个问题,本质上其实就是Google的代码写挫了嘛。

举一反三

所以代码里如果有使用到 ActivityInfo.ApplicationInfo.nativeLibraryDir的需要注意版本兼容问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值