App极限瘦身 | 动态下发so(2)

简介: App极限瘦身 | 动态下发so

遇到的问题

问题一

N 开始情况就不一样了:libxxx.so 能正常加载,而 liblog.so 会出现加载失败错误

E/ExceptionHandler: Uncaught Exception java.lang.UnsatisfiedLinkError: dlopen failed: library "liblog.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:xxx)
at java.lang.System.loadLibrary(System.java:xxx)
问题一原因分析

Android P 以后,Linker 里检索的路径在创建 ClassLoader 实例后就被系统通过 Namespace 机制绑定了,当我们注入新的路径之后,虽然 ClassLoader 里的路径增加了,但是 Linker 里 Namespace 已经绑定的路径集合并没有同步更新

问题一解决方案

完全自己控制 so 文件的检索逻辑

ARM 手机主动检测 so,进入核心的 activity 时,开启延时检测,先停掉下载,减少对页面加载的影响,x 秒后重新下载

    public void checkArmSoDelayWhenCreate(int delayMillis) {
        if (localSoStatus.isDownloading) {
            ThreadManager.getBackgroundPool().execute(this::pauseDownloadTask);
        }
        weakHandler.removeCallbacks(startCheckRunnable);
        weakHandler.postDelayed(startCheckRunnable, delayMillis);
    }

放在单独的线程中检测 so 是否完整

    private void checkSoLibsInBackThread() {
        weakHandler.removeCallbacks(startCheckRunnable);
        final ThreadManager.ThreadPoolProxy singlePool = ThreadManager.getSinglePool("so-download");
        // 避免产生重复的检测任务。
        singlePool.remove(checkSoRunnable);
        singlePool.execute(checkSoRunnable);
    }

接下来我们详细了解一下具体的检测逻辑吧

zip 文件存在,则校验是否合法,md5 校验

  String soZipPath = soFileDownloader.getSoZipFilePath(SOURCE_MD5);
        final boolean allSoFilesExist = isAllSoFilesExist(soZipPath);
        //统计触发检测时,不存在so的情况
        StatisticsForSoLoader.sendSoFilesNotExist(allSoFilesExist);
        boolean hasInstalledSoPath = soFileDownloader.hasInstalledSoPath();
        localSoStatus.hasInstalledSoPath = hasInstalledSoPath;
        final boolean isPrepared = allSoFilesExist && hasInstalledSoPath;

完整解压,不完整删除缓存,重新下载

      localSoStatus.isPrepared = isPrepared;
       Log.d(TAG, "handleSoBackground isPrepared=" + isPrepared);
       if (isPrepared) { // 一切就绪,回调出去,ok
           if (soLoaderListener != null) {
               soLoaderListener.prepared(true);
           } else { // 回调出去继续执行
               MKWeexSoLoader.reloadWeexSoLib(this::notifyCallback);
           }
           return;
       }
           private void startDownload(SoLoaderListener soLoaderListener, String soZipPath) {
               //pauseDownloadTask();//每次下载前暂停上次任务,防止so读写出现问题
               String soUrl = getServerUrl();
               soFileDownloader.downloadSoFile(soUrl, soZipPath, soLoaderListener);
           }

是否存在 soNameList 里面指定的 so 文件

       for (File currentFile : currentFiles) {
            final String currentFileName = currentFile.getName();
            // so库,size>0,且是预先定义的合法so,统计so个数
            final boolean contains = allSoNameList.contains(currentFileName);
            if (currentFileName.endsWith(".so") && currentFile.length() > 0 && contains) {
                localSoFileCount++;
            }
        }
        // 如果本地下载目录中的so文件总数目,少于应该有的so文件数目,说明不完整
        localSoStatus.isAllSoFilesExist = localSoFileCount >= allSoNameList.size();
        return localSoStatus.isAllSoFilesExist;

问题二:将相关加载代码挪出静态代码块

so 动态化改造之后,如果项目后续开发中有人不小心在 so 插件尚未安装完成之前引用了相关的 JNI 类,则在改造成动态化的时候,最好将相关加载代码挪出静态代码块,并且增加 so 加载失败时候的 onFail 逻辑

  • 如果是 X86 的手机,初始化 x86 平台的 so 文件名列表
问题二:将相关加载代码挪出静态代码块
so 动态化改造之后,如果项目后续开发中有人不小心在 so 插件尚未安装完成之前引用了相关的 JNI 类,则在改造成动态化的时候,最好将相关加载代码挪出静态代码块,并且增加 so 加载失败时候的 onFail 逻辑
如果是 X86 的手机,初始化 x86 平台的 so 文件名列表
作者:小木箱
链接:https://juejin.cn/post/6897095308157648910
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 如果是 armeabi-v7a 的手机,初始化 armeabi-v7a 平台的 so 文件名列表
else {
            SOURCE_MD5 = MD5_ARMEABI_V7A;
            initArmSoFileNameList();
        }
