Git Product home page Git Product logo

dyso's Introduction

dyso

so dynamically loading (dy so) android 动态加载so库实现 你所不知道的“船新”版本

关于demo

本项目是动态so加载的核心流程,所以并不直接演示so的下载过程,需要demo使用者将test_so_package的三个demo so库放到指定的演示位置(模拟so已经下载好了,可对接自己使用的下载方式),本例子是放在目录

data/data/com.example.sillyboy/files/dynamic_so

image

设计原理

文档请看这里:https://juejin.cn/post/7107958280097366030

使用说明

声明

注意:由于这是一个基础库,如果需要在正式项目中使用,还是需要继续封装或者优化的,这里只是动态加载so的实现,在实战中应当配合so版本管理,下载器等实际使用

使用步骤

  1. 如何删除本地的so库【可选】
方式1
packagingOptions下增加
exclude 'lib/arm64-v8a/xxx.so'
exclude 'lib/armeabi-v7a/xxx.so'
方式2
复制以下task到app的build.gradle

如果需要删除so的过程中进行一些列的定制化操作,可参考如下task,见app目录例子
ext {
    deleteSoName = ["libnativecpptwo.so","libnativecpp.so"]
}
// 这个是初始化 -配置 -执行阶段中,配置阶段执行的任务之一,完成afterEvaluate就可以得到所有的tasks,从而可以在里面插入我们定制化的数据
task(dynamicSo) {
}.doLast {
    println("dynamicSo insert!!!! ")
    //projectDir 在哪个project下面,projectDir就是哪个路径
    print(getRootProject().findAll())

    def file = new File("${projectDir}/build/intermediates/merged_native_libs/debug/out/lib")
    //默认删除所有的so库
    if (file.exists()) {
        file.listFiles().each {
            if (it.isDirectory()) {
                it.listFiles().each {
                    target ->
                        print("file ${target.name}")
                        def compareName = target.name
                        deleteSoName.each {
                            if (compareName.contains(it)) {
                                target.delete()
                            }
                        }
                }
            }
        }
    } else {
        print("nil")
    }
}
afterEvaluate {
    print("dynamicSo task start")
    def customer = tasks.findByName("dynamicSo")
    def merge = tasks.findByName("mergeDebugNativeLibs")
    def strip = tasks.findByName("stripDebugDebugSymbols")
    if (merge != null || strip != null) {
        customer.mustRunAfter(merge)
        strip.dependsOn(customer)
    }

}
  1. 初始化

通过initDynamicSoConfig 方法初始化,第一个参数是context,第二个参数是path,即下载完的so的path,第三个参数是一个回调,在调用动态so加载的时候会先回调,如果返回值为true,才会真正进入到so的加载逻辑,提供给使用者版本校验等一些列活动

    // 在合适的时候将自定义路径插入so检索路径 需要使用者自己负责在这个路径上有写入权限
        DynamicSoLauncher.INSTANCE.initDynamicSoConfig(this, path, s -> {
            // 处理一些自定义逻辑
            return true;
        });
  1. 调用so加载【有两种使用姿势】

3.1 手动加载

在使用到某个so方法前,需要调用loadSoDynamically(代替System.loadLibrary方法),参数是so的名称或者一个File,该File位于初始化时传入的path之内即可

DynamicSoLauncher.INSTANCE.loadSoDynamically(file)

3.2 注解加载

在需要采取动态so加载的类上,添加@DynamicLoad注解即可,内部会采用字节码插桩的方式,会把类中所有的System.loadLibrary 替换为DynamicSoLauncher内部的so加载

//@DynamicLoad
public class MainActivity extends AppCompatActivity {

注意,执行这个步骤需要发布当前的lib_sillyplugin插件 image 之后在需要的工程build.gradle引入即可,跟一般的插件引入方式一样

apply plugin: 'com.plugins.core'

ps:这里其实已经可以实现了无痕替代,只是考虑到大多数可能只动态加载少数的so,才提供出来注解的方式去限定范围。

项目层级介绍

  • app下是使用例子
  • lib_sillyboy 是dyso的封装实现
  • lib_sillyplugin 是dyso的插件,用于替换代码的System.loadLibrary,默认关闭了,可看代码注释打开

环境准备

建议直接用最新的稳定版本Android Studio打开工程。目前项目已适配Android Studio Arctic Fox | 2020.3.1

后续todo

