Repository: TestPlanB/SillyBoy Branch: master Commit: 08f4d5773130 Files: 68 Total size: 99.6 KB Directory structure: gitextract_mqv2qagf/ ├── .gitignore ├── .idea/ │ ├── .gitignore │ ├── compiler.xml │ ├── gradle.xml │ ├── misc.xml │ └── vcs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── nativecpp/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── cpp/ │ │ │ ├── CMakeLists.txt │ │ │ ├── nativeso1.c │ │ │ ├── nativeso2.c │ │ │ └── nativeso3.c │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ └── nativecpp/ │ │ │ ├── CustomApplication.java │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ └── ic_launcher_background.xml │ │ ├── drawable-v24/ │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ └── values-night/ │ │ └── themes.xml │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── nativecpp/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── lib_sillyboy/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── lib_sillyboy/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── pika/ │ │ └── sillyboy/ │ │ ├── DynamicLoad.kt │ │ ├── DynamicSo.java │ │ ├── DynamicSoLauncher.kt │ │ ├── elf/ │ │ │ ├── Dynamic32Structure.java │ │ │ ├── Dynamic64Structure.java │ │ │ ├── Elf.java │ │ │ ├── Elf32Header.java │ │ │ ├── Elf64Header.java │ │ │ ├── ElfParser.java │ │ │ ├── Program32Header.java │ │ │ ├── Program64Header.java │ │ │ ├── Section32Header.java │ │ │ └── Section64Header.java │ │ └── pathinsert/ │ │ ├── LoadLibraryUtils.java │ │ └── ShareReflectUtil.java │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── lib_sillyboy/ │ └── ExampleUnitTest.java ├── lib_sillyplugin/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── example/ │ │ └── lib_sillyplugin/ │ │ └── ExampleInstrumentedTest.kt │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── groovy/ │ │ │ └── com.plugin.core/ │ │ │ ├── DynamicPlugin.groovy │ │ │ └── DynamicTransform.groovy │ │ ├── java/ │ │ │ └── com/ │ │ │ └── plugin/ │ │ │ └── helper/ │ │ │ └── SystemLoadHelper.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── gradle-plugins/ │ │ └── com.plugins.core.properties │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── lib_sillyplugin/ │ └── ExampleUnitTest.kt └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx local.properties ================================================ FILE: .idea/.gitignore ================================================ # Default ignored files /shelf/ /workspace.xml ================================================ FILE: .idea/compiler.xml ================================================ ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/misc.xml ================================================ ================================================ FILE: .idea/vcs.xml ================================================ ================================================ FILE: README.md ================================================ # 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://user-images.githubusercontent.com/65278264/235083927-1a23914d-44da-493f-88ab-1b3dfa99326d.png) ## 设计原理 文档请看这里: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) } } ``` 2. 初始化 通过initDynamicSoConfig 方法初始化,第一个参数是context,第二个参数是path,即下载完的so的path,第三个参数是一个回调,在调用动态so加载的时候会先回调,如果返回值为true,才会真正进入到so的加载逻辑,提供给使用者版本校验等一些列活动 ``` // 在合适的时候将自定义路径插入so检索路径 需要使用者自己负责在这个路径上有写入权限 DynamicSoLauncher.INSTANCE.initDynamicSoConfig(this, path, s -> { // 处理一些自定义逻辑 return true; }); ``` 3. 调用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](https://user-images.githubusercontent.com/65278264/235086729-fa92051d-4282-4ffe-9817-d04de2587711.png) 之后在需要的工程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发布** * **贡献者名单** ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' // 替换System.loadLibrary的插件 //apply plugin: 'com.plugins.core' android { compileSdkVersion 31 packagingOptions { exclude 'META-INF/DEPENDENCIES' exclude 'migrateToAndroidx/migration.xml' exclude 'META-INF/common.kotlin_module' } defaultConfig { applicationId "com.example.sillyboy" minSdkVersion 23 targetSdkVersion 29 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } viewBinding { enabled = true } } ext { deleteSoName = ["libnative1.so","libnative2.so","libnative3.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 -> def compareName = target.name deleteSoName.each { if (compareName.contains(it)) { target.delete() print("delete file ${target.name}") } } } } } } 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) } } dependencies { implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.core:core-ktx:1.5.0' implementation project(path: ':lib_sillyboy') } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/androidTest/java/com/example/nativecpp/ExampleInstrumentedTest.java ================================================ package com.example.nativecpp; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.example.nativecpp", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/cpp/CMakeLists.txt ================================================ # For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.18.1) # Declares and names the project. project("nativecpp") # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( native1 SHARED nativeso1.c ) add_library( native2 SHARED nativeso2.c ) add_library( native3 SHARED nativeso3.c ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. # 构建多依赖场景 target_link_libraries( # Specifies the target library. native1 # Links the target library to the log library # included in the NDK. ${log-lib}) target_link_libraries( # Specifies the target library. native2 # Links the target library to the log library # included in the NDK. ${log-lib} native1) target_link_libraries( # Specifies the target library. native3 # Links the target library to the log library # included in the NDK. ${log-lib} native2) ================================================ FILE: app/src/main/cpp/nativeso1.c ================================================ #include #include JNIEXPORT void JNICALL Java_com_example_nativecpp_MainActivity_clickNative1(JNIEnv *env, jobject thiz) { __android_log_print(ANDROID_LOG_ERROR, "hello", "%s", "native1"); } ================================================ FILE: app/src/main/cpp/nativeso2.c ================================================ #include #include JNIEXPORT void JNICALL Java_com_example_nativecpp_MainActivity_clickNative2(JNIEnv *env, jobject thiz) { __android_log_print(ANDROID_LOG_ERROR, "hello", "%s", "native2"); } ================================================ FILE: app/src/main/cpp/nativeso3.c ================================================ #include #include // // Created by chenhailiang on 2023/4/27. // JNIEXPORT void JNICALL Java_com_example_nativecpp_MainActivity_clickNative3(JNIEnv *env, jobject thiz) { __android_log_print(ANDROID_LOG_ERROR, "hello", "%s", "native3"); } ================================================ FILE: app/src/main/java/com/example/nativecpp/CustomApplication.java ================================================ package com.example.nativecpp; import android.app.Application; import android.util.Log; import com.pika.sillyboy.DynamicSoLauncher; import java.io.File; import kotlin.jvm.functions.Function1; public class CustomApplication extends Application { @Override public void onCreate() { super.onCreate(); // data/data/com.example.sillyboy/files/dynamic_so String path = getFilesDir().getAbsolutePath() + "/dynamic_so/"; File file = new File(path); if (!file.exists()) { file.mkdir(); } // 在合适的时候将自定义路径插入so检索路径 需要使用者自己负责在这个路径上有写入权限 DynamicSoLauncher.INSTANCE.initDynamicSoConfig(this, path, s -> { // 处理一些自定义逻辑 return true; }); } } ================================================ FILE: app/src/main/java/com/example/nativecpp/MainActivity.java ================================================ package com.example.nativecpp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import com.example.nativecpp.databinding.ActivityMainBinding; import com.pika.sillyboy.DynamicLoad; import com.pika.sillyboy.DynamicSoLauncher; import java.io.File; // 把这个注解删掉,System.loadLibrary就会走正常的流程,否则就会走插桩流程 //@DynamicLoad public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String path = getFilesDir().getAbsolutePath() + "/dynamic_so/"; Log.i("hello", "path:" + path); File file = new File(path + "libnative3.so"); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); // 正常的流程System.loadLibrary binding.loadNormal.setOnClickListener(v -> System.loadLibrary("native3")); // 调用动态so库 binding.loadDynamic.setOnClickListener(v -> DynamicSoLauncher.INSTANCE.loadSoDynamically(file)); binding.native1.setOnClickListener(v -> clickNative1()); binding.native2.setOnClickListener(v -> clickNative2()); binding.native3.setOnClickListener(v -> clickNative3()); } public native void clickNative1(); public native void clickNative2(); public native void clickNative3(); } ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================