private void initArmSoFileNameList() {
                if (allSoNameList != null) {
                    addAgoraSoLibs();
                    addWeexSolibs();
                }
            }
    private void addAgoraSoLibs() {
        allSoNameList.add("xxxxxx.so");
       ```
       ```
    }
private void addWeexSolibs() {//weex 核心库,x86 arm都需要下发,不需要的不要乱加入
           allSoNameList.add("xxxxxx.so");
        ```
        ```
}
  • 检测 so 库是否准备好了。ARM 手机只有声网相关业务和 weex 的创建,需要检测 so
  • 其他不需要;x86 手机则无论如何需要检测
    public void checkSoLibReady(Context context, boolean isNeedCheckWhenArm, CheckSoCallback callback) {
        if (isX86Phone && isNeedCheckWhenArm) {//如果是x86手机,arm需要检测的地方无视
            doCallback(callback);
            return;
        }
        if (!isX86Phone && !isNeedCheckWhenArm) {//arm手机,无需检测则无视
            doCallback(callback);
            return;
        }
        this.mCallback = callback;
        boolean doCheck = doCheck(context);
        if (doCheck) {//直接callback回去
            doCallback(callback);
        }
    }

然后再把检测 so 的回调传给业务层处理

public interface CheckSoCallback {
    void prepared();
}

问题三: Google Play Store 动态代码禁用问题

包含有动态代码的 APK 包是无法上传到 Play Store 的,可以向 APK 客户端下发绑定版本的“一个主资源包 + 一个 patch 包”,体积上限个 1G。so 动态化和版本绑定非,一旦发布就无法修改

问题四: 部分 ROM 机型删了 Build.VERSION.PREVIEW_SDK_INT 属性,导致无法获取 SDK 版本信息

   @TargetApi(Build.VERSION_CODES.M)
    private static int getPreviousSdkInt() {
        try {
            return Build.VERSION.PREVIEW_SDK_INT;
        } catch (Throwable ignore) {
        }
        return 1;
    }

总结

实际项目中,so 动态下发遇到的坑比较多,熟悉系统加载 so 库的工作流程和反射流程。才能解决动态化过程中的安全性问题,版本控制问题,abi 兼容性判断和 System#load 加载代码侵入问题。当然理论是基石,线上打点分析 so 状态和网络状态才能保证我们应用在线上的稳定性。关于 App 瘦身可以聊的东西太多,如果本篇文章阅读量超过 2000,下一节写一下关于 png 转 webpng 自动化转化教程,满足大家对 App 瘦身的好奇心。

相关文章
|
9天前
|
存储 Java API
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
52 0
|
5月前
uniapp App端 解决input@input事件动态修改值不生效的问题
uniapp App端 解决input@input事件动态修改值不生效的问题
99 1
|
5月前
|
XML Java Android开发
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
42 0
|
6月前
uniapp App端 解决input@input事件动态修改值不生效的问题
uniapp App端 解决input@input事件动态修改值不生效的问题
73 0
|
8月前
App逆向百例|18|某A系防护SO跳转修复
App逆向百例|18|某A系防护SO跳转修复
271 0
|
10月前
|
双11 Android开发
Android动态来改变App桌面图标
其实对于这样的一个桌面图标更换,Android中为我们提供了AndroidManifest.xml里的<activity-alias>标签实现方式。
225 0
|
11月前
|
存储 缓存 监控
毕业设计So Easy:Java实现手机APP安全卫士
很多计算机专业大学生经常和我交流:毕业设计没思路、不会做、论文不会写、太难了...... 针对这些问题,决定分享一些软、硬件项目的设计思路和实施方法,希望可以帮助大家,也祝愿各位学子,顺利毕业!
|
11月前
|
存储 文字识别 JavaScript
毕业设计So Easy:Java MySQL智能报纸阅读器APP应用
很多计算机专业大学生经常和我交流:毕业设计没思路、不会做、论文不会写、太难了...... 针对这些问题,决定分享一些软、硬件项目的设计思路和实施方法,希望可以帮助大家,也祝愿各位学子,顺利毕业!
|
11月前
|
机器学习/深度学习 数据采集 NoSQL
毕业设计So Easy:卷积神经网络实现中药材识别系统APP
很多计算机专业大学生经常和我交流:毕业设计没思路、不会做、论文不会写、太难了...... 针对这些问题,决定分享一些软、硬件项目的设计思路和实施方法,希望可以帮助大家,也祝愿各位学子,顺利毕业!
|
存储 缓存 安全
App极限瘦身 | 动态下发so(1)
App极限瘦身 | 动态下发so
235 0
App极限瘦身 | 动态下发so(1)