  • maven发布
  • 贡献者名单

dyso's People

Contributors

testplanb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

dyso's Issues

找不到so

我是直接把jniLibs的so删了 运行 然后手动放app外部私有目录里,点击一个按钮加载so
错误信息是ib/arm64-v8a, /system/lib64, /system/product/lib64]]] couldn't find "libxxxx.so"😂 我debug看了 传的文件是存在的

has invalid shdr offset/size

"java.lang.UnsatisfiedLinkError: dlopen failed: ""/data/data/com.example.demo/files/dynamic_so/libVECoreFFmpeg.so"" has invalid shdr offset/size: 20189888/1344
at java.lang.Runtime.loadLibrary0(Runtime.java:1077)
at java.lang.Runtime.loadLibrary0(Runtime.java:998)
at java.lang.System.loadLibrary(System.java:1657)
at YHlnQn.tDgmK.XBLtLhuy(DynamicSo.java:195)
at YHlnQn.tDgmK.XBLtLhuy(DynamicSo.java:105)
at YHlnQn.tDgmK.XBLtLhuy(DynamicSo.java:105)
at com.pika.sillyboy.DynamicSoLauncher.loadSoDynamically(DynamicSoLauncher.kt:3)

从远程服务器上下载 so,手动加载后会出现以上问题

java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.exemple.myapplication/files/dynamic_so/libtest.so" has unexpected e_version: 65725

在用demo尝试的时候,发生了这个错误

java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.exemple.myapplication/files/dynamic_so/libtest.so" has unexpected e_version: 65725
at java.lang.Runtime.loadLibrary0(Runtime.java:1071)
at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
at java.lang.System.loadLibrary(System.java:1668)
at com.exemple.myapplication.TestSo.(TestSo.kt:41)
at com.exemple.myapplication.MainActivity.onCreate$lambda-3(MainActivity.kt:62)
at com.exemple.myapplication.MainActivity.$r8$lambda$04hyejgvbGZzKFahH40V5n53yRg(Unknown Source:0)
at com.exemple.myapplication.MainActivity$$ExternalSyntheticLambda0.onClick(Unknown Source:2)

打印classLoader的时候,确认自定义的solib已经插入nativeLibraryDirectories

dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/cn.lgh.myapplication-8GvN7C-0ytdB_y8C5cgMpw==/base.apk"],nativeLibraryDirectories=[/data/user/0/com.exemple.myapplication/files/dynamic_so, /data/app/com.exemple.myapplication-8GvN7C-0ytdB_y8C5cgMpw==/lib/arm64, /data/app/com.exemple.myapplication-8GvN7C-0ytdB_y8C5cgMpw==/base.apk!/lib/arm64-v8a, /system/lib64, /hw_product/lib64, /system/product/lib64]]]

同时要加载的so库也已经放到/data/user/0/com.exemple.myapplication/files/dynamic_so目录下

求解

cannot locate symbol "__cxa_finalize" referenced by "/system/lib64/libdl.so"

2022-08-29 15:46:34.714 7719-7719/com.liwei.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.liwei.myapplication, PID: 7719
java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__cxa_finalize" referenced by "/system/lib64/libdl.so"...
at java.lang.Runtime.loadLibrary(Runtime.java:372)
at java.lang.System.loadLibrary(System.java:1076)
at com.liwei.myapplication.lib_sillyboy.DynamicSo.loadStaticSo(DynamicSo.java:31)
at com.liwei.myapplication.MainActivity.onCreate(MainActivity.java:32)
at android.app.Activity.performCreate(Activity.java:6289)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2405)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2535)
at android.app.ActivityThread.access$900(ActivityThread.java:155)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1380)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:152)
at android.app.ActivityThread.main(ActivityThread.java:5497)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

load报错
直接使用System.load(path)是正常的

java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib64/libdl.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"

前置条件:
1、系统版本Android7.0、已Root
2、Demo代码未修改逻辑,只添加了几行日志
3、运行前已导入so [libnativecpp.so、libnativecpptwo.so]到此目录: /data/user/0/com.example.nativecpp/files/dynamic_so/
4、
arm-linux-androideabi-readelf -d libnativecpptwo.so

