├── spi-api
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── SPI.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── spi-app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── fragment_main.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── drawable
│ │ │ │ ├── selector_green_round_5dp.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── afirez
│ │ │ │ └── spi
│ │ │ │ └── app
│ │ │ │ ├── PrivateUtil.java
│ │ │ │ ├── App.java
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── spi-applike
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── applike
│ │ │ ├── AppLike.java
│ │ │ ├── App.java
│ │ │ └── AppDelegate.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── applike
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── applike
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── spi-plugin
├── .gitignore
├── src
│ └── main
│ │ ├── resources
│ │ └── META-INF
│ │ │ └── gradle-plugins
│ │ │ ├── spi.properties
│ │ │ └── com.afirez.spi.properties
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ ├── SpiExtension.kt
│ │ ├── SpiContext.kt
│ │ ├── SpiPlugin.kt
│ │ └── weave
│ │ ├── SpiWeaveCodeClassVisitor.kt
│ │ └── SpiScanClassVisitor.kt
└── build.gradle
├── knight-transform
├── .gitignore
├── src
│ └── main
│ │ ├── resources
│ │ └── META-INF
│ │ │ └── gradle-plugins
│ │ │ └── com.knight.transform.properties
│ │ └── kotlin
│ │ └── com
│ │ └── knight
│ │ └── transform
│ │ ├── BaseExtension.kt
│ │ ├── MemberEntity.kt
│ │ ├── Interceptor
│ │ ├── IClassVIsitorInterceptor.kt
│ │ ├── ClassVisitorChain.kt
│ │ └── impl
│ │ │ └── BaseClassVisitorInterceptor.kt
│ │ ├── Utils
│ │ ├── Timer.kt
│ │ ├── PrintAllTaskUtil.kt
│ │ ├── TypeUtil.kt
│ │ └── ASMUtils.kt
│ │ ├── task
│ │ ├── WeavedClass.kt
│ │ └── OutPutMappingTask.kt
│ │ ├── IPlugin.kt
│ │ ├── asm
│ │ ├── IWeaver.kt
│ │ ├── ScanWeaver.kt
│ │ └── CodeWeaver.kt
│ │ ├── weave
│ │ ├── BaseClassVisitor.kt
│ │ └── ExtendClassWriter.kt
│ │ ├── transform
│ │ ├── TransformContext.kt
│ │ └── Transform.kt
│ │ ├── BaseContext.kt
│ │ ├── KnightTaskPlugin.kt
│ │ ├── KnightTransform.kt
│ │ └── KnightPlugin.kt
└── build.gradle
├── spi-component1
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component1
│ │ │ ├── SpiJavaProvider.java
│ │ │ └── SpiRouter1Provider.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component1
│ │ │ └── api
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── component1
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── spi-component2
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component2
│ │ │ ├── SpiKotlinProvider.kt
│ │ │ └── SpiRouter2Provider.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component2
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── component2
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── spi-plugin-booster
├── .gitignore
├── src
│ └── main
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ ├── dummy
│ │ └── TestPrivateTransformer.kt
│ │ ├── SpiSupervisor.kt
│ │ └── SpiTransformer.kt
└── build.gradle
├── spi-component1-impl
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ ├── activity_spi_fragment.xml
│ │ │ │ └── activity_spi.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component1
│ │ │ └── impl
│ │ │ ├── App.kt
│ │ │ ├── SpiJavaProviderImpl.java
│ │ │ ├── SpiActivity.java
│ │ │ ├── SpiRouter1ProviderImpl.java
│ │ │ └── SpiFragmentActivity.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component1
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── component1
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── spi-component2-impl
├── .gitignore
├── src
│ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ └── fragment_spi.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component2
│ │ │ └── impl
│ │ │ ├── SpiKotlinProviderImpl.kt
│ │ │ ├── App.kt
│ │ │ ├── SpiRouter2ProviderImpl.kt
│ │ │ └── SpiFragment.kt
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── afirez
│ │ │ └── spi
│ │ │ └── component2
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── afirez
│ │ └── spi
│ │ └── component2
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── app-afirez.jks
├── repo
└── com
│ ├── afirez
│ ├── spi
│ │ ├── spi
│ │ │ ├── maven-metadata.xml.md5
│ │ │ ├── 1.0.0
│ │ │ │ ├── spi-1.0.0.aar.md5
│ │ │ │ ├── spi-1.0.0.pom.md5
│ │ │ │ ├── spi-1.0.0.aar.sha1
│ │ │ │ ├── spi-1.0.0.pom.sha1
│ │ │ │ ├── spi-1.0.0.aar
│ │ │ │ └── spi-1.0.0.pom
│ │ │ ├── 1.0.1
│ │ │ │ ├── spi-1.0.1.aar.md5
│ │ │ │ ├── spi-1.0.1.pom.md5
│ │ │ │ ├── spi-1.0.1-javadoc.jar.md5
│ │ │ │ ├── spi-1.0.1-sources.jar.md5
│ │ │ │ ├── spi-1.0.1.aar.sha1
│ │ │ │ ├── spi-1.0.1.pom.sha1
│ │ │ │ ├── spi-1.0.1-javadoc.jar.sha1
│ │ │ │ ├── spi-1.0.1-sources.jar.sha1
│ │ │ │ ├── spi-1.0.1.aar
│ │ │ │ ├── spi-1.0.1-javadoc.jar
│ │ │ │ ├── spi-1.0.1-sources.jar
│ │ │ │ └── spi-1.0.1.pom
│ │ │ ├── maven-metadata.xml.sha1
│ │ │ └── maven-metadata.xml
│ │ ├── spi-app-lib1
│ │ │ ├── maven-metadata.xml.md5
│ │ │ ├── 1.0.1
│ │ │ │ ├── spi-app-lib1-1.0.1.aar.md5
│ │ │ │ ├── spi-app-lib1-1.0.1.pom.md5
│ │ │ │ ├── spi-app-lib1-1.0.1.aar.sha1
│ │ │ │ ├── spi-app-lib1-1.0.1.pom.sha1
│ │ │ │ ├── spi-app-lib1-1.0.1.aar
│ │ │ │ └── spi-app-lib1-1.0.1.pom
│ │ │ ├── maven-metadata.xml.sha1
│ │ │ └── maven-metadata.xml
│ │ └── spi-gradle-plugin
│ │ │ ├── maven-metadata.xml.md5
│ │ │ ├── maven-metadata.xml.sha1
│ │ │ ├── 1.0.0
│ │ │ ├── spi-gradle-plugin-1.0.0.jar.md5
│ │ │ ├── spi-gradle-plugin-1.0.0.pom.md5
│ │ │ ├── spi-gradle-plugin-1.0.0.jar.sha1
│ │ │ ├── spi-gradle-plugin-1.0.0.pom.sha1
│ │ │ ├── spi-gradle-plugin-1.0.0.jar
│ │ │ └── spi-gradle-plugin-1.0.0.pom
│ │ │ ├── 1.0.1
│ │ │ ├── spi-gradle-plugin-1.0.1.jar.md5
│ │ │ ├── spi-gradle-plugin-1.0.1.pom.md5
│ │ │ ├── spi-gradle-plugin-1.0.1-javadoc.jar.md5
│ │ │ ├── spi-gradle-plugin-1.0.1-sources.jar.md5
│ │ │ ├── spi-gradle-plugin-1.0.1.jar.sha1
│ │ │ ├── spi-gradle-plugin-1.0.1.pom.sha1
│ │ │ ├── spi-gradle-plugin-1.0.1-javadoc.jar.sha1
│ │ │ ├── spi-gradle-plugin-1.0.1-sources.jar.sha1
│ │ │ ├── spi-gradle-plugin-1.0.1.jar
│ │ │ ├── spi-gradle-plugin-1.0.1-javadoc.jar
│ │ │ ├── spi-gradle-plugin-1.0.1-sources.jar
│ │ │ └── spi-gradle-plugin-1.0.1.pom
│ │ │ ├── 2.0.0
│ │ │ ├── spi-gradle-plugin-2.0.0.jar.md5
│ │ │ ├── spi-gradle-plugin-2.0.0.pom.md5
│ │ │ ├── spi-gradle-plugin-2.0.0-javadoc.jar.md5
│ │ │ ├── spi-gradle-plugin-2.0.0-sources.jar.md5
│ │ │ ├── spi-gradle-plugin-2.0.0.jar.sha1
│ │ │ ├── spi-gradle-plugin-2.0.0.pom.sha1
│ │ │ ├── spi-gradle-plugin-2.0.0-javadoc.jar.sha1
│ │ │ ├── spi-gradle-plugin-2.0.0-sources.jar.sha1
│ │ │ ├── spi-gradle-plugin-2.0.0.jar
│ │ │ ├── spi-gradle-plugin-2.0.0-javadoc.jar
│ │ │ ├── spi-gradle-plugin-2.0.0-sources.jar
│ │ │ └── spi-gradle-plugin-2.0.0.pom
│ │ │ ├── 3.0.0
│ │ │ ├── spi-gradle-plugin-3.0.0.jar.md5
│ │ │ ├── spi-gradle-plugin-3.0.0.pom.md5
│ │ │ ├── spi-gradle-plugin-3.0.0-javadoc.jar.md5
│ │ │ ├── spi-gradle-plugin-3.0.0-sources.jar.md5
│ │ │ ├── spi-gradle-plugin-3.0.0.jar.sha1
│ │ │ ├── spi-gradle-plugin-3.0.0.pom.sha1
│ │ │ ├── spi-gradle-plugin-3.0.0-javadoc.jar.sha1
│ │ │ ├── spi-gradle-plugin-3.0.0-sources.jar.sha1
│ │ │ ├── spi-gradle-plugin-3.0.0.jar
│ │ │ ├── spi-gradle-plugin-3.0.0-javadoc.jar
│ │ │ ├── spi-gradle-plugin-3.0.0-sources.jar
│ │ │ └── spi-gradle-plugin-3.0.0.pom
│ │ │ └── maven-metadata.xml
│ └── applike
│ │ └── applike
│ │ ├── maven-metadata.xml.md5
│ │ ├── 1.0.0
│ │ ├── applike-1.0.0.aar.md5
│ │ ├── applike-1.0.0.pom.md5
│ │ ├── applike-1.0.0.aar.sha1
│ │ ├── applike-1.0.0.pom.sha1
│ │ ├── applike-1.0.0.aar
│ │ └── applike-1.0.0.pom
│ │ ├── 1.0.1
│ │ ├── applike-1.0.1.aar.md5
│ │ ├── applike-1.0.1.pom.md5
│ │ ├── applike-1.0.1-javadoc.jar.md5
│ │ ├── applike-1.0.1-sources.jar.md5
│ │ ├── applike-1.0.1.aar.sha1
│ │ ├── applike-1.0.1.pom.sha1
│ │ ├── applike-1.0.1-javadoc.jar.sha1
│ │ ├── applike-1.0.1-sources.jar.sha1
│ │ ├── applike-1.0.1.aar
│ │ ├── applike-1.0.1-javadoc.jar
│ │ ├── applike-1.0.1-sources.jar
│ │ └── applike-1.0.1.pom
│ │ ├── maven-metadata.xml.sha1
│ │ └── maven-metadata.xml
│ └── knight
│ └── transform
│ ├── maven-metadata.xml.md5
│ ├── 2.0.0
│ ├── transform-2.0.0.jar.md5
│ ├── transform-2.0.0.pom.md5
│ ├── transform-2.0.0-javadoc.jar.md5
│ ├── transform-2.0.0-sources.jar.md5
│ ├── transform-2.0.0.jar.sha1
│ ├── transform-2.0.0.pom.sha1
│ ├── transform-2.0.0-javadoc.jar.sha1
│ ├── transform-2.0.0-sources.jar.sha1
│ ├── transform-2.0.0.jar
│ ├── transform-2.0.0-javadoc.jar
│ ├── transform-2.0.0-sources.jar
│ └── transform-2.0.0.pom
│ ├── 1.0.10
│ ├── transform-1.0.10.jar.md5
│ ├── transform-1.0.10.pom.md5
│ ├── transform-1.0.10-javadoc.jar.md5
│ ├── transform-1.0.10-sources.jar.md5
│ ├── transform-1.0.10.jar.sha1
│ ├── transform-1.0.10.pom.sha1
│ ├── transform-1.0.10-javadoc.jar.sha1
│ ├── transform-1.0.10-sources.jar.sha1
│ ├── transform-1.0.10.jar
│ ├── transform-1.0.10-javadoc.jar
│ ├── transform-1.0.10-sources.jar
│ └── transform-1.0.10.pom
│ ├── maven-metadata.xml.sha1
│ └── maven-metadata.xml
├── assets
├── 2019-06-01-15-54-28.png
├── 2019-06-01-16-04-00.png
├── 2019-06-01-16-16-26.png
├── 2019-06-01-16-24-31.png
├── 2019-06-01-16-41-42.png
├── 2019-06-01-16-59-26.png
├── 2019-06-01-17-04-39.png
└── 2019-06-01-21-16-27.png
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── pom-evaluator.gradle
└── pkg-src.gradle
├── settings.gradle
├── .gitignore
├── .travis.yml
├── gradle.properties
├── gradlew.bat
└── README_CN.md
/spi-api/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-applike/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-plugin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/knight-transform/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-component1/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-component2/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-plugin-booster/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/spi-component1-impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spi-component2-impl/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app-afirez.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/app-afirez.jks
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | e7187f0bd4acf3a4d2cd0ae1c7bbc118
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.aar.md5:
--------------------------------------------------------------------------------
1 | 09b1693d0be0c1063d894ccb39925fd3
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 9a25bbfd1c1ac1d3e2df8a734ac135eb
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.aar.md5:
--------------------------------------------------------------------------------
1 | e6bee482dac6d5bbbd185f2aae462f1a
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.pom.md5:
--------------------------------------------------------------------------------
1 | bf3db2f07f9a5f9f9d0e7a8cea2e3d8f
--------------------------------------------------------------------------------
/repo/com/knight/transform/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | ab1d120e808f7d993582e70f68d572c0
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 7b2fc88c7a72a46abccb222625fd9aff
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 4e6892fa9d79e87034c9a302faa1260d
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.aar.sha1:
--------------------------------------------------------------------------------
1 | 20cdbd46633e5cf09b18d2734e1d06b8c08cafed
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | efa646fb92ac86006d50f3e98f1c23c4d3bd4ac1
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | c284fd3b2f2f5b411afb184b60fef5fd
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-sources.jar.md5:
--------------------------------------------------------------------------------
1 | 18dad9b9fed3bcb734886c857b976348
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.aar.sha1:
--------------------------------------------------------------------------------
1 | 41613d865522d5c93c022b0cb9e0731c87437e71
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.pom.sha1:
--------------------------------------------------------------------------------
1 | d7efd845dd257e8930fb4c155daab07f57ef09d8
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | 8bc752c07e085e2f58a2bcd01eab25e068fa0044
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | 2cecdca4ef6992b06900247c027e9224
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 1db53efd306abe0ad7f32797179b38a1
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.aar.md5:
--------------------------------------------------------------------------------
1 | 0979ad92c011bd4c6a57dcf33eca0ca9
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | bceec30a3683b3db58e2ede3fc0f98a3
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.aar.md5:
--------------------------------------------------------------------------------
1 | 7a6d17189d6a08697e1bcccda9b4ae5f
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.pom.md5:
--------------------------------------------------------------------------------
1 | 703622f383bf2349f390999543c64ee7
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/maven-metadata.xml.md5:
--------------------------------------------------------------------------------
1 | 3cc7369ac318ae7805c8c02419c98465
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.jar.md5:
--------------------------------------------------------------------------------
1 | fcd3034a549962749c535eb004774a05
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.pom.md5:
--------------------------------------------------------------------------------
1 | 9b84cd6a23d7e23b2040c0263330436f
--------------------------------------------------------------------------------
/repo/com/knight/transform/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | c8452399537f1f900eb4c950d29b634a43d3c845
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | f6e57fabeb5856668ee95cd3a651943d6d472acf
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.aar.md5:
--------------------------------------------------------------------------------
1 | f18d99e114a48e8917e599d0a4f50cbc
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.pom.md5:
--------------------------------------------------------------------------------
1 | b7389c2ce9a62c32a7f41c691b86b4c3
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | e63aae948622f4dfeb6c2262018e4fa874d85d90
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | b0ba666565651dbeb9cc4e03e31a9dae91efb3e2
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 5ea49014d224e46b1ef6d0d53f0f0472aa9898fb
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | 89052a37c7220d50bfa9c78e7a5b96a1
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-sources.jar.md5:
--------------------------------------------------------------------------------
1 | 29c769c289c8315c8a79f3c0a88d88d3
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.jar.sha1:
--------------------------------------------------------------------------------
1 | f062163e3423768579d7269f4f7530002591f0f4
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.pom.sha1:
--------------------------------------------------------------------------------
1 | e57210f42b9ba5e1fc18a70550b7f91c2e9fc84d
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | 110157761f6e5c7ac561779ffeeb7897
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-sources.jar.md5:
--------------------------------------------------------------------------------
1 | b8f75bfef94c7520aa9612448249a665
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | d0c5d6635bf220a423874dc95350dac84eb97047
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 867ba285aa2d1c6bdcc2fb304eb84ad1e2a5a689
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.aar.sha1:
--------------------------------------------------------------------------------
1 | 16d18ca2f6f3fdf0a03e1d1d660928fa10fe7f0a
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 2309513929115f16a3436c5d92b6cdf5b6d17c5e
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | 87324f47e373f73b9b1fa842d1b6ffe2
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-sources.jar.md5:
--------------------------------------------------------------------------------
1 | 9bede3cccaca47360937a9af3b7ae775
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.aar.sha1:
--------------------------------------------------------------------------------
1 | a8c4a99d99f891f586d4410fb9c4d4b837bf2408
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.pom.sha1:
--------------------------------------------------------------------------------
1 | cf070d47e758a8d2733c64c747ee24f706ba6059
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/maven-metadata.xml.sha1:
--------------------------------------------------------------------------------
1 | 65d50594dd33b659186c30eb6af093b21608ddb8
--------------------------------------------------------------------------------
/assets/2019-06-01-15-54-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-15-54-28.png
--------------------------------------------------------------------------------
/assets/2019-06-01-16-04-00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-16-04-00.png
--------------------------------------------------------------------------------
/assets/2019-06-01-16-16-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-16-16-26.png
--------------------------------------------------------------------------------
/assets/2019-06-01-16-24-31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-16-24-31.png
--------------------------------------------------------------------------------
/assets/2019-06-01-16-41-42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-16-41-42.png
--------------------------------------------------------------------------------
/assets/2019-06-01-16-59-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-16-59-26.png
--------------------------------------------------------------------------------
/assets/2019-06-01-17-04-39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-17-04-39.png
--------------------------------------------------------------------------------
/assets/2019-06-01-21-16-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/assets/2019-06-01-21-16-27.png
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | de34e4a8efe0a9cc12fcd5645342d1c561312fa5
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 6560133d7fdf287a7c3e4a3ad1926af791039691
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.aar.sha1:
--------------------------------------------------------------------------------
1 | 86a450523d3ed48b5f5b16379ca83eb2373ca78d
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.pom.sha1:
--------------------------------------------------------------------------------
1 | edf78da0b62de1f5db26ec7771a87148a789586a
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | bccaa3c3aa350c3ed74bf51b60ff2896
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 8843683e9613e5a8b453f21d39a91d70
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.jar.md5:
--------------------------------------------------------------------------------
1 | 3e4be855c15b017301c9d445a1c23756
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.pom.md5:
--------------------------------------------------------------------------------
1 | 17ac2da10e6db7ffcbfed7a4e09a2532
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | fda4deebc85b3ff4c614b5be921df17c
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | 7dace4e7e4b58e53866e97633e3d14ad
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.jar.md5:
--------------------------------------------------------------------------------
1 | 892a4a59f4a752ce5ac032095a1a2e64
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.pom.md5:
--------------------------------------------------------------------------------
1 | a216e76669dab97cf01ebb453930c339
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | c344c4be0e6a47c9e70426067ec18f7a0da6d85f
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 5ab10e67f599d2f518de4a7a9f350dd0336c178e
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | 14b60b052f61c0924b465994a75f49547c9e9568
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 00d465682568dd266819c6390fe8e00499ac079f
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/spi-plugin/src/main/resources/META-INF/gradle-plugins/spi.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.afirez.spi.SpiPlugin
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | 1a58ec2e006de4f31e7716cc506055bd6ba628bf
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 5c0edd636d83d4cc710d2508346f7a05969a7192
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | b26b62361ea076a9b537b530f3ff8c0d
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-sources.jar.md5:
--------------------------------------------------------------------------------
1 | 1d2aa2c49e89ced82f205e12ba1ad77c
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.jar.sha1:
--------------------------------------------------------------------------------
1 | 6cf10062267f3f25e0a60e91a69eff0551e1ae09
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.pom.sha1:
--------------------------------------------------------------------------------
1 | f14f8156213fbb13c058b491002912ce1853c888
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | a955158e6a3a361d6a6f05a1a357237a
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-sources.jar.md5:
--------------------------------------------------------------------------------
1 | e2959c585afd044b515d8703fa0f7831
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | 07ff40187098d5ffecafb3cb493db9ca4b8f62b7
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 8de94b7bf515ca39e60276ff05afeaec80b3c121
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-javadoc.jar.md5:
--------------------------------------------------------------------------------
1 | 577174b0e1207b53b99f794b351e1548
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-sources.jar.md5:
--------------------------------------------------------------------------------
1 | 115bdf61edc60eef2b04b95d4b0bb399
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.jar.sha1:
--------------------------------------------------------------------------------
1 | dbff93984b95b66f28fa121e1fbda78cb6eb6ef3
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.pom.sha1:
--------------------------------------------------------------------------------
1 | 88fc31f095d372f5ca8a786651a85838d344a5e3
--------------------------------------------------------------------------------
/spi-api/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | spi_api
3 |
4 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | f7d30e3af0e27d1319c1f995f629b440c64280e1
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | dff0f173b56d18fa2edddfeb64ed3d36d503b131
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | 5298f0e5f85a782e948d076397f019d3082b7c19
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 75b34aa783ae72ba85f703d6e80c5131c56c6476
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-javadoc.jar.sha1:
--------------------------------------------------------------------------------
1 | 9f0dd9ef95c1d1a54cbdd4685344a85cbd81a0f4
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-sources.jar.sha1:
--------------------------------------------------------------------------------
1 | 2c11148bdfa73deb16f894166edbf8c3ffc929ff
--------------------------------------------------------------------------------
/spi-plugin/src/main/resources/META-INF/gradle-plugins/com.afirez.spi.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.afirez.spi.SpiPlugin
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.aar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.aar
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | component1
3 |
4 |
--------------------------------------------------------------------------------
/spi-component1/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | component1_api
3 |
4 |
--------------------------------------------------------------------------------
/spi-component2/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | component2_api
3 |
4 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/spi-applike/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | spi_component_applike
3 |
4 |
--------------------------------------------------------------------------------
/knight-transform/src/main/resources/META-INF/gradle-plugins/com.knight.transform.properties:
--------------------------------------------------------------------------------
1 | implementation-class=com.knight.transform.KnightPlugin
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1-sources.jar
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/2.0.0/transform-2.0.0.jar
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/1.0.10/transform-1.0.10.jar
--------------------------------------------------------------------------------
/spi-api/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.aar
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.aar
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/spi-applike/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/spi-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.aar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.aar
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/1.0.10/transform-1.0.10-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/1.0.10/transform-1.0.10-sources.jar
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/2.0.0/transform-2.0.0-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/knight/transform/2.0.0/transform-2.0.0-sources.jar
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1-sources.jar
--------------------------------------------------------------------------------
/spi-component1/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/spi-component2/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/BaseExtension.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | open class BaseExtension(var isScanJar: Boolean = true) {
4 | }
5 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1-sources.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0-sources.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-javadoc.jar
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afirez/spi/HEAD/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0-sources.jar
--------------------------------------------------------------------------------
/spi-component1/src/main/java/com/afirez/spi/component1/SpiJavaProvider.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1;
2 |
3 | /**
4 | * https://github.com/afirez/spi
5 | */
6 | public interface SpiJavaProvider {
7 | String helloJava();
8 | }
9 |
--------------------------------------------------------------------------------
/spi-component2/src/main/java/com/afirez/spi/component2/SpiKotlinProvider.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2
2 |
3 | /**
4 | * https://github.com/afirez/spi
5 | */
6 | interface SpiKotlinProvider {
7 | fun helloKotlin(): String
8 | }
9 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | spi
3 |
4 |
5 | Hello blank fragment
6 |
7 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | component2
3 |
4 |
5 | Hello blank fragment
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun May 26 01:07:02 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':spi-app', ':spi-applike',
2 | ':spi-component1',
3 | ':spi-component1-impl',
4 | ':spi-component2',
5 | ':spi-component2-impl',
6 | ':spi-api',
7 | ':spi-plugin',
8 | ':knight-transform'
9 |
10 | include ':spi-plugin-booster'
11 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/spi-api/src/main/java/com/afirez/spi/SPI.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi;
2 |
3 | import java.lang.annotation.Retention;
4 | import java.lang.annotation.RetentionPolicy;
5 |
6 | /**
7 | * https://github.com/afirez/spi
8 | */
9 | @Retention(RetentionPolicy.CLASS)
10 | public @interface SPI {
11 | String path() default "";
12 | }
13 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #55b737
4 |
5 | @color/colorPrimary
6 | @color/colorPrimary
7 |
8 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/MemberEntity.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | open class MemberEntity(var access: Int, val className: String, val name: String, val desc: String, var type: Int = FIELD) {
4 |
5 | companion object {
6 | const val FIELD = 0
7 | const val METHOD = 1
8 | }
9 |
10 | }
--------------------------------------------------------------------------------
/spi-component1/src/main/java/com/afirez/spi/component1/SpiRouter1Provider.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1;
2 |
3 | /**
4 | * https://github.com/afirez/spi
5 | */
6 | public interface SpiRouter1Provider {
7 |
8 | void navSpiActivity();
9 |
10 | void navSpiFragmentActivity();
11 |
12 | SpiJavaProvider spiJavaProvider();
13 | }
14 |
--------------------------------------------------------------------------------
/spi-component2/src/main/java/com/afirez/spi/component2/SpiRouter2Provider.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2;
2 |
3 | import androidx.fragment.app.Fragment;
4 |
5 | /**
6 | * https://github.com/afirez/spi
7 | */
8 | public interface SpiRouter2Provider {
9 |
10 | SpiKotlinProvider spiKotlinProvider();
11 |
12 | Fragment spiFragment();
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/spi-plugin/src/main/java/com/afirez/spi/SpiExtension.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import com.knight.transform.BaseExtension
4 |
5 | /**
6 | * https://github.com/afirez/spi
7 | */
8 | open class SpiExtension(
9 | var spiPath: String = "com/afirez/spi/SPI",
10 | var loaderPath: String = "com/afirez/spi/ExtensionLoader",
11 | var add: String = "addExtension"
12 | ) : BaseExtension()
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.afirez.spi
4 | spi-app-lib1
5 |
6 | 1.0.1
7 |
8 | 1.0.1
9 |
10 | 20220908075135
11 |
12 |
13 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.afirez.spi
4 | spi
5 |
6 | 1.0.1
7 |
8 | 1.0.0
9 | 1.0.1
10 |
11 | 20220908075129
12 |
13 |
14 |
--------------------------------------------------------------------------------
/repo/com/knight/transform/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.knight
4 | transform
5 |
6 | 2.0.0
7 |
8 | 1.0.10
9 | 2.0.0
10 |
11 | 20220908075124
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spi-applike/src/main/java/com/afirez/applike/AppLike.java:
--------------------------------------------------------------------------------
1 | package com.afirez.applike;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * https://github.com/afirez/spi
8 | */
9 | public interface AppLike {
10 |
11 | void attachBaseContext(Application app,Context base);
12 |
13 | void onCreate(Application app);
14 |
15 | void onTerminate(Application app) ;
16 | }
17 |
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.afirez.applike
4 | applike
5 |
6 | 1.0.1
7 |
8 | 1.0.0
9 | 1.0.1
10 |
11 | 20210805065854
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Interceptor/IClassVIsitorInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Interceptor
2 |
3 |
4 | interface Chain {
5 | fun transform(): ByteArray
6 | fun request(): ClassVisitorParams;
7 | }
8 |
9 | interface IClassVisitorInterceptor {
10 | fun intercept(chain: Chain): ByteArray
11 | }
12 |
13 | data class ClassVisitorParams(var inputByte: ByteArray, var classloader: ClassLoader? = null)
--------------------------------------------------------------------------------
/spi-app/src/test/java/com/afirez/spi/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/java/com/afirez/spi/component2/impl/SpiKotlinProviderImpl.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2.impl
2 |
3 | import com.afirez.spi.SPI
4 | import com.afirez.spi.component2.SpiKotlinProvider
5 |
6 | /**
7 | * https://github.com/afirez/spi
8 | */
9 | @SPI(path = "/spi/provider/kotlin")
10 | class SpiKotlinProviderImpl : SpiKotlinProvider {
11 | override fun helloKotlin(): String {
12 | return "helloKotlin"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/spi-plugin/src/main/java/com/afirez/spi/SpiContext.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import com.knight.transform.BaseContext
4 | import com.knight.transform.BaseExtension
5 | import org.gradle.api.Project
6 |
7 | /**
8 | * https://github.com/afirez/spi
9 | */
10 | class SpiContext(project: Project, extension: BaseExtension) : BaseContext(project, extension) {
11 |
12 | val extensionsMap = HashMap>()
13 |
14 | }
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.0/spi-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.afirez.spi
6 | spi
7 | 1.0.0
8 | aar
9 |
10 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi/1.0.1/spi-1.0.1.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.afirez.spi
6 | spi
7 | 1.0.1
8 | aar
9 |
10 |
--------------------------------------------------------------------------------
/spi-api/src/test/java/com/afirez/spi/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-applike/src/test/java/com/afirez/applike/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.applike;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/maven-metadata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.afirez.spi
4 | spi-gradle-plugin
5 |
6 | 3.0.0
7 |
8 | 1.0.0
9 | 1.0.1
10 | 2.0.0
11 | 3.0.0
12 |
13 | 20220908075647
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spi-component2/src/test/java/com/afirez/spi/component2/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-component1-impl/src/test/java/com/afirez/spi/component1/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-component2-impl/src/test/java/com/afirez/spi/component2/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-component1/src/test/java/com/afirez/spi/component1/api/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.api;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/res/layout/activity_spi_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/spi-app/src/main/java/com/afirez/spi/app/PrivateUtil.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.app;
2 |
3 | import android.util.Log;
4 |
5 | public class PrivateUtil {
6 |
7 | public static void reportPrivateApi(String hookedClass, String hookedMethod, String invokedClass, String invokedMethod) {
8 | Log.w("alphazz", "======hookedClass = " + hookedClass);
9 | Log.w("alphazz", "======hookedMethod = " + hookedMethod);
10 | Log.w("alphazz", "======invokedClass = " + invokedClass);
11 | Log.w("alphazz", "======invokedMethod = " + invokedMethod);
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Utils/Timer.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Utils
2 |
3 | import java.util.concurrent.TimeUnit
4 |
5 | object Timer {
6 | val records = HashMap()
7 |
8 |
9 | fun start(key: String) {
10 | records[key] = System.nanoTime()
11 | }
12 |
13 |
14 | fun stop(key: String) {
15 | records[key]?.let {
16 | println("\n $key -->COST: ${TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - it)} ms \n")
17 | }
18 | }
19 |
20 | fun reset() {
21 | records.clear()
22 | }
23 | }
--------------------------------------------------------------------------------
/spi-app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/task/WeavedClass.kt:
--------------------------------------------------------------------------------
1 | package transform.task
2 |
3 | import java.io.Serializable
4 | import java.util.LinkedHashSet
5 |
6 | class WeavedClass(val className: String) : Serializable {
7 |
8 | val weavedMethods = LinkedHashSet()
9 |
10 |
11 | fun addWeavedMethod(methodSignature: String) {
12 | weavedMethods.add(methodSignature)
13 | }
14 |
15 |
16 | fun getWeavedMethods(): Set {
17 | return weavedMethods
18 | }
19 |
20 | fun hasWeavedMethod(): Boolean {
21 | return weavedMethods.size > 0
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/gradle/pom-evaluator.gradle:
--------------------------------------------------------------------------------
1 | apply from: "${rootDir}/gradle/bintrayUpload.gradle"
2 | afterEvaluate {
3 | install {
4 | repositories.mavenInstaller {
5 | pom.groupId = project.projectGroupId
6 | pom.version = project.projectVersion
7 |
8 | pom.whenConfigured { pom ->
9 | pom.dependencies.findAll { dep -> dep.groupId == rootProject.name }.collect { dep ->
10 | dep.groupId = pom.groupId = rootProject.groupId
11 | dep.version = pom.version = rootProject.versionName
12 | }
13 | }
14 | }
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
11 | #idea
12 | .idea/
13 | *.iml
14 | *.ipr
15 | *.iws
16 | out/
17 |
18 | #Gradle build folder
19 | build/
20 | .gradle
21 |
22 | #Android
23 | local.properties
24 | bin/
25 | gen/
26 |
27 | #Eclipse project files
28 | .classpath
29 | .settings/
30 | .project
31 |
32 | #JNI
33 | *.o
34 | *.o.d
35 |
36 | #as 2.2 c/c++ compile
37 | .externalNativeBuild/
38 |
39 | #OS autogen folder information
40 | .DS_Stroe
41 | Thumbs.db
42 |
43 | #Temp files
44 | *.bak
45 | *.tmp
46 | *.temp
47 | *.swp
48 | *.*~
49 | ~*.*
50 |
51 | keystore.properties
52 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/res/layout/fragment_spi.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.1/spi-gradle-plugin-1.0.1.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.afirez.spi
5 | spi-gradle-plugin
6 | 1.0.1
7 |
8 |
9 | com.knight
10 | transform
11 | 1.0.10
12 | runtime
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/2.0.0/spi-gradle-plugin-2.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.afirez.spi
5 | spi-gradle-plugin
6 | 2.0.0
7 |
8 |
9 | com.knight
10 | transform
11 | 2.0.0
12 | runtime
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.0/applike-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.afirez.applike
6 | applike
7 | 1.0.0
8 | aar
9 |
10 |
11 | com.afirez.spi
12 | spi
13 | 1.0.0
14 | compile
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/repo/com/afirez/applike/applike/1.0.1/applike-1.0.1.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.afirez.applike
6 | applike
7 | 1.0.1
8 | aar
9 |
10 |
11 | com.afirez.spi
12 | spi
13 | 1.0.1
14 | compile
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/1.0.0/spi-gradle-plugin-1.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.afirez.spi
5 | spi-gradle-plugin
6 | 1.0.0
7 |
8 |
9 | com.knight
10 | transform
11 | 1.0.10
12 | runtime
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Interceptor/ClassVisitorChain.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Interceptor
2 |
3 | import org.gradle.api.GradleException
4 |
5 | class ClassVisitorChain(val index: Int, val interceptors: List, val params: ClassVisitorParams) : Chain {
6 |
7 |
8 | override fun transform(): ByteArray {
9 | if (index >= interceptors.size) {
10 | return params.inputByte
11 | }
12 |
13 | val next = ClassVisitorChain(index + 1, interceptors, params)
14 | val interceptor = interceptors[index]
15 | return interceptor.intercept(next)
16 | }
17 |
18 | override fun request(): ClassVisitorParams {
19 | return params
20 | }
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/spi-applike/src/main/java/com/afirez/applike/App.java:
--------------------------------------------------------------------------------
1 | package com.afirez.applike;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * https://github.com/afirez/spi
8 | */
9 | public class App extends Application {
10 | @Override
11 | protected void attachBaseContext(Context base) {
12 | super.attachBaseContext(base);
13 | AppDelegate.getInstance().attachBaseContext(this, base);
14 | }
15 |
16 | @Override
17 | public void onCreate() {
18 | super.onCreate();
19 | AppDelegate.getInstance().onCreate(this);
20 | }
21 |
22 | @Override
23 | public void onTerminate() {
24 | super.onTerminate();
25 | AppDelegate.getInstance().onTerminate(this);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/spi-app/src/androidTest/java/com/afirez/spi/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.afirez.spi", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/IPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | import com.knight.transform.Interceptor.IClassVisitorInterceptor
4 | import com.knight.transform.asm.IWeaver
5 | import org.objectweb.asm.ClassVisitor
6 | import org.objectweb.asm.ClassWriter
7 |
8 | interface IPlugin {
9 | fun createWeaveClassVisitor(classWriter: ClassWriter): ClassVisitor
10 |
11 | fun createScanClassVisitor(classWriter: ClassWriter): ClassVisitor?
12 |
13 | // 是否需要预先扫描class文件
14 | fun isNeedScanClass(): Boolean
15 |
16 | // 是否需要扫描R文件
17 | fun isNeedScanWeaveRClass(): Boolean
18 |
19 | fun getScanClassVisitorInterceptor(): List?
20 | fun getWeaveClassVisitorInterceptor(): List?
21 |
22 | }
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/java/com/afirez/spi/component1/impl/App.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.impl
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.util.Log
6 | import com.afirez.spi.SPI
7 | import com.afirez.applike.AppLike
8 |
9 | /**
10 | * https://github.com/afirez/spi
11 | */
12 | @SPI
13 | class App : AppLike {
14 | override fun attachBaseContext(app: Application?, base: Context?) {
15 | Log.d("App", "--> attachBaseContext(app = $app, base = $base) : $this")
16 | }
17 |
18 | override fun onCreate(app: Application?) {
19 | Log.d("App", "--> onCreate(app = $app) : $this")
20 | }
21 |
22 | override fun onTerminate(app: Application?) {
23 | Log.d("App", "--> onTerminate(app = $app) : $this")
24 | }
25 | }
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/java/com/afirez/spi/component2/impl/App.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2.impl
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.util.Log
6 | import com.afirez.spi.SPI
7 | import com.afirez.applike.AppLike
8 |
9 | /**
10 | * https://github.com/afirez/spi
11 | */
12 | @SPI
13 | class App : AppLike {
14 | override fun attachBaseContext(app: Application?, base: Context?) {
15 | Log.d("App", "--> attachBaseContext(app = $app, base = $base) : $this")
16 | }
17 |
18 | override fun onCreate(app: Application?) {
19 | Log.d("App", "--> onCreate(app = $app) : $this")
20 | }
21 |
22 | override fun onTerminate(app: Application?) {
23 | Log.d("App", "--> onTerminate(app = $app) : $this")
24 | }
25 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/asm/IWeaver.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.asm
2 |
3 | import com.knight.transform.IPlugin
4 | import java.io.File
5 |
6 | abstract class IWeaver(var classloader: ClassLoader? = null, var isNeedScanRClass: Boolean = false,
7 | val iPlugin: IPlugin) {
8 | abstract fun weaveJar(inputJar: File, outputJar: File)
9 | abstract fun weaveFile(inputFile: File, outputFile: File, inputDir: String)
10 | open fun isWeaveableClass(filePath: String): Boolean = if (isNeedScanRClass) {
11 | filePath.endsWith(".class")
12 | } else {
13 | filePath.endsWith(".class")
14 | && !filePath.contains("R$")
15 | && !filePath.contains("R.class")
16 | && !filePath.contains("BuildConfig.class")
17 | }
18 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk: oraclejdk8
3 |
4 | sudo: false
5 |
6 |
7 | android:
8 | components:
9 | - platform-tools
10 | - tools
11 | - build-tools-28.0.3
12 | - android-28
13 | - extra-android-m2repository
14 | - extra-android-support
15 |
16 |
17 | before_script:
18 | - chmod +x gradlew
19 |
20 |
21 | script:
22 | - ./gradlew assembleRelease
23 |
24 | # 部署
25 | deploy:
26 | # 部署到GitHub Release。
27 | # 除此之外,Travis CI还支持发布到fir.im、AWS、Google App Engine等
28 | provider: releases
29 | # Github oauth token
30 | api_key:
31 | secure: ${ghToken}
32 | # 部署文件路径;对于Android就部署生成的 apk 文件
33 | file: "spi-app/build/outputs/apk/spi-app-release.apk"
34 | # 避免 Travis CI在部署之前清空生成的APK文件
35 | skip_cleanup: true
36 | # 发布时机
37 | on:
38 | # tags设置为true表示只有在有tag的情况下才部署
39 | tags: true
--------------------------------------------------------------------------------
/spi-app/src/main/java/com/afirez/spi/app/App.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.app;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import androidx.multidex.MultiDex;
7 |
8 | import com.afirez.applike.AppDelegate;
9 |
10 | public class App extends Application {
11 | @Override
12 | protected void attachBaseContext(Context base) {
13 | super.attachBaseContext(base);
14 | MultiDex.install(base);
15 | AppDelegate.getInstance().attachBaseContext(this, base);
16 | }
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 | AppDelegate.getInstance().onCreate(this);
22 | }
23 |
24 | @Override
25 | public void onTerminate() {
26 | super.onTerminate();
27 | AppDelegate.getInstance().onTerminate(this);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/spi-api/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/weave/BaseClassVisitor.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.weave
2 |
3 | import com.knight.transform.BaseContext
4 | import org.objectweb.asm.ClassVisitor
5 | import org.objectweb.asm.Opcodes
6 | import transform.task.WeavedClass
7 |
8 | abstract class BaseClassVisitor>(val context: C, cv: ClassVisitor) : ClassVisitor(Opcodes.ASM5, cv), Opcodes {
9 | lateinit var className: String
10 | lateinit var weavedClass: WeavedClass
11 | override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array?) {
12 | super.visit(version, access, name, signature, superName, interfaces)
13 | className = name
14 | weavedClass = WeavedClass(className)
15 | context.weavedClassMap.add(weavedClass)
16 | }
17 | }
--------------------------------------------------------------------------------
/spi-applike/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-component1/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-component2/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-component1-impl/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-component2-impl/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/spi-api/src/androidTest/java/com/afirez/spi/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.spi.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spi-applike/src/androidTest/java/com/afirez/applike/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.applike;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.applike.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/java/com/afirez/spi/component1/impl/SpiJavaProviderImpl.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.impl;
2 |
3 | import com.afirez.spi.ExtensionLoader;
4 | import com.afirez.spi.SPI;
5 | import com.afirez.spi.component1.SpiJavaProvider;
6 | import com.afirez.spi.component2.SpiRouter2Provider;
7 |
8 | /**
9 | * https://github.com/afirez/spi
10 | */
11 | @SPI(path = "/spi/provider/java")
12 | public class SpiJavaProviderImpl implements SpiJavaProvider {
13 |
14 | @Override
15 | public String helloJava() {
16 | SpiRouter2Provider spiRouter2Provider = ExtensionLoader.getInstance().loadExtension(SpiRouter2Provider.class);
17 | String kotlin = spiRouter2Provider != null && spiRouter2Provider.spiKotlinProvider() != null
18 | ? spiRouter2Provider.spiKotlinProvider().helloKotlin() : "";
19 | return "helloJava --> " + kotlin;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spi-component1/src/androidTest/java/com/afirez/spi/component1/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.app.lib1.api.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/androidTest/java/com/afirez/spi/component1/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.spi.app.lib1.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/androidTest/java/com/afirez/spi/component2/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.spi.app.lib2.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-component2/src/androidTest/java/com/afirez/spi/component2/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.afirez.spi_app_lib2_api2.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/java/com/afirez/spi/component2/impl/SpiRouter2ProviderImpl.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2.impl
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.afirez.spi.ExtensionLoader
5 | import com.afirez.spi.SPI
6 | import com.afirez.spi.component2.SpiKotlinProvider
7 | import com.afirez.spi.component2.SpiRouter2Provider
8 |
9 | @SPI(path = "com.afirez.spi.component2.SpiRouter2Provider")
10 | class SpiRouter2ProviderImpl : SpiRouter2Provider {
11 |
12 | override fun spiKotlinProvider(): SpiKotlinProvider? {
13 | return ExtensionLoader.getInstance().loadExtension("/spi/provider/kotlin")
14 | }
15 |
16 | override fun spiFragment(): Fragment? {
17 | val fragmentType = ExtensionLoader.getInstance().extension("/spi/fragment/spi")
18 | val fragment = fragmentType.newInstance() ?: null
19 | return fragment
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spi-component1/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation 'androidx.appcompat:appcompat:1.0.2'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'androidx.test:runner:1.1.1'
32 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
33 | }
34 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/java/com/afirez/spi/component1/impl/SpiActivity.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.impl;
2 |
3 | import android.view.View;
4 | import android.widget.Toast;
5 | import androidx.appcompat.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import com.afirez.spi.SPI;
8 |
9 | /**
10 | * https://github.com/afirez/spi
11 | */
12 | @SPI(path = "/spi/activity/spi")
13 | public class SpiActivity extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_spi);
19 | findViewById(R.id.tvSpiActivity).setOnClickListener(new View.OnClickListener() {
20 | @Override
21 | public void onClick(View v) {
22 | Toast.makeText(SpiActivity.this.getApplicationContext(), "SpiActivity", Toast.LENGTH_SHORT).show();
23 | }
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/drawable/selector_green_round_5dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
13 |
14 |
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 |
23 |
24 |
25 | -
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/res/layout/activity_spi.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/transform/TransformContext.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.transform
2 |
3 | import com.android.build.api.transform.Format
4 | import com.android.build.api.transform.JarInput
5 | import com.android.build.api.transform.QualifiedContent
6 | import com.android.build.api.transform.TransformInvocation
7 | import com.android.ide.common.internal.WaitableExecutor
8 | import java.io.File
9 | import java.util.concurrent.ConcurrentHashMap
10 |
11 | class TransformContext(val invocation: TransformInvocation) {
12 |
13 | val targetJars = ConcurrentHashMap>()
14 | val targetFile = ArrayList()
15 |
16 |
17 | val waitableExecutor = WaitableExecutor.useGlobalSharedThreadPool()
18 |
19 |
20 | fun getOutPutFile(content: QualifiedContent): File {
21 | return invocation.outputProvider.getContentLocation(
22 | content.file.absolutePath,
23 | content.contentTypes,
24 | content.scopes,
25 | if (content is JarInput) Format.JAR else Format.DIRECTORY)
26 | }
27 | }
--------------------------------------------------------------------------------
/spi-component2/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | //apply plugin: 'kotlin-android-extensions'
6 | apply plugin: 'kotlin-parcelize'
7 |
8 | android {
9 | compileSdkVersion 28
10 |
11 |
12 | defaultConfig {
13 | minSdkVersion 19
14 | targetSdkVersion 28
15 | versionCode 1
16 | versionName "1.0"
17 |
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 |
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
35 |
36 | implementation 'androidx.appcompat:appcompat:1.0.2'
37 | testImplementation 'junit:junit:4.12'
38 | androidTestImplementation 'androidx.test:runner:1.1.1'
39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
40 | }
41 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-gradle-plugin/3.0.0/spi-gradle-plugin-3.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.afirez.spi
5 | spi-gradle-plugin
6 | 3.0.0
7 |
8 |
9 | org.jetbrains.kotlin
10 | kotlin-stdlib-jdk8
11 | 1.5.31
12 | runtime
13 |
14 |
15 | com.didiglobal.booster
16 | booster-api
17 | 4.6.0
18 | runtime
19 |
20 |
21 | com.didiglobal.booster
22 | booster-transform-asm
23 | 4.6.0
24 | runtime
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/BaseContext.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | import com.android.build.gradle.AppExtension
4 | import com.android.build.gradle.LibraryPlugin
5 | import org.gradle.api.Project
6 | import transform.task.WeavedClass
7 | import java.io.File
8 | import java.io.FileNotFoundException
9 |
10 | open class BaseContext(val project: Project, val extension: T) {
11 |
12 | val isLibrary = project.plugins.hasPlugin(LibraryPlugin::class.java)
13 | var weavedClassMap = ArrayList()
14 |
15 |
16 | // private fun getSdkJarDir(): String {
17 | // val compileSdkVersion = android?.compileSdkVersion
18 | // return arrayOf(android?.sdkDirectory?.absolutePath, "platforms", compileSdkVersion).joinToString(File.separator)
19 | // }
20 | //
21 | // fun buildDir(): File {
22 | // return File(project.buildDir, "")
23 | // }
24 | //
25 | // @Throws(FileNotFoundException::class)
26 | // fun androidJar(): File {
27 | // val jar = File(getSdkJarDir(), "android.jar")
28 | // if (!jar.exists()) {
29 | // throw FileNotFoundException("Android jar not found!")
30 | // }
31 | // return jar
32 | // }
33 | }
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/java/com/afirez/spi/component1/impl/SpiRouter1ProviderImpl.java:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.impl;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import com.afirez.spi.ExtensionLoader;
6 | import com.afirez.spi.SPI;
7 | import com.afirez.applike.AppDelegate;
8 | import com.afirez.spi.component1.SpiJavaProvider;
9 | import com.afirez.spi.component1.SpiRouter1Provider;
10 |
11 | /**
12 | * https://github.com/afirez/spi
13 | */
14 | @SPI(path = "com.afirez.spi.component1.SpiRouter1Provider")
15 | public class SpiRouter1ProviderImpl implements SpiRouter1Provider {
16 |
17 |
18 | @Override
19 | public void navSpiActivity() {
20 | Context context = AppDelegate.getInstance().topActivityOrApp();
21 | Intent intent = new Intent(context, SpiActivity.class);
22 | context.startActivity(intent);
23 | }
24 |
25 | @Override
26 | public void navSpiFragmentActivity() {
27 | Context context = AppDelegate.getInstance().topActivityOrApp();
28 | Intent intent = new Intent(context, SpiFragmentActivity.class);
29 | context.startActivity(intent);
30 | }
31 |
32 | @Override
33 | public SpiJavaProvider spiJavaProvider() {
34 | return ExtensionLoader.getInstance().loadExtension("/spi/provider/java");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Interceptor/impl/BaseClassVisitorInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Interceptor.impl
2 |
3 | import com.knight.transform.BaseContext
4 | import com.knight.transform.Interceptor.Chain
5 | import com.knight.transform.Interceptor.IClassVisitorInterceptor
6 | import com.knight.transform.weave.BaseClassVisitor
7 | import com.knight.transform.weave.ExtendClassWriter
8 | import org.objectweb.asm.ClassReader
9 | import org.objectweb.asm.ClassVisitor
10 | import org.objectweb.asm.ClassWriter
11 |
12 | class BaseClassVisitorInterceptor(private val wrapClassWriter: (ClassWriter) -> ClassVisitor?) : IClassVisitorInterceptor {
13 | override fun intercept(chain: Chain): ByteArray {
14 | chain.request().let {
15 | val classReader = ClassReader(it.inputByte)
16 | val classWriter = ExtendClassWriter(it.classloader, classReader,
17 | ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
18 | val classVisitor = wrapClassWriter.invoke(classWriter)
19 | try {
20 | classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
21 | } catch (e: Exception) {
22 | println("Exception occurred when visit code \n " + e.printStackTrace())
23 |
24 | }
25 | it.inputByte = classWriter.toByteArray()
26 | return chain.transform()
27 | }
28 | }
29 |
30 |
31 | }
--------------------------------------------------------------------------------
/spi-component2-impl/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | //apply plugin: 'kotlin-android-extensions'
6 | apply plugin: 'kotlin-parcelize'
7 |
8 | android {
9 | compileSdkVersion 28
10 |
11 |
12 | defaultConfig {
13 | minSdkVersion 19
14 | targetSdkVersion 28
15 | versionCode 1
16 | versionName "1.0"
17 |
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 |
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
34 |
35 | implementation "com.afirez.spi:spi:$SPI_VERSION"
36 |
37 | implementation project(':spi-applike')
38 |
39 | implementation project(':spi-component1')
40 | implementation project(':spi-component2')
41 |
42 | implementation 'androidx.appcompat:appcompat:1.0.2'
43 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
44 | testImplementation 'junit:junit:4.12'
45 | androidTestImplementation 'androidx.test:runner:1.1.1'
46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
47 | }
48 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
23 | BUILD_GRADLE_VERION=4.0.2
24 |
25 | TRANSFORM_VERSION=2.0.0
26 |
27 | #SPI_PLUGIN_VERSION=2.0.0
28 | SPI_PLUGIN_VERSION=3.0.0
29 |
30 | SPI_VERSION=1.0.1
31 |
32 | #groupId=com.afirez.spi
33 | #versionName=2.0.0
34 |
35 |
--------------------------------------------------------------------------------
/spi-component1-impl/src/main/java/com/afirez/spi/component1/impl/SpiFragmentActivity.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component1.impl
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.afirez.spi.ExtensionLoader
6 | import com.afirez.spi.SPI
7 | import com.afirez.spi.component2.SpiRouter2Provider
8 |
9 | /**
10 | * https://github.com/afirez/spi
11 | */
12 | @SPI(path = "/spi/activity/fragment")
13 | class SpiFragmentActivity : AppCompatActivity() {
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | // setContentView(R.layout.activity_spi_fragment)
18 |
19 | showSpiFragment()
20 | }
21 |
22 | private fun showSpiFragment() {
23 | val path = "/spi/fragment/spi"
24 | val tag = path
25 | var fragment = supportFragmentManager.findFragmentByTag(tag)
26 | if (fragment == null) {
27 | fragment = ExtensionLoader.getInstance().loadExtension(SpiRouter2Provider::class.java).spiFragment()
28 | }
29 |
30 | if (fragment == null) {
31 | return
32 | }
33 |
34 | val transaction = supportFragmentManager.beginTransaction()
35 | if (fragment.isAdded) {
36 | transaction.show(fragment)
37 | } else {
38 | transaction.add(android.R.id.content, fragment, tag)
39 | }
40 | transaction.commitAllowingStateLoss()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/spi-component2-impl/src/main/java/com/afirez/spi/component2/impl/SpiFragment.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.component2.impl
2 |
3 |
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.TextView
9 | import android.widget.Toast
10 | import androidx.fragment.app.Fragment
11 | import com.afirez.spi.ExtensionLoader
12 | import com.afirez.spi.SPI
13 | import com.afirez.spi.component1.SpiRouter1Provider
14 |
15 | /**
16 | * https://github.com/afirez/spi
17 | */
18 | @SPI(path = "/spi/fragment/spi")
19 | class SpiFragment : Fragment() {
20 |
21 | override fun onCreateView(
22 | inflater: LayoutInflater, container: ViewGroup?,
23 | savedInstanceState: Bundle?
24 | ): View? {
25 | // Inflate the layout for this fragment
26 | return inflater.inflate(R.layout.fragment_spi, container, false)
27 | }
28 |
29 |
30 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
31 | super.onViewCreated(view, savedInstanceState)
32 | view.findViewById(R.id.tvSpiFragment).setOnClickListener {
33 | activity ?: return@setOnClickListener
34 |
35 | val spiRouter1Provider = ExtensionLoader.getInstance().loadExtension(SpiRouter1Provider::class.java)
36 | val tips = if (spiRouter1Provider?.spiJavaProvider() == null) {
37 | ""
38 | } else {
39 | spiRouter1Provider.spiJavaProvider().helloJava()
40 | }
41 | Toast.makeText(activity?.applicationContext, "SpiActivity $tips", Toast.LENGTH_SHORT).show()
42 | }
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/repo/com/knight/transform/1.0.10/transform-1.0.10.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.knight
5 | transform
6 | 1.0.10
7 |
8 |
9 | com.android.tools.build
10 | gradle
11 | 3.2.0
12 | compile
13 |
14 |
15 | com.android.tools.build
16 | transform-api
17 | 2.0.0-deprecated-use-gradle-api
18 | compile
19 |
20 |
21 | commons-io
22 | commons-io
23 | 2.6
24 | compile
25 |
26 |
27 | org.ow2.asm
28 | asm
29 | 6.2.1
30 | compile
31 |
32 |
33 | org.ow2.asm
34 | asm-commons
35 | 6.2
36 | compile
37 |
38 |
39 | org.ow2.asm
40 | asm-util
41 | 6.0
42 | compile
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/knight-transform/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 | apply plugin: 'kotlin'
3 | //apply plugin: 'com.novoda.bintray-release'//添加
4 |
5 | dependencies {
6 | api localGroovy()
7 | //gradle sdk
8 | api gradleApi()
9 | //build tools
10 | api "com.android.tools.build:gradle:$BUILD_GRADLE_VERION"
11 |
12 |
13 | // transform
14 | // api 'com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api'
15 |
16 |
17 | // 开源框架,io操作
18 | api 'commons-io:commons-io:2.6'
19 |
20 | //包中定义类基于核心 API 的相关操作类
21 | //包中定义了核心 API 的类,其中 ClassVisitor、FieldVisitor、MethodVisitor 和 AnnotationVisitor
22 | // 四个抽象类,用于访问 .class 字节码文件中的 fields、 methods 和 annotations 相关的指令。
23 | api 'org.ow2.asm:asm:7.1'
24 | //包中提供了实用的类或方法的 Adapter 方法转换器;
25 | api 'org.ow2.asm:asm-commons:7.1'
26 | //包中提供了常见的类分析框架和分析器类;
27 | api 'org.ow2.asm:asm-analysis:7.1'
28 | //包中提供了基于核心 API 的常见工具类。
29 | api 'org.ow2.asm:asm-util:7.1'
30 | //包中定义了基于树 API 的类,以及一些用于事件和树 API 转换的工具类;
31 | api 'org.ow2.asm:asm-tree:7.1'
32 | }
33 |
34 | apply from: "${rootDir}/gradle/pkg-src.gradle"
35 | apply plugin: 'maven'
36 |
37 | uploadArchives {
38 | repositories {
39 | mavenDeployer {
40 | //GVA的参数
41 | pom.groupId = 'com.knight'//组id
42 | pom.artifactId = 'transform'//名称
43 | pom.version = TRANSFORM_VERSION//版本号
44 | repository(url: uri('../repo'))//发布的目录
45 | }
46 | }
47 | }
48 |
49 | //publish {
50 | // userOrg = 'knight'//bintray.com用户名
51 | // groupId = 'com.knight'//jcenter上的路径
52 | // artifactId = 'transform'//项目名称
53 | // publishVersion = TRANSFORM_VERSION //版本号
54 | // desc = 'this is base plugin'//描述,不重要
55 | //}
56 |
--------------------------------------------------------------------------------
/spi-component1-impl/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation "com.afirez.spi:spi:$SPI_VERSION"
30 |
31 | implementation project(':spi-applike')
32 |
33 | implementation project(':spi-component1')
34 | implementation project(':spi-component2')
35 |
36 | implementation 'androidx.appcompat:appcompat:1.0.2'
37 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
38 | testImplementation 'junit:junit:4.12'
39 | androidTestImplementation 'androidx.test:runner:1.1.1'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
41 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
42 | }
43 |
44 | apply plugin: 'maven'
45 | apply plugin: 'kotlin-android'
46 | //apply plugin: 'kotlin-android-extensions'
47 | apply plugin: 'kotlin-parcelize'
48 |
49 | uploadArchives {
50 | repositories {
51 | mavenDeployer {
52 | repository(url: uri('../repo'))
53 |
54 | pom.groupId = 'com.afirez.spi'
55 | pom.artifactId = 'spi-app-lib1'
56 | pom.version = SPI_VERSION
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/spi-plugin/src/main/java/com/afirez/spi/SpiPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import com.afirez.spi.weave.SpiScanClassVisitor
4 | import com.afirez.spi.weave.SpiWeaveCodeClassVisitor
5 | import com.android.build.gradle.TestedExtension
6 | import com.knight.transform.KnightPlugin
7 | import org.gradle.api.Project
8 | import org.objectweb.asm.ClassVisitor
9 | import org.objectweb.asm.ClassWriter
10 |
11 | /**
12 | * https://github.com/afirez/spi
13 | */
14 | class SpiPlugin : KnightPlugin() {
15 |
16 | private val EXTENSION_NAME = "spi"
17 |
18 | override val isNeedPrintMapAndTaskCostTime: Boolean = true
19 |
20 | override fun createExtensions(): SpiExtension {
21 | project.extensions.create(EXTENSION_NAME, SpiExtension::class.java)
22 | (project.extensions.getByName(EXTENSION_NAME) as SpiExtension).apply {
23 | context = SpiContext(project, this)
24 | return this
25 | }
26 | }
27 |
28 | override fun createScanClassVisitor(classWriter: ClassWriter): ClassVisitor? {
29 | return SpiScanClassVisitor(context, classWriter)
30 | }
31 |
32 | override fun createWeaveClassVisitor(classWriter: ClassWriter): ClassVisitor {
33 | return SpiWeaveCodeClassVisitor(context, classWriter)
34 | }
35 |
36 | override fun getContext(project: Project, extension: SpiExtension, android: TestedExtension): SpiContext {
37 | return SpiContext(project, extension)
38 | }
39 |
40 | override fun getTransformName(): String {
41 | return "SpiTransform"
42 | }
43 |
44 | override fun isNeedScanClass(): Boolean {
45 | return true
46 | }
47 |
48 | override fun isNeedScanWeaveRClass(): Boolean {
49 | return false
50 | }
51 | }
--------------------------------------------------------------------------------
/gradle/pkg-src.gradle:
--------------------------------------------------------------------------------
1 | //apply plugin: 'com.jfrog.bintray'
2 | //apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'org.jetbrains.dokka-android'
4 |
5 | def hasKotlin = project.hasProperty("kotlin")
6 | def hasAndroid = project.hasProperty("android")
7 | def hasJava = project.hasProperty("java")
8 | println("hasKotlin = $hasKotlin hasAndroid = $hasAndroid hasJava = $hasJava")
9 |
10 | if (hasKotlin) { //Kotlin libraries
11 | def srcDirs = hasAndroid ? android.sourceSets.main.java.srcDirs : sourceSets.main.allSource
12 | task sourcesJar(type: Jar) {
13 | classifier = 'sources'
14 | from srcDirs
15 | }
16 | if (hasAndroid) {
17 | task javadoc(type: Javadoc, dependsOn: dokka) {
18 |
19 | }
20 | }
21 | } else if (hasAndroid) { // Android libraries
22 | task sourcesJar(type: Jar) {
23 | classifier = 'sources'
24 | from android.sourceSets.main.java.srcDirs
25 | }
26 |
27 | task javadoc(type: Javadoc) {
28 | source = android.sourceSets.main.java.srcDirs
29 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
30 | }
31 | } else { // Java libraries
32 | task sourcesJar(type: Jar, dependsOn: classes) {
33 | classifier = 'sources'
34 | from sourceSets.main.allSource
35 | }
36 | }
37 |
38 | task javadocJar(type: Jar, dependsOn: javadoc) {
39 | classifier = 'javadoc'
40 | from javadoc.destinationDir
41 | }
42 |
43 | //解决kotlin javadoc.options抱错
44 | dokka {
45 | outputFormat = 'html'
46 | outputDirectory = "$buildDir/javadoc"
47 | }
48 |
49 | tasks.withType(Javadoc) {
50 | options.addStringOption('Xdoclint:none', '-quiet')
51 | options.addStringOption('encoding', 'UTF-8')
52 | options.addStringOption('charSet', 'UTF-8')
53 | }
54 | artifacts {
55 | archives javadocJar
56 | archives sourcesJar
57 | }
58 | sourceSets {
59 | main.java.srcDirs += 'src/main/kotlin'
60 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/KnightTaskPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | import com.android.build.gradle.*
4 | import com.android.build.gradle.api.BaseVariant
5 | import com.knight.transform.Utils.PrintAllTaskUtil
6 | import org.gradle.api.GradleException
7 | import org.gradle.api.Plugin
8 | import org.gradle.api.Project
9 | import transform.KnightTransform
10 |
11 | abstract class KnightTaskPlugin> : Plugin {
12 |
13 | protected lateinit var context: C
14 | protected lateinit var extension: E
15 |
16 | protected lateinit var project: Project
17 | protected lateinit var android: TestedExtension
18 |
19 |
20 | abstract fun getContext(project: Project, extension: E, android: TestedExtension): C
21 | abstract fun createExtensions(): E
22 | lateinit var transform: KnightTransform
23 |
24 | override fun apply(project: Project) {
25 | if (!project.plugins.hasPlugin(AppPlugin::class.java) && !project.plugins.hasPlugin(LibraryPlugin::class.java)) {
26 | throw GradleException("'com.android.application' or 'com.android.library' plugin required!")
27 | }
28 |
29 | val aClass = if (project.plugins.hasPlugin(AppPlugin::class.java))
30 | AppExtension::class.java
31 | else LibraryExtension::class.java
32 | this.project = project
33 | android = project.extensions.getByType(aClass)
34 | extension = createExtensions()
35 | context = getContext(project, extension, android)
36 | project.afterEvaluate {
37 | if (android is AppExtension) {
38 | (android as AppExtension).applicationVariants.all {
39 | createTask(it, context)
40 | }
41 | } else if (android is LibraryExtension) {
42 | (android as LibraryExtension).libraryVariants.all {
43 | createTask(it, context)
44 | }
45 | }
46 | }
47 | }
48 |
49 | abstract fun createTask(variant: BaseVariant, context: C)
50 | }
--------------------------------------------------------------------------------
/repo/com/knight/transform/2.0.0/transform-2.0.0.pom:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.knight
5 | transform
6 | 2.0.0
7 |
8 |
9 | com.android.tools.build
10 | gradle
11 | 4.0.2
12 | compile
13 |
14 |
15 | commons-io
16 | commons-io
17 | 2.6
18 | compile
19 |
20 |
21 | org.ow2.asm
22 | asm
23 | 7.1
24 | compile
25 |
26 |
27 | org.ow2.asm
28 | asm-commons
29 | 7.1
30 | compile
31 |
32 |
33 | org.ow2.asm
34 | asm-analysis
35 | 7.1
36 | compile
37 |
38 |
39 | org.ow2.asm
40 | asm-util
41 | 7.1
42 | compile
43 |
44 |
45 | org.ow2.asm
46 | asm-tree
47 | 7.1
48 | compile
49 |
50 |
51 | org.jetbrains.kotlin
52 | kotlin-stdlib-jdk8
53 | 1.5.31
54 | compile
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/repo/com/afirez/spi/spi-app-lib1/1.0.1/spi-app-lib1-1.0.1.pom:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.afirez.spi
6 | spi-app-lib1
7 | 1.0.1
8 | aar
9 |
10 |
11 | org.jetbrains.kotlin
12 | kotlin-parcelize-runtime
13 | 1.5.31
14 | compile
15 |
16 |
17 | com.afirez.spi
18 | spi
19 | 1.0.1
20 | compile
21 |
22 |
23 | spi
24 | applike
25 | unspecified
26 | compile
27 |
28 |
29 | spi
30 | spi-component1
31 | unspecified
32 | compile
33 |
34 |
35 | spi
36 | spi-component2
37 | unspecified
38 | compile
39 |
40 |
41 | androidx.appcompat
42 | appcompat
43 | 1.0.2
44 | compile
45 |
46 |
47 | androidx.constraintlayout
48 | constraintlayout
49 | 1.1.3
50 | compile
51 |
52 |
53 | org.jetbrains.kotlin
54 | kotlin-stdlib-jdk7
55 | 1.5.31
56 | compile
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/spi-plugin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 | apply plugin: 'kotlin'
3 |
4 |
5 | dependencies {
6 | implementation gradleApi()
7 | implementation localGroovy()
8 | // implementation 'commons-io:commons-io:2.6'
9 | // implementation 'commons-codec:commons-codec:1.10'
10 | //noinspection GradleDependency
11 | // implementation 'org.ow2.asm:asm:5.1'
12 | //noinspection GradleDependency
13 | // implementation 'org.ow2.asm:asm-util:5.1'
14 | //noinspection GradleDependency
15 | // implementation 'org.ow2.asm:asm-commons:5.1'
16 | // implementation 'com.quinn.hunter:hunter-transform:0.9.0'
17 | // implementation "com.knight.component:component:1.0.1"
18 | implementation "com.knight:transform:2.0.0"
19 | }
20 |
21 | repositories {
22 | google()
23 | maven {
24 | url uri('../repo')
25 | }
26 | jcenter()
27 | mavenCentral()
28 | }
29 |
30 | apply from: "${rootDir}/gradle/pkg-src.gradle"
31 | apply plugin: 'maven'
32 |
33 | uploadArchives {
34 | repositories {
35 | mavenDeployer {
36 | pom.groupId = 'com.afirez.spi'
37 | pom.artifactId = 'spi-gradle-plugin'
38 | pom.version = SPI_PLUGIN_VERSION
39 | repository(url: uri('../repo'))
40 | }
41 | }
42 | }
43 |
44 | //ext {
45 | // projectGroupId = 'com.afirez.spi'
46 | // projectPackageing = 'jar'
47 | // projectName = 'spi-gradle-plugin'
48 | // projectArtifactId = 'spi-gradle-plugin'
49 | // projectDesc = 'SPI for componentization.'
50 | //
51 | // siteUrl = 'https://github.com/afirez/spi'
52 | // gitUrl = 'https://github.com/afirez/spi.git'
53 | //
54 | // projectVersion = '1.0.1'
55 | //
56 | // developerId = 'afirez'
57 | // developerName = 'afirez'
58 | // developerEmail = 'afirez.io@gmail.com'
59 | //
60 | // licenseName = 'The Apache Software License, Version 2.0'
61 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
62 | //
63 | // bintrayUser = 'afirez'
64 | // bintrayApiKey = ''
65 | // bintrayRepo = 'android'
66 | // bintrayName = 'spi-gradle-plugin'
67 | // bintrayLicenses = ["Apache-2.0"]
68 | //}
69 | //
70 | //apply from: "${rootDir}/gradle/pom-evaluator.gradle"
--------------------------------------------------------------------------------
/spi-applike/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 |
29 | implementation "com.afirez.spi:spi:$SPI_VERSION"
30 |
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'androidx.test:runner:1.1.1'
33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
34 | }
35 |
36 | apply from: "${rootDir}/gradle/pkg-src.gradle"
37 | apply plugin: 'maven'
38 |
39 | uploadArchives {
40 | repositories {
41 | mavenDeployer {
42 | repository(url: uri('../repo'))
43 |
44 | pom.groupId = 'com.afirez.applike'
45 | pom.artifactId = 'applike'
46 | pom.version = SPI_VERSION
47 | // pom.version = "1.0.0"
48 | }
49 | }
50 | }
51 |
52 | //ext {
53 | // projectGroupId = 'com.afirez.applike'
54 | // projectPackageing = 'aar'
55 | // projectName = 'applike'
56 | // projectArtifactId = 'applike'
57 | // projectDesc = 'SPI Applike for componentization.'
58 | //
59 | // siteUrl = 'https://github.com/afirez/spi'
60 | // gitUrl = 'https://github.com/afirez/spi.git'
61 | //
62 | // projectVersion = '1.0.1'
63 | //
64 | // developerId = 'afirez'
65 | // developerName = 'afirez'
66 | // developerEmail = 'afirez.io@gmail.com'
67 | //
68 | // licenseName = 'The Apache Software License, Version 2.0'
69 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
70 | //
71 | // bintrayUser = 'afirez'
72 | // bintrayApiKey = ''
73 | // bintrayRepo = 'android'
74 | // bintrayName = 'applike'
75 | // bintrayLicenses = ["Apache-2.0"]
76 | //}
77 | //
78 | //apply from: "${rootDir}/gradle/pom-evaluator.gradle"
79 |
--------------------------------------------------------------------------------
/spi-api/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 19
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | //
29 | // implementation 'androidx.appcompat:appcompat:1.0.2'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'androidx.test:runner:1.1.1'
32 | // androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
33 | }
34 |
35 | apply from: "${rootDir}/gradle/pkg-src.gradle"
36 | apply plugin: 'maven'
37 |
38 | uploadArchives {
39 | repositories {
40 | mavenDeployer {
41 | repository(url: uri('../repo'))
42 | // repository(url: uri(rootProject.ext.mavenAndroidRepoUrlLocal))
43 |
44 | pom.groupId = 'com.afirez.spi'
45 | pom.artifactId = 'spi'
46 | pom.version = SPI_VERSION
47 | }
48 | }
49 | }
50 |
51 | //ext {
52 | // projectGroupId = 'com.afirez.spi'
53 | // projectPackageing = 'aar'
54 | // projectName = 'spi'
55 | // projectArtifactId = 'spi'
56 | // projectDesc = 'SPI for componentization.'
57 | //
58 | // siteUrl = 'https://github.com/afirez/spi'
59 | // gitUrl = 'https://github.com/afirez/spi.git'
60 | //
61 | // projectVersion = '1.0.1'
62 | //
63 | // developerId = 'afirez'
64 | // developerName = 'afirez'
65 | // developerEmail = 'afirez.io@gmail.com'
66 | //
67 | // licenseName = 'The Apache Software License, Version 2.0'
68 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
69 | //
70 | // bintrayUser = 'afirez'
71 | // bintrayApiKey = ''
72 | // bintrayRepo = 'android'
73 | // bintrayName = 'spi'
74 | // bintrayLicenses = ["Apache-2.0"]
75 | //}
76 | //
77 | //apply from: "${rootDir}/gradle/pom-evaluator.gradle"
78 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/task/OutPutMappingTask.kt:
--------------------------------------------------------------------------------
1 | package transform.task
2 |
3 | import org.apache.commons.io.FileUtils
4 | import org.gradle.api.DefaultTask
5 | import org.gradle.api.model.ObjectFactory
6 | import org.gradle.api.tasks.Input
7 | import org.gradle.api.tasks.Internal
8 | import org.gradle.api.tasks.OutputFile
9 | import org.gradle.api.tasks.TaskAction
10 | import java.lang.StringBuilder
11 | import kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType
12 |
13 | open class OutPutMappingTask : DefaultTask() {
14 | init {
15 | group = "outputmapping"
16 | description = "write mapping file"
17 | }
18 |
19 | @Input
20 | var variantName = project.objects.property(String::class.java)
21 |
22 | // @OutputFile
23 | // var outputMappingFile = newOutputFile()
24 |
25 | // @OutputFile
26 | // var outputMappingFile = getServices().get(ObjectFactory::class.java).fileProperty()
27 |
28 | @OutputFile // property needs an annotation
29 | var outputMappingFile = project.objects.fileProperty()
30 |
31 | //// @Internal
32 | // var classes = project.objects.property(ArrayList::class.java)
33 |
34 | @Internal
35 | var classes = project.objects.listProperty(Any::class.java)
36 |
37 | @TaskAction
38 | fun writeMapping() {
39 | var loggable = false
40 | val mappingFile = outputMappingFile.get().asFile
41 |
42 | FileUtils.touch(mappingFile)
43 | val content = StringBuilder()
44 | if (loggable) println("outputtask size: ${classes.get().size}")
45 | (classes.get() as List).forEach {
46 | it?.takeIf { it ->
47 | it?.hasWeavedMethod()
48 | }?.let {
49 | val className = it.className
50 | val doubleCheckMethods = it.weavedMethods
51 | content.append(className).append("\n")
52 | if (loggable) println(className)
53 | doubleCheckMethods.forEach {
54 | content.append("\u21E2 $it").append("\n")
55 | if (loggable) println("\u21E2 $it")
56 | }
57 | }
58 | }
59 | mappingFile.writeText(content.toString())
60 | // println("Success wrote TXT mapping report to file://${outputMappingFile}")
61 |
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/spi-app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
24 |
25 |
29 |
30 |
34 |
35 |
39 |
40 |
44 |
45 |
49 |
50 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/KnightTransform.kt:
--------------------------------------------------------------------------------
1 | package transform
2 |
3 | import com.android.build.api.transform.QualifiedContent
4 | import com.android.build.api.transform.Transform
5 | import com.android.build.api.transform.TransformInvocation
6 | import com.android.build.gradle.internal.pipeline.TransformManager
7 | import com.knight.transform.BaseContext
8 | import com.knight.transform.IPlugin
9 | import com.knight.transform.Utils.Log
10 | import com.knight.transform.Utils.TransFormUtil
11 | import com.knight.transform.asm.CodeWeaver
12 | import com.knight.transform.asm.ScanWeaver
13 | import transform.Utils.ASMUtils
14 |
15 | open class KnightTransform(private val context: BaseContext<*>, val iPlugin: IPlugin, val getTransformName: () -> String) : Transform() {
16 | private val codeWeaver: CodeWeaver = CodeWeaver(iPlugin)
17 | private val scanWeaver: ScanWeaver? = if (iPlugin.isNeedScanClass()) {
18 | ScanWeaver(iPlugin)
19 | } else null
20 |
21 |
22 | override fun getName(): String {
23 | return getTransformName.invoke()
24 | }
25 |
26 | override fun getInputTypes(): MutableSet {
27 | return TransformManager.CONTENT_CLASS
28 | }
29 |
30 | override fun isIncremental(): Boolean {
31 | return true
32 | }
33 |
34 | override fun getScopes(): MutableSet {
35 | return if (context.isLibrary) TransformManager.PROJECT_ONLY else TransformManager.SCOPE_FULL_PROJECT
36 | }
37 |
38 | override fun transform(invocation: TransformInvocation) {
39 | val classLoader = ASMUtils.getClassLoader(invocation.inputs, invocation.referencedInputs, context.project)
40 | scanWeaver?.let {
41 | it.classloader = classLoader
42 | it.isNeedScanRClass = iPlugin.isNeedScanWeaveRClass()
43 | val startTime = System.currentTimeMillis()
44 | TransFormUtil.transform(context, it, invocation)
45 | val costTime = System.currentTimeMillis() - startTime
46 | Log.i("$name : scan code has costed $costTime ms")
47 | }
48 | codeWeaver.run {
49 | val startTime = System.currentTimeMillis()
50 | classloader = classLoader
51 | isNeedScanRClass = iPlugin.isNeedScanWeaveRClass()
52 | TransFormUtil.transform(context, this, invocation)
53 | val costTime = System.currentTimeMillis() - startTime
54 | Log.i("$name : weave code has costed $costTime ms")
55 | }
56 |
57 | }
58 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Utils/PrintAllTaskUtil.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Utils
2 |
3 | import com.knight.transform.BaseContext
4 | import org.gradle.BuildListener
5 | import org.gradle.BuildResult
6 | import org.gradle.api.Project
7 | import org.gradle.api.Task
8 | import org.gradle.api.execution.TaskExecutionListener
9 | import org.gradle.api.initialization.Settings
10 | import org.gradle.api.invocation.Gradle
11 | import org.gradle.api.tasks.TaskState
12 |
13 | /**
14 | * description
15 | *
16 | * @author liyachao
17 | * @date 2019/3/23
18 | */
19 | object PrintAllTaskUtil {
20 | val TAG = "PrintAllTaskUtil"
21 | //用来记录 task 的执行时长等信息
22 | val timeCostMap = LinkedHashMap()
23 |
24 | private val taskExecutionListener = object : TaskExecutionListener {
25 | override fun beforeExecute(task: Task) {
26 | timeCostMap[task.path] = TaskExecTimeInfo(System.currentTimeMillis(), 0, task.path, 0)
27 | }
28 |
29 | override fun afterExecute(task: Task, p1: TaskState) {
30 | timeCostMap[task.path]?.let {
31 | it.endTime = System.currentTimeMillis()
32 | it.totalTime = it.endTime - it.startTime
33 | }
34 | }
35 | }
36 |
37 | private val buildListener = object : BuildListener {
38 | override fun buildFinished(buildResult: BuildResult) {
39 | val sb = StringBuilder()
40 | sb.append("build finished, now println all task execution time: ")
41 | timeCostMap.forEach {
42 | sb.append("\n${it.key} [${it.value.totalTime}]")
43 | }
44 | Log.e(sb.toString())
45 | }
46 |
47 | override fun projectsLoaded(p0: Gradle) {
48 | }
49 |
50 | override fun buildStarted(p0: Gradle) {
51 | }
52 |
53 | override fun projectsEvaluated(p0: Gradle) {
54 | }
55 |
56 | override fun settingsEvaluated(p0: Settings) {
57 | }
58 |
59 | }
60 |
61 | fun printAllTasks(context: BaseContext<*>) {
62 | context.project.gradle.addListener(taskExecutionListener)
63 | context.project.gradle.addBuildListener(buildListener)
64 | }
65 |
66 | fun printAllTasks(project: Project) {
67 | project.gradle.addListener(taskExecutionListener)
68 | project.gradle.addBuildListener(buildListener)
69 | }
70 | }
71 |
72 | data class TaskExecTimeInfo(var startTime: Long = 0, var endTime: Long = 0,
73 | var path: String = "", var totalTime: Long = 0)
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/asm/ScanWeaver.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.asm
2 |
3 | import com.knight.transform.IPlugin
4 | import com.knight.transform.Interceptor.ClassVisitorChain
5 | import com.knight.transform.Interceptor.ClassVisitorParams
6 | import com.knight.transform.Utils.Log
7 | import com.knight.transform.weave.ExtendClassWriter
8 | import org.objectweb.asm.ClassReader
9 | import org.objectweb.asm.ClassVisitor
10 | import org.objectweb.asm.ClassWriter
11 | import java.io.BufferedInputStream
12 | import java.io.File
13 | import java.io.FileInputStream
14 | import java.io.InputStream
15 | import java.util.zip.ZipEntry
16 | import java.util.zip.ZipFile
17 |
18 |
19 | class ScanWeaver(iPlugin: IPlugin) : IWeaver(iPlugin = iPlugin) {
20 |
21 | val TAG = "ScanWeaver"
22 | override fun weaveJar(inputJar: File, outputJar: File) {
23 | val inputZip = ZipFile(inputJar)
24 | Log.d("scanJar is ${inputJar.name}")
25 | inputZip.entries().toList().forEach { entry ->
26 | val outEntry = ZipEntry(entry.name)
27 | if (isWeaveableClass(outEntry.name.replace("/", "."))) {
28 | scanWithByteChain(BufferedInputStream(inputZip.getInputStream(entry)))
29 | }
30 | }
31 | }
32 |
33 | override fun weaveFile(inputFile: File, outputFile: File, inputDir: String) {
34 | var inputBaseDir = inputDir
35 | if (!inputBaseDir.endsWith("/")) inputBaseDir += "/"
36 | if (inputFile.isFile && isWeaveableClass(inputFile.absolutePath.replace(inputBaseDir, "").replace("/", "."))) {
37 | val inputStream = FileInputStream(inputFile)
38 | scanWithByteChain(inputStream)
39 | }
40 | }
41 |
42 | fun scanWithByte(intputStream: InputStream) {
43 | val classReader = ClassReader(intputStream)
44 | val classWriter = ExtendClassWriter(classloader, classReader,
45 | ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
46 | val classVisitor = iPlugin.createScanClassVisitor(classWriter)
47 | try {
48 | classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
49 | } catch (e: Exception) {
50 | println("Exception occurred when visit code \n " + e.printStackTrace())
51 |
52 | }
53 | }
54 |
55 | fun scanWithByteChain(intputStream: InputStream) {
56 | iPlugin.getScanClassVisitorInterceptor()?.let {
57 | val chain = ClassVisitorChain(0, it, ClassVisitorParams(intputStream.readBytes(), classloader))
58 | chain.transform()
59 | } ?: scanWithByte(intputStream)
60 |
61 | }
62 | }
--------------------------------------------------------------------------------
/spi-plugin-booster/src/main/java/com/afirez/spi/dummy/TestPrivateTransformer.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.dummy
2 |
3 | import com.didiglobal.booster.kotlinx.asIterable
4 | import com.didiglobal.booster.transform.TransformContext
5 | import com.didiglobal.booster.transform.asm.ClassTransformer
6 | import org.objectweb.asm.Opcodes
7 | import org.objectweb.asm.tree.ClassNode
8 | import org.objectweb.asm.tree.LdcInsnNode
9 | import org.objectweb.asm.tree.MethodInsnNode
10 |
11 | //@AutoService(ClassTransformer::class)
12 | class TestPrivateTransformer : ClassTransformer {
13 |
14 | override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
15 | val hookedClassName = klass.name
16 | var hookedMethodName = ""
17 | val iterator = klass.methods.iterator()
18 | while (iterator.hasNext()) {
19 | val method = iterator.next()
20 | method.instructions?.iterator()?.forEach {
21 | // 查找TelephonyManager.getNetworkType
22 | if (it.opcode == Opcodes.INVOKEVIRTUAL && it is MethodInsnNode) {
23 | if (it.owner == "android/telephony/TelephonyManager" && it.name == "getNetworkType") {
24 | hookedMethodName = method.name
25 |
26 | println("====find private success : $hookedClassName / $hookedMethodName")
27 | }
28 | }
29 | }
30 | // 如果hookedMethodName不为空那么表示找到了,那么就插入代码
31 | if (!(hookedMethodName == null || hookedClassName.isEmpty())) {
32 | method?.instructions?.iterator()?.asIterable()?.filter {
33 | it.opcode == Opcodes.RETURN
34 | }?.forEach {
35 | method.instructions?.apply {
36 | insertBefore(it, LdcInsnNode(hookedClassName))
37 | insertBefore(it, LdcInsnNode(hookedMethodName))
38 | insertBefore(it, LdcInsnNode("android/telephony/TelephonyManager"))
39 | insertBefore(it, LdcInsnNode("getNetworkType"))
40 | insertBefore(
41 | it,
42 | MethodInsnNode(
43 | Opcodes.INVOKESTATIC,
44 | "com/afirez/spi/app",
45 | "reportPrivateApi",
46 | "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
47 | false
48 | )
49 | )
50 | }
51 | }
52 | }
53 | }
54 |
55 | return super.transform(context, klass)
56 | }
57 | }
--------------------------------------------------------------------------------
/spi-plugin/src/main/java/com/afirez/spi/weave/SpiWeaveCodeClassVisitor.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.weave
2 |
3 | import com.afirez.spi.SpiContext
4 | import com.afirez.spi.SpiExtension
5 | import com.knight.transform.weave.BaseClassVisitor
6 | import org.objectweb.asm.*
7 |
8 | /**
9 | * https://github.com/afirez/spi
10 | */
11 | class SpiWeaveCodeClassVisitor(context: SpiContext, cv: ClassVisitor) : BaseClassVisitor(context, cv) {
12 | val extension = context.extension as SpiExtension
13 |
14 | var isServiceLoader = false
15 |
16 |
17 | override fun visit(
18 | version: Int,
19 | access: Int,
20 | name: String,
21 | signature: String?,
22 | superName: String?,
23 | interfaces: Array?
24 | ) {
25 | super.visit(version, access, name, signature, superName, interfaces)
26 | isServiceLoader = name == extension.loaderPath
27 | }
28 |
29 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
30 | return super.visitAnnotation(descriptor, visible)
31 | }
32 |
33 | override fun visitMethod(
34 | access: Int,
35 | name: String?,
36 | descriptor: String?,
37 | signature: String?,
38 | exceptions: Array?
39 | ): MethodVisitor {
40 |
41 | val mv = super.visitMethod(access, name, descriptor, signature, exceptions)
42 |
43 | if (isServiceLoader && access == 2 && name == "" && descriptor == "()V") { //找到目标类的私有构造方法
44 | return SpiWeaveMethodVisitor(context, mv)
45 | }
46 |
47 | return mv
48 | }
49 |
50 | class SpiWeaveMethodVisitor(val context: SpiContext, mv: MethodVisitor?) : MethodVisitor(Opcodes.ASM5, mv) {
51 | val extension = context.extension as SpiExtension
52 |
53 | override fun visitInsn(opcode: Int) {
54 | when (opcode) {
55 | Opcodes.IRETURN,
56 | Opcodes.FRETURN,
57 | Opcodes.ARETURN,
58 | Opcodes.LRETURN,
59 | Opcodes.DRETURN,
60 | Opcodes.RETURN -> {
61 | context.extensionsMap.forEach { type, map ->
62 | map.forEach{path, extension ->
63 | inject(type, extension, path)
64 | }
65 | }
66 | }
67 | }
68 | super.visitInsn(opcode)
69 | }
70 |
71 | private fun inject(type: String, extension: String, path: String) {
72 | println(" ====> Spi ${this.extension.add} [ $type --> $extension ]")
73 | mv.visitVarInsn(Opcodes.ALOAD, 0)
74 | mv.visitLdcInsn(Type.getObjectType(type))
75 | mv.visitLdcInsn(Type.getObjectType(extension))
76 | mv.visitLdcInsn(path)
77 | mv.visitMethodInsn(
78 | Opcodes.INVOKEVIRTUAL,
79 | this.extension.loaderPath,
80 | this.extension.add,
81 | "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)V",
82 | false
83 | )
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/spi-plugin-booster/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'groovy'
2 | apply plugin: 'kotlin'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | //plugins {
6 | // id 'java-library'
7 | // id 'org.jetbrains.kotlin.jvm'
8 | //}
9 |
10 | java {
11 | sourceCompatibility = JavaVersion.VERSION_1_8
12 | targetCompatibility = JavaVersion.VERSION_1_8
13 | }
14 |
15 | compileKotlin {
16 | kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8
17 | }
18 |
19 |
20 | dependencies {
21 | // implementation gradleApi()
22 | // implementation localGroovy()
23 |
24 | // implementation 'commons-io:commons-io:2.6'
25 | // implementation 'commons-codec:commons-codec:1.10'
26 | //noinspection GradleDependency
27 | // implementation 'org.ow2.asm:asm:5.1'
28 | //noinspection GradleDependency
29 | // implementation 'org.ow2.asm:asm-util:5.1'
30 | //noinspection GradleDependency
31 | // implementation 'org.ow2.asm:asm-commons:5.1'
32 | // implementation 'com.quinn.hunter:hunter-transform:0.9.0'
33 | // implementation "com.knight.component:component:1.0.1"
34 | // implementation "com.knight:transform:2.0.0"
35 |
36 | kapt "com.google.auto.service:auto-service:1.0"
37 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
38 |
39 | implementation "com.didiglobal.booster:booster-api:$booster_version"
40 | implementation "com.didiglobal.booster:booster-transform-asm:$booster_version"
41 |
42 | }
43 |
44 | repositories {
45 | google()
46 | maven {
47 | url uri('../repo')
48 | }
49 | jcenter()
50 | mavenCentral()
51 |
52 | // OPTIONAL If you want to use SNAPSHOT version, sonatype repository is required.
53 | // maven { url 'https://oss.sonatype.org/content/repositories/public' }
54 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
55 | // maven { url "https://plugins.gradle.org/m2/" }
56 | }
57 |
58 | apply from: "${rootDir}/gradle/pkg-src.gradle"
59 | apply plugin: 'maven'
60 |
61 | uploadArchives {
62 | repositories {
63 | mavenDeployer {
64 | pom.groupId = 'com.afirez.spi'
65 | pom.artifactId = 'spi-gradle-plugin'
66 | pom.version = SPI_PLUGIN_VERSION
67 | // repository(url: uri('../repo'))
68 | repository(url: uri('/Users/afirez/studio/android/apps-android-repo'))
69 | }
70 | }
71 | }
72 |
73 | //ext {
74 | // projectGroupId = 'com.afirez.spi'
75 | // projectPackageing = 'jar'
76 | // projectName = 'spi-gradle-plugin'
77 | // projectArtifactId = 'spi-gradle-plugin'
78 | // projectDesc = 'SPI for componentization.'
79 | //
80 | // siteUrl = 'https://github.com/afirez/spi'
81 | // gitUrl = 'https://github.com/afirez/spi.git'
82 | //
83 | // projectVersion = '1.0.1'
84 | //
85 | // developerId = 'afirez'
86 | // developerName = 'afirez'
87 | // developerEmail = 'afirez.io@gmail.com'
88 | //
89 | // licenseName = 'The Apache Software License, Version 2.0'
90 | // licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
91 | //
92 | // bintrayUser = 'afirez'
93 | // bintrayApiKey = ''
94 | // bintrayRepo = 'android'
95 | // bintrayName = 'spi-gradle-plugin'
96 | // bintrayLicenses = ["Apache-2.0"]
97 | //}
98 | //
99 | //apply from: "${rootDir}/gradle/pom-evaluator.gradle"
--------------------------------------------------------------------------------
/spi-app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | //apply plugin: 'kotlin-android-extensions'
6 | apply plugin: 'kotlin-parcelize'
7 |
8 | // didi booster
9 | apply plugin: 'com.didiglobal.booster'
10 | // gradle plugin version <= 4.1.2
11 | // apply plugin: 'spi'
12 | // or apply plugin: 'com.afirez.spi'
13 |
14 | def isRunningOnTravis = System.getenv("CI") == "true"
15 |
16 | def keystorePropertiesFile = rootProject.file("keystore.properties")
17 | def keystoreProperties = new Properties()
18 | if (keystorePropertiesFile.exists()) {
19 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
20 | }
21 |
22 | android {
23 | compileSdkVersion 28
24 | defaultConfig {
25 | applicationId "com.afirez.spiapp"
26 | minSdkVersion 19
27 | targetSdkVersion 28
28 | versionCode 1
29 | versionName "1.0"
30 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31 | }
32 |
33 | if (isRunningOnTravis) {
34 | signingConfigs {
35 | release {
36 | keyAlias System.getenv("keyAlias")
37 | keyPassword System.getenv("keyPwd")
38 | storeFile rootProject.file('app-afirez.jks')
39 | storePassword System.getenv("storePwd")
40 | }
41 | }
42 | } else {
43 | signingConfigs {
44 | release {
45 | keyAlias keystoreProperties.containsKey("keyAlias") ? keystoreProperties['keyAlias'] : System.getenv("keyAlias")
46 | keyPassword keystoreProperties.containsKey("keyPwd") ? keystoreProperties['keyPwd'] : System.getenv("keyPwd")
47 | storeFile rootProject.file('app-afirez.jks')
48 | storePassword keystoreProperties.containsKey("storePwd") ? keystoreProperties['storePwd'] : System.getenv("storePwd")
49 | }
50 | }
51 | }
52 |
53 |
54 | buildTypes {
55 | release {
56 | signingConfig signingConfigs.release
57 | minifyEnabled false
58 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
59 | }
60 | }
61 |
62 | lintOptions {
63 | checkReleaseBuilds false
64 | abortOnError false
65 | }
66 |
67 | compileOptions {
68 | sourceCompatibility JavaVersion.VERSION_1_8
69 | targetCompatibility JavaVersion.VERSION_1_8
70 | }
71 |
72 | }
73 |
74 | dependencies {
75 | implementation fileTree(dir: 'libs', include: ['*.jar'])
76 | implementation "androidx.multidex:multidex:2.0.0"
77 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
78 | implementation 'androidx.appcompat:appcompat:1.0.2'
79 | implementation 'androidx.core:core-ktx:1.0.2'
80 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
81 |
82 | implementation "com.afirez.spi:spi:$SPI_VERSION"
83 |
84 | implementation project(':spi-applike')
85 |
86 | implementation project(':spi-component1')
87 | implementation project(':spi-component2')
88 |
89 | implementation project(':spi-component1-impl')
90 | implementation project(':spi-component2-impl')
91 |
92 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
93 | testImplementation 'junit:junit:4.12'
94 | androidTestImplementation 'androidx.test:runner:1.1.1'
95 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
96 | }
97 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Utils/TypeUtil.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.Utils
2 |
3 | import org.objectweb.asm.Opcodes
4 |
5 | import java.util.regex.Matcher
6 | import java.util.regex.Pattern
7 |
8 | object TypeUtil {
9 | private val paramsPat = Pattern.compile("(\\[?[BCZSIJFD])|(L[^;]+;)")
10 |
11 | fun removeFirstParam(desc: String): String {
12 | if (desc.startsWith("()")) {
13 | return desc
14 | }
15 | var index = 1
16 | var c = desc[index]
17 | while (c == '[') {
18 | index++
19 | c = desc[index]
20 | }
21 | if (c == 'L') {
22 | while (desc[index] != ';') {
23 | index++
24 | }
25 | }
26 | return "(" + desc.substring(index + 1)
27 | }
28 |
29 | fun getParameterCountFromMethodDesc(desc: String): Int {
30 | val beginIndex = desc.indexOf('(') + 1
31 | val endIndex = desc.lastIndexOf(')')
32 | val paramsDesc = desc.substring(beginIndex, endIndex)
33 | if (paramsDesc.isEmpty()) return 0
34 | var count = 0
35 | val matcher = paramsPat.matcher(paramsDesc)
36 | while (matcher.find()) {
37 | count++
38 | }
39 | return count
40 | }
41 |
42 | fun desc2Name(desc: String): String {
43 | return if (!desc.startsWith("L") && !desc.endsWith(";")) {
44 | desc
45 | } else desc.substring(1, desc.length - 1)
46 | }
47 |
48 | fun descToStatic(access: Int, desc: String, className: String): String {
49 | var desc = desc
50 | if (access and Opcodes.ACC_STATIC == 0) {
51 | desc = "(L" + className.replace('.', '/') + ";" + desc.substring(1)
52 | }
53 | return desc
54 | }
55 |
56 | fun descToNonStatic(desc: String): String {
57 | return "(" + desc.substring(desc.indexOf(';') + 1)
58 | }
59 |
60 | fun parseArray(index: Int, desc: String): Int {
61 | var index = index
62 | while (desc[index] == '[') index++
63 | if (desc[index] == 'L') {
64 | while (desc[index] != ';') index++
65 | }
66 | return index
67 | }
68 |
69 | fun parseObject(index: Int, desc: String): Int {
70 | var index = index
71 | while (desc[index] != ';') index++
72 | return index
73 | }
74 |
75 | fun isStatic(access: Int): Boolean {
76 | return access and Opcodes.ACC_STATIC == Opcodes.ACC_STATIC
77 | }
78 |
79 | fun isAbstract(access: Int): Boolean {
80 | return access and Opcodes.ACC_ABSTRACT == Opcodes.ACC_ABSTRACT
81 | }
82 |
83 | fun isSynthetic(access: Int): Boolean {
84 | return access and Opcodes.ACC_SYNTHETIC == Opcodes.ACC_SYNTHETIC
85 | }
86 |
87 | fun isPrivate(access: Int): Boolean {
88 | return access and Opcodes.ACC_PRIVATE == Opcodes.ACC_PRIVATE
89 | }
90 |
91 | fun isPublic(access: Int): Boolean {
92 | return access and Opcodes.ACC_PUBLIC == Opcodes.ACC_PUBLIC
93 | }
94 |
95 | fun isProtected(access: Int): Boolean {
96 | return access and Opcodes.ACC_PROTECTED == Opcodes.ACC_PROTECTED
97 | }
98 |
99 | fun resetAccessScope(access: Int, scope: Int): Int {
100 | return access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED).inv() or scope
101 | }
102 |
103 | fun isInterface(access: Int): Boolean {
104 | return access and Opcodes.ACC_INTERFACE == Opcodes.ACC_INTERFACE
105 | }
106 |
107 | fun isInt(desc: String): Boolean {
108 | return "I" == desc
109 | }
110 |
111 | fun isFinal(access: Int): Boolean {
112 | return access and Opcodes.ACC_FINAL == Opcodes.ACC_FINAL
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/Utils/ASMUtils.kt:
--------------------------------------------------------------------------------
1 | package transform.Utils
2 |
3 | import org.objectweb.asm.Opcodes
4 | import org.objectweb.asm.Type
5 |
6 | import org.objectweb.asm.Opcodes.ACC_PRIVATE
7 | import org.objectweb.asm.Opcodes.ACC_PUBLIC
8 | import org.objectweb.asm.Opcodes.ACC_STATIC
9 | import com.android.build.api.transform.JarInput
10 | import com.android.build.api.transform.DirectoryInput
11 | import com.android.build.api.transform.TransformInput
12 | import com.android.build.gradle.AppExtension
13 | import com.google.common.collect.ImmutableList
14 | import com.google.common.collect.Iterables
15 | import org.gradle.api.Project
16 | import java.io.File
17 | import java.net.MalformedURLException
18 | import java.net.URL
19 | import java.net.URLClassLoader
20 |
21 |
22 | object ASMUtils {
23 | fun isPrivate(access: Int): Boolean {
24 | return access and ACC_PRIVATE != 0
25 | }
26 |
27 | fun isPublic(access: Int): Boolean {
28 | return access and ACC_PUBLIC != 0
29 | }
30 |
31 | fun isStatic(access: Int): Boolean {
32 | return access and ACC_STATIC != 0
33 | }
34 |
35 | fun isFinal(access: Int): Boolean {
36 | return access and Opcodes.ACC_FINAL == Opcodes.ACC_FINAL
37 | }
38 |
39 | fun isInt(desc: String): Boolean {
40 | return "I" == desc
41 | }
42 |
43 | fun convertSignature(name: String, desc: String): String {
44 | val method = Type.getType(desc)
45 | val sb = StringBuilder()
46 | sb.append(method.returnType.className).append(" ").append(name)
47 | sb.append("(")
48 | for (i in 0 until method.argumentTypes.size) {
49 | sb.append(method.argumentTypes[i].className)
50 | if (i != method.argumentTypes.size - 1) {
51 | sb.append(",")
52 | }
53 | }
54 | sb.append(")")
55 | return sb.toString()
56 | }
57 |
58 | fun isSelfFile(className: String): Boolean {
59 | if (className.startsWith("android/") || className.startsWith("kotlin/")) {
60 | return false
61 | }
62 | return true
63 | }
64 |
65 | @Throws(MalformedURLException::class)
66 | fun getClassLoader(inputs: Collection,
67 | referencedInputs: Collection,
68 | project: Project): URLClassLoader {
69 | val urls = ImmutableList.Builder()
70 | val androidJarPath = getAndroidJarPath(project)
71 | val file = File(androidJarPath)
72 | val androidJarURL = file.toURI().toURL()
73 | urls.add(androidJarURL)
74 | for (totalInputs in Iterables.concat(inputs, referencedInputs)) {
75 | for (directoryInput in totalInputs.directoryInputs) {
76 | if (directoryInput.file.isDirectory) {
77 | urls.add(directoryInput.file.toURI().toURL())
78 | }
79 | }
80 | for (jarInput in totalInputs.jarInputs) {
81 | if (jarInput.file.isFile) {
82 | urls.add(jarInput.file.toURI().toURL())
83 | }
84 | }
85 | }
86 | val allUrls = urls.build()
87 | val classLoaderUrls = allUrls.toTypedArray()
88 | return URLClassLoader(classLoaderUrls)
89 | }
90 |
91 | /**
92 | * /Users/quinn/Documents/Android/SDK/platforms/android-28/android.jar
93 | */
94 | private fun getAndroidJarPath(project: Project): String {
95 | val appExtension = project.properties["android"] as AppExtension?
96 | var sdkDirectory = appExtension!!.sdkDirectory.absolutePath
97 | val compileSdkVersion = appExtension.compileSdkVersion
98 | sdkDirectory = sdkDirectory + File.separator + "platforms" + File.separator
99 | return sdkDirectory + compileSdkVersion + File.separator + "android.jar"
100 | }
101 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/asm/CodeWeaver.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.asm
2 |
3 | import com.knight.transform.IPlugin
4 | import com.knight.transform.Interceptor.ClassVisitorChain
5 | import com.knight.transform.Interceptor.ClassVisitorParams
6 | import com.knight.transform.weave.ExtendClassWriter
7 | import org.apache.commons.io.FileUtils
8 | import org.apache.commons.io.IOUtils
9 | import org.objectweb.asm.ClassReader
10 | import org.objectweb.asm.ClassVisitor
11 | import org.objectweb.asm.ClassWriter
12 | import java.io.*
13 | import java.lang.Exception
14 | import java.nio.file.Files
15 | import java.nio.file.attribute.FileTime
16 | import java.util.zip.CRC32
17 | import java.util.zip.ZipEntry
18 | import java.util.zip.ZipFile
19 | import java.util.zip.ZipOutputStream
20 |
21 |
22 | class CodeWeaver(iPlugin: IPlugin) : IWeaver(iPlugin = iPlugin) {
23 |
24 |
25 | private val ZERO = FileTime.fromMillis(0)!!
26 |
27 | fun weaveClassToByteArray(intpuStream: InputStream): ByteArray {
28 | val classReader = ClassReader(intpuStream)
29 | val classWriter = ExtendClassWriter(classloader, classReader,
30 | ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
31 | val classVisitor = iPlugin.createWeaveClassVisitor(classWriter)
32 | try {
33 | classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
34 | } catch (e: Exception) {
35 | println("Exception occurred when visit code \n " + e.printStackTrace())
36 |
37 | }
38 | return classWriter.toByteArray()
39 | }
40 |
41 | fun weaveClassToByteChain(intpuStream: InputStream): ByteArray {
42 | iPlugin.getWeaveClassVisitorInterceptor()?.let {
43 | val chain = ClassVisitorChain(0, it, ClassVisitorParams(intpuStream.readBytes(), classloader))
44 | return chain.transform()
45 | } ?: return weaveClassToByteArray(intpuStream)
46 | }
47 |
48 | override fun weaveJar(inputJar: File, outputJar: File) {
49 | val inputZip = ZipFile(inputJar)
50 | val outputZipStream = ZipOutputStream(BufferedOutputStream(Files.newOutputStream(outputJar.toPath())))
51 | inputZip.entries().toList().forEach { entry ->
52 | val originalFile = BufferedInputStream(inputZip.getInputStream(entry))
53 | val outEntry = ZipEntry(entry.name)
54 | val newEntryContent: ByteArray
55 | if (!isWeaveableClass(outEntry.name.replace("/", "."))) {
56 | newEntryContent = IOUtils.toByteArray(originalFile)
57 | } else {
58 | newEntryContent = weaveClassToByteChain(originalFile)
59 | }
60 | val crc32 = CRC32()
61 | crc32.update(newEntryContent)
62 | outEntry.crc = crc32.value
63 | outEntry.method = (ZipEntry.STORED)
64 | outEntry.size = (newEntryContent.size.toLong())
65 | outEntry.compressedSize = (newEntryContent.size.toLong())
66 | outEntry.lastAccessTime = (ZERO)
67 | outEntry.lastModifiedTime = (ZERO)
68 | outEntry.creationTime = (ZERO)
69 | outputZipStream.putNextEntry(outEntry)
70 | outputZipStream.write(newEntryContent)
71 | outputZipStream.closeEntry()
72 | }
73 | outputZipStream.flush()
74 | outputZipStream.close()
75 | }
76 |
77 | override fun weaveFile(inputFile: File, outputFile: File, inputDir: String) {
78 | var inputBaseDir = inputDir
79 | if (!inputBaseDir.endsWith("/")) inputBaseDir += "/"
80 | if (isWeaveableClass(inputFile.absolutePath.replace(inputBaseDir, "").replace("/", "."))) {
81 | FileUtils.touch(outputFile)
82 | val inputStream = FileInputStream(inputFile)
83 | val bytes = weaveClassToByteChain(inputStream)
84 | val fos = FileOutputStream(outputFile)
85 | fos.write(bytes)
86 | fos.close()
87 | inputStream.close()
88 | } else {
89 | if (inputFile.isFile) {
90 | FileUtils.touch(outputFile)
91 | FileUtils.copyFile(inputFile, outputFile)
92 | }
93 | }
94 | }
95 |
96 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/KnightPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform
2 |
3 | import com.android.build.api.transform.Transform
4 | import com.android.build.gradle.*
5 | import com.android.build.gradle.api.BaseVariant
6 | import com.android.builder.model.AndroidProject
7 | import com.knight.transform.Interceptor.IClassVisitorInterceptor
8 | import com.knight.transform.Utils.Timer
9 | import org.gradle.api.*
10 | import transform.KnightTransform
11 | import transform.task.OutPutMappingTask
12 |
13 | abstract class KnightPlugin> : Plugin, IPlugin {
14 |
15 | protected lateinit var context: C
16 | protected lateinit var extension: E
17 |
18 | protected lateinit var project: Project
19 | protected lateinit var android: TestedExtension
20 | protected abstract val isNeedPrintMapAndTaskCostTime: Boolean
21 |
22 |
23 | abstract fun getContext(project: Project, extension: E, android: TestedExtension): C
24 | abstract fun createExtensions(): E
25 | lateinit var transform: KnightTransform
26 |
27 | override fun apply(project: Project) {
28 | if (!project.plugins.hasPlugin(AppPlugin::class.java) && !project.plugins.hasPlugin(LibraryPlugin::class.java)) {
29 | throw GradleException("'com.android.application' or 'com.android.library' plugin required!")
30 | }
31 |
32 | val aClass = if (project.plugins.hasPlugin(AppPlugin::class.java))
33 | AppExtension::class.java
34 | else LibraryExtension::class.java
35 | this.project = project
36 | android = project.extensions.getByType(aClass)
37 | extension = createExtensions()
38 | context = getContext(project, extension, android)
39 | if (android is AppExtension) {
40 | (android as AppExtension).applicationVariants.all {
41 | createTask(it, context)
42 | }
43 | } else if (android is LibraryExtension) {
44 | (android as LibraryExtension).libraryVariants.all {
45 | createTask(it, context)
46 | }
47 | }
48 | transform = KnightTransform(context, this, ::getTransformName)
49 | android.registerTransform(transform)
50 | }
51 |
52 | abstract fun getTransformName(): String
53 |
54 |
55 | override fun getScanClassVisitorInterceptor(): List? {
56 | return null
57 | }
58 |
59 | override fun getWeaveClassVisitorInterceptor(): List? {
60 | return null
61 | }
62 |
63 |
64 | open fun createTask(variant: BaseVariant, context: BaseContext<*>) {
65 | createWriteMappingTask(variant, context)
66 | }
67 |
68 | private fun createWriteMappingTask(variant: BaseVariant, context: BaseContext<*>) {
69 | if (!isNeedPrintMapAndTaskCostTime) {
70 | return
71 | }
72 | val mappingTaskName = "${transform.name}outputMappingFor${variant.name.capitalize()}"
73 | val myTask = project.tasks.getByName("transformClassesWith${transform.name}For${variant.name.capitalize()}")
74 | myTask.apply {
75 | doFirst {
76 | Timer.start(name)
77 | }
78 |
79 | doLast {
80 | Timer.stop(name)
81 | }
82 | }
83 | val outputMappingTask = project.tasks.create(mappingTaskName, OutPutMappingTask::class.java)
84 |
85 | outputMappingTask.apply {
86 | this as OutPutMappingTask
87 | classes.set(context.weavedClassMap)
88 | variantName.set(transform.name)
89 | outputMappingFile.set(com.android.utils.FileUtils.join(project.buildDir, AndroidProject.FD_OUTPUTS, "mapping",
90 | transform.name, variant.name, "${transform.name}Mapping.txt"))
91 |
92 | doFirst {
93 | Timer.start(name)
94 | }
95 |
96 | doLast {
97 | Timer.stop(name)
98 | }
99 | }
100 |
101 | myTask.finalizedBy(outputMappingTask)
102 | outputMappingTask.onlyIf { myTask.didWork }
103 | outputMappingTask.dependsOn(myTask)
104 | }
105 | }
--------------------------------------------------------------------------------
/spi-plugin-booster/src/main/java/com/afirez/spi/SpiSupervisor.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 |
4 | import com.didiglobal.booster.transform.asm.ClassTransformer
5 | import com.didiglobal.booster.transform.asm.asClassNode
6 | import com.didiglobal.booster.transform.asm.className
7 | import com.didiglobal.booster.transform.util.AbstractSupervisor
8 | import org.objectweb.asm.tree.AnnotationNode
9 |
10 | /**
11 | * 收集 spi service
12 | */
13 | class SpiSupervisor(
14 | // private val prefix: String,
15 | action: (HashMap>) -> Unit
16 | ) : AbstractSupervisor>>(action) {
17 | override fun accept(name: String): Boolean {
18 | return name.endsWith(".class", true)
19 | }
20 |
21 | override fun collect(name: String, data: () -> ByteArray) {
22 | val klass = data().asClassNode()
23 | val className = klass.name
24 | // val superName = klass.superName
25 | val interfaces = klass.interfaces
26 |
27 | val annotations = mutableListOf()
28 | if (klass.invisibleAnnotations != null) {
29 | annotations += klass.invisibleAnnotations
30 | }
31 | if (klass.visibleAnnotations != null) {
32 | annotations += klass.visibleAnnotations
33 | }
34 | annotations.forEach {
35 | // println("Annotation ${it.desc}")
36 | when (it.desc) {
37 | "L${SpiTransformer.spiAnotattion};" -> {
38 | /**
39 | * {api -> { path -> impl}} or {api -> { path -> impl}}
40 | */
41 | var extensionsMap = HashMap>()
42 | var anotattionName: Any? = null
43 | var anotattionValue: Any? = null
44 | if (!it.values.isNullOrEmpty()) {
45 | anotattionName = it.values[0]
46 | anotattionValue = it.values[1]
47 | }
48 | println("@SPI ( $anotattionName = '$anotattionValue' ) class $className:")
49 |
50 | if (interfaces?.size == 0) {
51 |
52 | val type = className
53 | val extension = className
54 | val path = if (anotattionValue == null) {
55 | className.replace("/", ".")
56 | } else {
57 | "$anotattionValue"
58 | }
59 |
60 | // var extensionMap = context.extensionsMap[type]
61 | var extensionMap = extensionsMap[type]
62 | if (extensionMap == null) {
63 | extensionMap = HashMap()
64 | extensionsMap[type] = extensionMap
65 | // println(" new Map:[ $type -> $extensionMap ]")
66 | }
67 | extensionMap[path] = extension
68 | action(extensionsMap)
69 | return
70 | }
71 |
72 | interfaces?.forEach {
73 | val type = it
74 | val extension = className
75 | println(" SPI: [ $type -> $extension ]")
76 | val path = if (anotattionValue == null) {
77 | className.replace("/", ".")
78 | } else {
79 | "$anotattionValue"
80 | }
81 | // var extensionMap = context.extensionsMap[type]
82 | var extensionMap = extensionsMap[type]
83 | if (extensionMap == null) {
84 | extensionMap = HashMap()
85 | // context.extensionsMap[type] = extensionMap!!
86 | extensionsMap[type] = extensionMap!!
87 | // println("Spi: new Map:[ $service -> $map ]")
88 | }
89 | extensionMap!![path] = extension
90 |
91 | action(extensionsMap)
92 | }
93 |
94 | println(" --> end class $className")
95 | }
96 |
97 | }
98 | }
99 |
100 |
101 | // action(HashMap>())
102 | }
103 |
104 |
105 | }
--------------------------------------------------------------------------------
/spi-app/src/main/java/com/afirez/spi/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.app
2 |
3 | import android.os.Bundle
4 | import android.widget.TextView
5 | import android.widget.Toast
6 | import androidx.appcompat.app.AppCompatActivity
7 | import com.afirez.spi.ExtensionLoader
8 | import com.afirez.spi.SPI
9 | import com.afirez.spi.component1.SpiRouter1Provider
10 | import com.afirez.spi.component2.SpiRouter2Provider
11 |
12 | /**
13 | * https://github.com/afirez/spi
14 | */
15 | @SPI(path = "/spi/activity/main")
16 | class MainActivity : AppCompatActivity() {
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.activity_main)
21 | val tv01AppLike = findViewById(R.id.tv01AppLike)
22 | val tv02Activity = findViewById(R.id.tv02Activity)
23 | val tv03Fragment = findViewById(R.id.tv03Fragment)
24 | val tv04Provider = findViewById(R.id.tv04Provider)
25 | val tv05RouterProvider = findViewById(R.id.tv05RouterProvider)
26 | val tv05More = findViewById(R.id.tv05More)
27 | tv01AppLike.setOnClickListener {
28 | /**
29 | * @see com.afirez.applike.AppLike
30 | */
31 | Toast.makeText(this@MainActivity, "@see com.afirez.applike.AppLike", Toast.LENGTH_SHORT).show()
32 | }
33 |
34 | tv02Activity.setOnClickListener {
35 | /**
36 | * @see com.afirez.spi.component1.impl.SpiActivity
37 | */
38 | ExtensionLoader.getInstance().loadExtension(SpiRouter1Provider::class.java).navSpiActivity()
39 | }
40 |
41 | tv03Fragment.setOnClickListener {
42 | /**
43 | * @see com.afirez.spi.component2.impl.SpiFragment
44 | */
45 | ExtensionLoader.getInstance().loadExtension(SpiRouter1Provider::class.java).navSpiFragmentActivity()
46 | }
47 |
48 | tv04Provider.setOnClickListener {
49 | /**
50 | * @see com.afirez.spi.component1.SpiJavaProvider
51 | */
52 | val spiJavaProvider =
53 | ExtensionLoader.getInstance().loadExtension(SpiRouter1Provider::class.java).spiJavaProvider()
54 | if (spiJavaProvider != null) {
55 | val helloJava = spiJavaProvider.helloJava()
56 | Toast.makeText(this@MainActivity, "spi $helloJava", Toast.LENGTH_SHORT).show()
57 | }
58 | /**
59 | * @see com.afirez.spi.component2.SpiKotlinProvider
60 | */
61 | val spiKotlinProvider =
62 | ExtensionLoader.getInstance().loadExtension(SpiRouter2Provider::class.java).spiKotlinProvider()
63 | if (spiJavaProvider != null) {
64 | val helloKotlin = spiKotlinProvider.helloKotlin()
65 | tv04Provider.postDelayed({
66 | Toast.makeText(this@MainActivity, "spi $helloKotlin", Toast.LENGTH_SHORT).show()
67 | }, 1000)
68 | }
69 | }
70 |
71 | tv05RouterProvider.setOnClickListener {
72 | /**
73 | * @see com.afirez.spi.component1.SpiRouter1Provider
74 | */
75 | Toast.makeText(
76 | this@MainActivity,
77 | "@see com.afirez.spi.component1.SpiRouter1Provider",
78 | Toast.LENGTH_SHORT
79 | ).show()
80 |
81 |
82 | /**
83 | * @see com.afirez.spi.component2.SpiRouter2Provider
84 | */
85 | tv05RouterProvider.postDelayed({
86 | Toast.makeText(
87 | this@MainActivity,
88 | "@see com.afirez.spi.component2.SpiRouter2Provider",
89 | Toast.LENGTH_SHORT
90 | ).show()
91 | }, 1000)
92 | }
93 |
94 | tv05More.setOnClickListener {
95 | Toast.makeText(
96 | this@MainActivity,
97 | "1 、 SPI 是一个方便接口聚合与分发的工具,很多接口扩展的场景都需要用到, 让我们的代码遵循开闭原则 !例如 AppLike 实现 Application 生命周期分发, 请参考 AppLike 和 Provider 等用例自行扩展。\n"
98 | + "2、 SPI 本身已有一个路由表,可以为接口或类(类支持 Activity 和 Fragment )的实现类指定 path 路径 , 方便我们拿到指定 path 的 实现类 。 如果你想自己实现组件化的路由, 完全可以基于 spi 实现 \n"
99 | + "3、 SPI 聚焦于接口发现与注册和路由表实现, 仅仅基于 SPI 实现 Android 组件化, 模块解偶很彻底, 但是缺少路由的一些功能,后续考虑开源一个路由项目,敬请期待!",
100 | Toast.LENGTH_LONG
101 | ).show()
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/spi-applike/src/main/java/com/afirez/applike/AppDelegate.java:
--------------------------------------------------------------------------------
1 | package com.afirez.applike;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.os.Bundle;
7 | import com.afirez.spi.ExtensionLoader;
8 |
9 | import java.util.Map;
10 | import java.util.Set;
11 |
12 | /**
13 | * https://github.com/afirez/spi
14 | */
15 | public class AppDelegate {
16 |
17 |
18 | public static AppDelegate getInstance() {
19 | return SingletonHolder.INSTANCE;
20 | }
21 |
22 | private static class SingletonHolder {
23 | static AppDelegate INSTANCE = new AppDelegate();
24 | }
25 |
26 | private Application app;
27 |
28 | private Activity topActivity;
29 |
30 | public Application app() {
31 | return app;
32 | }
33 |
34 | public Activity topActivity() {
35 | return topActivity;
36 | }
37 |
38 | public Context topActivityOrApp() {
39 | return topActivity == null ? app : topActivity;
40 | }
41 |
42 | public void attachBaseContext(Application app, Context base) {
43 | this.app = app;
44 | Map appLikeMap = ExtensionLoader.getInstance().loadExtensions(AppLike.class);
45 | if (appLikeMap == null) {
46 | return;
47 | }
48 | Set> entries = appLikeMap.entrySet();
49 |
50 | for (Map.Entry entry : entries) {
51 | if (entry == null) {
52 | continue;
53 | }
54 |
55 | AppLike appLike = entry.getValue();
56 | if (appLike == null) {
57 | continue;
58 | }
59 |
60 | appLike.attachBaseContext(app, base);
61 | }
62 | }
63 |
64 | public void onCreate(Application app) {
65 | this.app = app;
66 | app.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
67 |
68 | Map appLikeMap = ExtensionLoader.getInstance().loadExtensions(AppLike.class);
69 | if (appLikeMap == null) {
70 | return;
71 | }
72 |
73 | Set> entries = appLikeMap.entrySet();
74 |
75 | for (Map.Entry entry : entries) {
76 | if (entry == null) {
77 | continue;
78 | }
79 |
80 | AppLike appLike = entry.getValue();
81 | if (appLike == null) {
82 | continue;
83 | }
84 |
85 | appLike.onCreate(app);
86 | }
87 | }
88 |
89 |
90 | public void onTerminate(Application app) {
91 | Map appLikeMap = ExtensionLoader.getInstance().loadExtensions(AppLike.class);
92 | if (appLikeMap == null) {
93 | return;
94 | }
95 | Set> entries = appLikeMap.entrySet();
96 |
97 | for (Map.Entry entry : entries) {
98 | if (entry == null) {
99 | continue;
100 | }
101 |
102 | AppLike appLike = entry.getValue();
103 | if (appLike == null) {
104 | continue;
105 | }
106 |
107 | appLike.onTerminate(app);
108 | }
109 | }
110 |
111 |
112 | private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
113 | new Application.ActivityLifecycleCallbacks() {
114 | @Override
115 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
116 |
117 | }
118 |
119 | @Override
120 | public void onActivityStarted(Activity activity) {
121 |
122 | }
123 |
124 | @Override
125 | public void onActivityResumed(Activity activity) {
126 | topActivity = activity;
127 | }
128 |
129 | @Override
130 | public void onActivityPaused(Activity activity) {
131 | topActivity = null;
132 | }
133 |
134 | @Override
135 | public void onActivityStopped(Activity activity) {
136 |
137 | }
138 |
139 | @Override
140 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
141 |
142 | }
143 |
144 | @Override
145 | public void onActivityDestroyed(Activity activity) {
146 |
147 | }
148 | };
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/spi-plugin-booster/src/main/java/com/afirez/spi/SpiTransformer.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi
2 |
3 | import com.didiglobal.booster.transform.TransformContext
4 | import com.didiglobal.booster.transform.asm.ClassTransformer
5 | import com.didiglobal.booster.transform.asm.defaultInit
6 | import com.didiglobal.booster.transform.util.NameCollector
7 | import com.google.auto.service.AutoService
8 | import org.objectweb.asm.Opcodes
9 | import org.objectweb.asm.Type
10 | import org.objectweb.asm.tree.*
11 |
12 | @AutoService(ClassTransformer::class)
13 | class SpiTransformer : ClassTransformer {
14 |
15 | val services = HashMap>()
16 |
17 | override fun onPreTransform(context: TransformContext) {
18 | context.registerCollector(SpiSupervisor {
19 | it.forEach { api, newImplMap ->
20 | val implMap = services.getOrDefault(api, HashMap())
21 | implMap += newImplMap
22 | services.put(api, implMap)
23 | }
24 | })
25 |
26 | // 一旦有 JAR/DIR 中包含 ${SERVICE_REGISTRY} 这个文件
27 | // 则强制走 transform 的流程,无论是全量还是增量
28 | context.registerCollector(NameCollector(SERVICE_REGISTRY))
29 | }
30 |
31 | override fun transform(context: TransformContext, klass: ClassNode) = klass.apply {
32 | val name = klass.name
33 | when (name) {
34 | SERVICE_REGISTRY -> {
35 | // println("services ${services.size}")
36 | val init = methods.find {
37 | it.name == "" && it.desc == "()V"
38 | } ?: defaultInit.apply {
39 | methods.add(this)
40 | }
41 |
42 | val pre = InsnList()
43 | val post = InsnList()
44 | var beforeReturn = true
45 | val oldInstructions = init.instructions
46 | val size = oldInstructions.size()
47 | for (i in 0 until size) {
48 | val inst = oldInstructions.get(i)
49 | if (beforeReturn) {
50 | // if (inst is InsnNode) {
51 | when (inst.opcode) {
52 | Opcodes.IRETURN,
53 | Opcodes.FRETURN,
54 | Opcodes.ARETURN,
55 | Opcodes.LRETURN,
56 | Opcodes.DRETURN,
57 | Opcodes.RETURN -> {
58 | beforeReturn = false
59 | }
60 | else -> {}
61 | }
62 | // }
63 | }
64 | if (beforeReturn) {
65 | pre.add(inst)
66 | } else {
67 | post.add(inst)
68 | }
69 | }
70 |
71 | val newInsnList = InsnList().apply {
72 | add(pre)
73 |
74 | services.forEach { api, implMap ->
75 | implMap.forEach { path, impl ->
76 | add(VarInsnNode(Opcodes.ALOAD, 0))
77 | // class of api
78 | add(LdcInsnNode(Type.getObjectType(api)))
79 | // class of implementation
80 | add(LdcInsnNode(Type.getObjectType(impl)))
81 | // path to class of api
82 | add(LdcInsnNode(path))
83 | // ServiceRegistry.register(interface, path, implementation)
84 | add(
85 | MethodInsnNode(
86 | Opcodes.INVOKEVIRTUAL,
87 | SERVICE_REGISTRY,
88 | addService,
89 | "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)V",
90 | )
91 | )
92 | }
93 | }
94 |
95 | add(post)
96 | // add(InsnNode(Opcodes.RETURN))
97 | }
98 | init.instructions = newInsnList
99 | }
100 | }
101 | }
102 |
103 | companion object {
104 | val spiAnotattion: String = "com/afirez/spi/SPI"
105 | // private const val SERVICE_REGISTRY = "com/your/package/ServiceRegistry.class"
106 | // private const
107 |
108 | val SERVICE_REGISTRY = "com/afirez/spi/ExtensionLoader"
109 | val addService: String = "addExtension"
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/spi-app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # 你可能需要 SPI 了
2 |
3 | 
4 |
5 | ## 为什么会开源这个 SPI 项目 ?
6 |
7 | 在项目组件化的过程中,曾通过 AndoridManifest.xml 注册的方式实现过 Application 生命周期的组件化,类似 Glide 中解析 AndoridManifest.xml 发现 GlideModule 的机制,有点繁琐。而后了解到 Google 的 AutoService,通过注解加 Java 原生 SPI,实现 Appcation 生命周期的组件化,好像刚刚好。SPI 可以没有,没有 SPI,可以用其他方案,比如说路由。首先,一般路由基本在提供路由该提供的功能,路由发现和注册机制局限在路由框架内部,不会提供 SPI,如果用路由实现,每添加一个需要 Application 生命周期的组件都需要通过在 Application 中添加代码,不是那么好维护。SPI 也可以到处都是,随着项目组件化后,发现很多地方都需要用到 SPI 机制,AutoService 加原生 SPI 机制,除了有反射还有IO,在接口非常多的时候,比较耗时。而 SPI 机制是遵循开闭原则的,这个事情也是在脑海里留下了不可描述的印象......于是慢慢的,才有了这个 SPI 项目。
8 |
9 | ## 在项目中引入 [SPI](https://github.com/afirez/spi)
10 |
11 | 添加 **spi-gradle-plugin** 插件到你的项目
12 |
13 | ```
14 |
15 | buildscript {
16 | ext {
17 | kotlin_version = '1.5.31'
18 | booster_version = '4.13.0'
19 | }
20 |
21 | repositories {
22 | jcenter()
23 |
24 | // add maven repository for spi-plugin at build.gradle file of root project
25 | maven { url "https://raw.githubusercontent.com/afirez/spi/master/repo/" }
26 | }
27 |
28 | dependencies {
29 | classpath 'com.afirez.spi:spi-gradle-plugin:3.0.0'
30 | // ①
31 | classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"
32 | // ② figure out the features you really need, then choose the right module for integration
33 | // ② 弄清楚真正需要的特性,然后从下面的模块列表中选择正确的模块进行集成
34 | // classpath "com.didiglobal.booster:booster-task-all:$booster_version"
35 | // classpath "com.didiglobal.booster:booster-transform-all:$booster_version"
36 |
37 | // gradle plugin version <= 4.1.2
38 | // classpath 'com.afirez.spi:spi-gradle-plugin:2.0.0'
39 |
40 | // gradle plugin version <= 3.6.4
41 | // classpath 'com.afirez.spi:spi-gradle-plugin:1.0.1'
42 | }
43 | }
44 |
45 | // in module build.gradle
46 | apply plugin: 'com.android.application'
47 |
48 | // didi booster
49 | apply plugin: 'com.didiglobal.booster' // ③
50 |
51 | // gradle plugin version <= 4.1.2
52 | // apply plugin: 'spi'
53 | // or apply plugin: 'com.afirez.spi'
54 | ```
55 |
56 | 添加 **spi** 到需要的子模块
57 |
58 | ```
59 | allprojects {
60 | repositories {
61 | ...
62 |
63 | mavenCentral()
64 | google()
65 | // jcenter()
66 |
67 | // didi booster
68 | maven { url 'https://oss.sonatype.org/content/repositories/public/' }
69 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
70 |
71 | // add maven repository for spi at build.gradle file of root project
72 | maven { url "https://raw.githubusercontent.com/afirez/spi/master/repo/" }
73 |
74 | ...
75 | }
76 | }
77 | ```
78 |
79 | ```
80 |
81 | implementation "com.afirez.spi:spi:1.0.1"
82 | ```
83 |
84 | ## 如何使用 SPI
85 |
86 | ### 1、从 SPI AppLike 谈起
87 |
88 | 
89 |
90 | 实现 AppLike 接口,并添加 @SPI 注解,该实现者 App 将会在编译时被发现和注册到 ExtensionLoader 中,如下图所示:
91 |
92 | 
93 |
94 | 然后, AppDelegate 可以通过 ExtensionLoader 拿到所有 AppLike 的实现者并分发 Application 的生命周期。如下图所示:
95 |
96 | 
97 |
98 | 最后,记得在项目的 App 中调用 AppDelegate 相应方法(attachBaseContext,onCreate,onTerminate 等方法),这样其他模块便有了 Application 的生命周期。
99 |
100 | 
101 |
102 | ### 2、SPI Actiivty
103 |
104 | 
105 |
106 | 被 @SPI 注解过的 Activity ,会在编译期被发现并注册到 ExtensionLoader,然后可以通过 ExtensionLoader 获取指定路径 path 的 Activity 类。(如果 @SPI 中没有路径 path 的值,路径 path 的值将会是被注解类的包名加类名)
107 |
108 | ### 3、SPI Fragment
109 |
110 | 
111 |
112 | 被 @SPI 注解过的 Fragment,会在编译期被发现并注册到 ExtensionLoader,然后可以通过 ExtensionLoader 获取指定路径 path 的 Fragment 类。(如果 @SPI 中没有路径 path 的值,路径 path 的值将会是被注解类的包名加类名)
113 |
114 | ### 4、SPI Provider
115 |
116 | 
117 |
118 | 被 @SPI 注解过的 SpiKotlinProviderImpl,同样会在编译期被发现并注册到 ExtensionLoader,然后可以通过 ExtensionLoader 获取指定路径 path 的 SpiKotlinProvider 实现类。(如果 @SPI 中没有路径 path 的值,路径 path 的值将会是被注解类的包名加类名)
119 |
120 | ### 5、用 SPI 搞更多事情
121 |
122 | SPI 是一个方便接口聚合与分发的工具,很多接口扩展的场景都需要用到,让我们的代码遵循开闭原则!例如 AppLike 实现 Application 生命周期组件化,请参考 AppLike 等用例自行扩展。
123 |
124 | SPI 其实本身已有一个路由表,可以为接口或类(类支持 Activity 和 Fragment)的实现类指定 path 路径,方便我们拿到指定 path 的 实现类。如果你想自己实现组件化的路由,完全可以基于 spi 实现
125 |
126 | SPI 聚焦于接口发现与注册和路由表,仅仅基于 SPI 实现 Android 组件化,模块解偶很彻底,但是缺少路由的一些功能,后续考虑开源一个路由项目,敬请期待!
127 |
128 | 去 [spi](https://github.com/afirez/spi) github 下载源码试试, 了解更多细节。
129 |
130 | ## 感谢
131 |
132 | - [ASM](https://asm.ow2.io/)
133 | - [hunter](https://github.com/Leaking/Hunter)
134 | - [KnightTransform](https://github.com/kakayang2011/KnightTransform)
135 | - [booster](https://github.com/didi/booster)
136 |
137 | 
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/transform/Transform.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.transform
2 |
3 | import com.android.build.api.transform.Format
4 | import com.android.build.api.transform.Status
5 | import com.android.build.api.transform.TransformInvocation
6 | import com.android.ide.common.internal.WaitableExecutor
7 | import com.knight.transform.BaseContext
8 | import com.knight.transform.asm.IWeaver
9 | import org.apache.commons.io.FileUtils
10 | import java.io.File
11 |
12 |
13 | class Transform(val myContext: BaseContext<*>, val weaver: IWeaver) {
14 | val waitableExecutor = WaitableExecutor.useGlobalSharedThreadPool()
15 |
16 | fun transform(transform: TransformInvocation) {
17 | transform.apply {
18 | if (!isIncremental) outputProvider.deleteAll()
19 | inputs.forEach {
20 | it.jarInputs.forEach { jarInput ->
21 | val status = jarInput.status
22 | val dest = outputProvider.getContentLocation(
23 | jarInput.file.absolutePath,
24 | jarInput.contentTypes,
25 | jarInput.scopes,
26 | Format.JAR)
27 | if (isIncremental) {
28 | when (status) {
29 | Status.ADDED, Status.CHANGED -> {
30 | transformJar(jarInput.file, dest, myContext.extension.isScanJar)
31 | }
32 | Status.REMOVED -> {
33 | if (dest.exists()) {
34 | FileUtils.forceDelete(dest)
35 | }
36 | }
37 | else -> {
38 | }
39 | }
40 | } else {
41 | transformJar(jarInput.file, dest, myContext.extension.isScanJar)
42 | }
43 | }
44 |
45 | it.directoryInputs.forEach { directoryInput ->
46 | val dest = outputProvider.getContentLocation(
47 | directoryInput.name,
48 | directoryInput.contentTypes,
49 | directoryInput.scopes,
50 | Format.DIRECTORY)
51 | FileUtils.forceMkdir(dest)
52 | if (isIncremental) {
53 | val srcDirPath = directoryInput.file.absolutePath
54 | val destDirPath = dest.absolutePath
55 | directoryInput.changedFiles.forEach { inputFile, status ->
56 | val destFilePath = inputFile.absolutePath.replace(srcDirPath, destDirPath)
57 | val destFile = File(destFilePath)
58 | when (status) {
59 | Status.ADDED, Status.CHANGED -> {
60 | FileUtils.touch(destFile)
61 | transformSingleFile(inputFile, destFile, srcDirPath)
62 | }
63 | Status.REMOVED -> {
64 | if (destFile.exists()) {
65 | destFile.delete()
66 | }
67 | }
68 | else -> {
69 | }
70 | }
71 | }
72 | } else {
73 | transformDir(directoryInput.file, dest)
74 | }
75 | }
76 | }
77 | }
78 | waitableExecutor.waitForTasksWithQuickFail(true)
79 | }
80 |
81 | private fun transformJar(srcJar: File, destJar: File, isNeedScan: Boolean) {
82 | waitableExecutor.execute {
83 | if (isNeedScan) {
84 | weaver.weaveJar(srcJar, destJar)
85 | } else {
86 | FileUtils.copyFile(srcJar, destJar)
87 | }
88 | }
89 | }
90 |
91 | private fun transformSingleFile(inputFile: File, outputFile: File, srcDir: String) {
92 | waitableExecutor.execute {
93 | weaver.weaveFile(inputFile, outputFile, srcDir)
94 | }
95 | }
96 |
97 | private fun transformDir(inputFile: File, outputFile: File) {
98 | val inputDirPath = inputFile.absolutePath
99 | val outputDirPath = outputFile.absolutePath
100 | if (inputFile.isDirectory) {
101 | com.android.utils.FileUtils.getAllFiles(inputFile).forEach {
102 | waitableExecutor.execute {
103 | try {
104 | val filePath = it.absolutePath
105 | val outputFile = File(filePath.replace(inputDirPath, outputDirPath))
106 | weaver.weaveFile(it, outputFile, inputDirPath)
107 | } catch (e: Exception) {
108 | println("error: ${e}")
109 | }
110 | }
111 | }
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/knight-transform/src/main/kotlin/com/knight/transform/weave/ExtendClassWriter.kt:
--------------------------------------------------------------------------------
1 | package com.knight.transform.weave
2 |
3 | import org.objectweb.asm.ClassReader
4 | import org.objectweb.asm.ClassWriter
5 | import org.objectweb.asm.Opcodes
6 | import java.io.IOException
7 |
8 | class ExtendClassWriter(private val urlClassLoader: ClassLoader?, classReader: ClassReader? = null, flags: Int) : ClassWriter(classReader, flags) {
9 |
10 | override fun getCommonSuperClass(type1: String?, type2: String?): String? {
11 | if (urlClassLoader == null) {
12 | super.getCommonSuperClass(type1, type2)
13 | }
14 | if (type1 == null || type1 == OBJECT || type2 == null || type2 == OBJECT) {
15 | return OBJECT
16 | }
17 | if (type1 == type2) {
18 | return type1
19 | }
20 | val type1ClassReader = getClassReader(type1)
21 | val type2ClassReader = getClassReader(type2)
22 | if (type1ClassReader == null || type2ClassReader == null) {
23 | return OBJECT
24 | }
25 | if (isInterface(type1ClassReader)) {
26 | var interfaceName: String = type1
27 | if (isImplements(interfaceName, type2ClassReader)) {
28 | return interfaceName
29 | }
30 | if (isInterface(type2ClassReader)) {
31 | interfaceName = type2
32 | if (isImplements(interfaceName, type1ClassReader)) {
33 | return interfaceName
34 | }
35 | }
36 | return OBJECT
37 | }
38 | if (isInterface(type2ClassReader)) {
39 | return if (isImplements(type2, type1ClassReader)) {
40 | type2
41 | } else OBJECT
42 | }
43 | val superClassNames = HashSet()
44 | superClassNames.add(type1)
45 | superClassNames.add(type2)
46 | var type1SuperClassName = type1ClassReader.superName
47 | if (!superClassNames.add(type1SuperClassName)) {
48 | return type1SuperClassName
49 | }
50 | var type2SuperClassName = type2ClassReader.superName
51 | if (!superClassNames.add(type2SuperClassName)) {
52 | return type2SuperClassName
53 | }
54 | while (type1SuperClassName != null || type2SuperClassName != null) {
55 | if (type1SuperClassName != null) {
56 | type1SuperClassName = getSuperClassName(type1SuperClassName)
57 | if (type1SuperClassName != null) {
58 | if (!superClassNames.add(type1SuperClassName)) {
59 | return type1SuperClassName
60 | }
61 | }
62 | }
63 | if (type2SuperClassName != null) {
64 | type2SuperClassName = getSuperClassName(type2SuperClassName)
65 | if (type2SuperClassName != null) {
66 | if (!superClassNames.add(type2SuperClassName)) {
67 | return type2SuperClassName
68 | }
69 | }
70 | }
71 | }
72 | return OBJECT
73 | }
74 |
75 | private fun isImplements(interfaceName: String?, classReader: ClassReader): Boolean {
76 | var classInfo: ClassReader? = classReader
77 | while (classInfo != null) {
78 | val interfaceNames = classInfo.interfaces
79 | for (name in interfaceNames) {
80 | if (name != null && name == interfaceName) {
81 | return true
82 | }
83 | }
84 | for (name in interfaceNames) {
85 | if (name != null) {
86 | val interfaceInfo = getClassReader(name)
87 | if (interfaceInfo != null) {
88 | if (isImplements(interfaceName, interfaceInfo)) {
89 | return true
90 | }
91 | }
92 | }
93 | }
94 | val superClassName = classInfo.superName
95 | if (superClassName == null || superClassName == OBJECT) {
96 | break
97 | }
98 | classInfo = getClassReader(superClassName)
99 | }
100 | return false
101 | }
102 |
103 | private fun isInterface(classReader: ClassReader): Boolean {
104 | return classReader.getAccess() and Opcodes.ACC_INTERFACE !== 0
105 | }
106 |
107 | private fun getSuperClassName(className: String): String? {
108 | val classReader = getClassReader(className) ?: return null
109 | return classReader.superName
110 | }
111 |
112 | private fun getClassReader(className: String): ClassReader? {
113 | val inputStream = urlClassLoader?.getResourceAsStream("$className.class")
114 | try {
115 | if (inputStream != null) {
116 | return ClassReader(inputStream)
117 | }
118 | } catch (ignored: IOException) {
119 | } finally {
120 | if (inputStream != null) {
121 | try {
122 | inputStream.close()
123 | } catch (ignored: IOException) {
124 | }
125 |
126 | }
127 | }
128 | return null
129 | }
130 |
131 | companion object {
132 | val TAG = "ExtendClassWriter"
133 | private val OBJECT = "java/lang/Object"
134 | }
135 | }
--------------------------------------------------------------------------------
/spi-plugin/src/main/java/com/afirez/spi/weave/SpiScanClassVisitor.kt:
--------------------------------------------------------------------------------
1 | package com.afirez.spi.weave
2 |
3 | import com.afirez.spi.SpiContext
4 | import com.afirez.spi.SpiExtension
5 | import com.knight.transform.weave.BaseClassVisitor
6 | import org.objectweb.asm.*
7 |
8 | /**
9 | * @link https://github.com/afirez/spi
10 | */
11 | class SpiScanClassVisitor(context: SpiContext, classWriter: ClassWriter) :
12 | BaseClassVisitor(context, classWriter) {
13 | var superName: String? = null
14 | var interfaces: Array? = null
15 | val extension = context.extension as SpiExtension
16 |
17 | override fun visit(
18 | version: Int,
19 | access: Int,
20 | name: String,
21 | signature: String?,
22 | superName: String?,
23 | interfaces: Array?
24 | ) {
25 | super.visit(version, access, name, signature, superName, interfaces)
26 | this.superName = superName
27 | this.interfaces = interfaces
28 | }
29 |
30 | override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
31 | var av = super.visitAnnotation(descriptor, visible)
32 |
33 | when (descriptor) {
34 | "L${extension.spiPath};" -> {
35 | av = SpiScanAnnotationVisitor(av)
36 | }
37 | }
38 |
39 | return av
40 | }
41 |
42 |
43 | inner class SpiScanAnnotationVisitor(av: AnnotationVisitor) : AnnotationVisitor(Opcodes.ASM5, av) {
44 | var name: String? = null
45 | var value: Any? = null
46 |
47 |
48 | override fun visit(name: String?, value: Any?) {
49 | super.visit(name, value)
50 | this.name = name
51 | this.value = value
52 | }
53 |
54 | override fun visitEnd() {
55 | super.visitEnd()
56 | println("@SPI ( $name = '$value' ) class $className:")
57 |
58 | if (interfaces?.size == 0) {
59 |
60 | val type = className
61 | val extension = className
62 | val path = if (value == null) {
63 | className.replace("/", ".")
64 | } else {
65 | "$value"
66 | }
67 | var extensionMap = context.extensionsMap[type]
68 | if (extensionMap == null) {
69 | extensionMap = HashMap()
70 | context.extensionsMap[type] = extensionMap
71 | // println(" new Map:[ $type -> $extensionMap ]")
72 | }
73 | extensionMap[path] = extension
74 | return
75 | }
76 |
77 | interfaces?.forEach {
78 | val type = it
79 | val extension = className
80 | println(" SPI: [ $type -> $extension ]")
81 | val path = if (value == null) {
82 | className.replace("/", ".")
83 | } else {
84 | "$value"
85 | }
86 | var extensionMap = context.extensionsMap[type]
87 | if (extensionMap == null) {
88 | extensionMap = HashMap()
89 | context.extensionsMap[type] = extensionMap!!
90 | // println("Spi: new Map:[ $service -> $map ]")
91 | }
92 | extensionMap!![path] = extension
93 | }
94 |
95 | println(" --> end class $className")
96 | }
97 | }
98 |
99 |
100 | // private fun typeOf(className: String?, targetclassName: String): Boolean {
101 | // className ?: return false
102 | //
103 | // if (className == "Ljava/lang/Object") {
104 | // return false
105 | // }
106 | // if (className == targetclassName) {
107 | // return true
108 | // }
109 | //
110 | // return typeOf(getSuperClass(className), targetclassName)
111 | //
112 | // }
113 |
114 | // List getSuperClasses(className){
115 | // superClass=getSuperClass(className)
116 | // return superClass+getSuperClasses(superClass)
117 | // }
118 | //
119 | // String getSuperClass(className) {
120 | // cw=new ClassWriter()
121 | // v=new SuperClassReadingClassVisitor(cw)
122 | // new ClassReader(className).accept(v)
123 | // return v.superClass
124 | // }
125 |
126 | // fun getSuperClass(className: String?): String? {
127 | // className ?: return null
128 | //
129 | // try {
130 | // val classReader = ClassReader(className)
131 | // val cv = object : ClassVisitor(Opcodes.ASM5, cv) {
132 | // var superName: String? = ""
133 | // override fun visit(
134 | // version: Int,
135 | // access: Int,
136 | // name: String?,
137 | // signature: String?,
138 | // superName: String?,
139 | // interfaces: Array?
140 | // ) {
141 | // this.superName = superName
142 | // super.visit(version, access, name, signature, superName, interfaces)
143 | // }
144 | // }
145 | // classReader.accept(cv, ClassReader.SKIP_FRAMES)
146 | // return cv.superName
147 | // } catch (e: Throwable) {
148 | // e.printStackTrace()
149 | // return null
150 | // }
151 | // }
152 |
153 | }
154 |
--------------------------------------------------------------------------------