Dynamic section at offset 0xdc8 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libnativecpp.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libnativecpptwo.so]

5、$ /data/user/0/com.example.nativecpp/files/dynamic_so # ls -al
-rwxrwxrwx 1 root root 1414352 2023-03-01 11:48 libnativecpp.so
-rwxrwxrwx 1 root root 60120 2023-03-01 11:48 libnativecpptwo.so

运行日志如下:

2023-03-01 14:38:46.899 5737-5737/com.example.nativecpp I/hello: path /data/user/0/com.example.nativecpp/files/dynamic_so/
2023-03-01 14:38:46.905 5737-5737/com.example.nativecpp I/hello: dependencies: [libnativecpp.so, liblog.so, libm.so, libdl.so, libc.so]
2023-03-01 14:38:46.909 5737-5737/com.example.nativecpp I/hello: dependencies: [liblog.so, libm.so, libdl.so, libc.so]
2023-03-01 14:38:46.909 5737-5737/com.example.nativecpp I/hello: dependencySo: log
2023-03-01 14:38:46.910 5737-5737/com.example.nativecpp I/hello: dependencySo: m
2023-03-01 14:38:46.911 5737-5737/com.example.nativecpp I/hello: dependencySo: dl
2023-03-01 14:38:46.913 5737-5737/com.example.nativecpp E/linker: library "/system/lib64/libdl.so" ("/system/lib64/libdl.so") needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="", permitted_paths="/data:/mnt/expand:/data/data/com.example.nativecpp"]
2023-03-01 14:38:46.914 5737-5737/com.example.nativecpp D/AndroidRuntime: Shutting down VM
2023-03-01 14:38:46.914 5737-5737/com.example.nativecpp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.nativecpp, PID: 5737
java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib64/libdl.so" needed or dlopened by "/system/lib64/libnativeloader.so" is not accessible for the namespace "classloader-namespace"
at java.lang.Runtime.loadLibrary0(Runtime.java:977)
at java.lang.System.loadLibrary(System.java:1530)
at com.example.lib_sillyboy.DynamicSo.loadStaticSo(DynamicSo.java:45)
at com.example.lib_sillyboy.DynamicSo.loadStaticSo(DynamicSo.java:38)
at com.example.nativecpp.MainActivity.onCreate(MainActivity.java:33)
at android.app.Activity.performCreate(Activity.java:6666)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2677)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6251)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)

添加日志代码:
public class DynamicSo {
public static void loadStaticSo(File soFIle, String path) {
try {
ElfParser parser = null;
List dependencies = null;
try {
parser = new ElfParser(soFIle);
dependencies = parser.parseNeededDependencies();
} catch (Throwable e) {
throw e;
} finally {
if (parser != null) {
parser.close();
}
}
Log.i("hello", "dependencies: " + dependencies);
//如果nativecpp3->nativecpptwo->nativecpp 则先加载 DynamicSo.loadStaticSo(nativecpptwo),此时nativecpp作为nativecpptwo的直接依赖被加载了
//不能直接加载nativecpp3,导致加载直接依赖nativetwo的时候nativecpp没加载导致错误。 这个可以优化,比如递归
for (final String dependency : dependencies) {

            try {
                File file = new File(path + dependency);
                if (file.exists()) {
                    //递归查找
                    loadStaticSo(file, path);
                } else {
                    // so文件不存在这个文件夹,代表是ndk中的so,如liblog.so,则直接加载
                    // 把本来lib前缀和.so后缀去掉即可
                    String dependencySo = dependency.substring(3, dependency.length() - 3);
                    //在application已经注入了路径DynamicSo.insertPathToNativeSystem(this,file) 所以采用系统的加载就行
                    Log.i("hello", "dependencySo: " + dependencySo);
                    System.loadLibrary(dependencySo);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    } catch (IOException ignored) {
    }

关于删除apk内so的操作建议

packagingOptions下增加
exclude 'lib/arm64-v8a/xxx.so'
exclude 'lib/armeabi-v7a/xxx.so'
即可在打包后自动移除对应的so,无需增加task

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.