├── .gitignore ├── LICENSE.txt ├── build.gradle ├── easyrouter-annotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── zane │ └── easyrouter_annotation │ ├── Param.java │ ├── Result.java │ └── Route.java ├── easyrouter-compiler ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── zane │ │ └── easyrouter_compiler │ │ ├── Constant.java │ │ ├── entity │ │ ├── ParameAnnotationClass.java │ │ ├── ParameAnnotationGenerator.java │ │ ├── RouterAnnotationClass.java │ │ └── RouterAnnotationGenerator.java │ │ ├── exception │ │ ├── NoSuchUrlException.java │ │ └── OnlyClassException.java │ │ └── process │ │ ├── BaseProcess.java │ │ ├── RouterProcess.java │ │ ├── Validator.java │ │ └── zParameProcess.java │ └── test │ └── java │ └── com │ └── example │ ├── ParameAnntationClassesTest.java │ └── RouterUtilTest.java ├── easyrouter-generated ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── zane │ │ └── easyrouter_generated │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zane │ │ │ └── easyrouter_generated │ │ │ └── EasyRouterTable.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── example │ └── zane │ └── easyrouter_generated │ └── ExampleUnitTest.java ├── easyrouter-merge ├── build.gradle └── src │ └── main │ ├── java │ └── me │ │ └── zane │ │ └── easyrouter_merge │ │ ├── ApplicationTransform.java │ │ ├── LibraryTransform.java │ │ ├── RouterModulePlugin.java │ │ ├── bean │ │ └── MergeInfo.java │ │ ├── collect │ │ ├── CollectClassVisitor.java │ │ ├── CollectEngine.java │ │ └── CollectHandler.java │ │ ├── framework │ │ ├── ClassHandler.java │ │ ├── ContextReader.java │ │ ├── DirectoryContentProvider.java │ │ ├── DirectoryWriter.java │ │ ├── JarContentProvider.java │ │ ├── JarWriter.java │ │ ├── QualifiedContentProvider.java │ │ ├── TargetedQualifiedContentProvider.java │ │ └── TransformContext.java │ │ ├── log │ │ ├── ILogger.java │ │ ├── Impl │ │ │ ├── BaseLogger.java │ │ │ ├── FileLoggerImpl.java │ │ │ ├── NoLogger.java │ │ │ ├── SystemLoggerImpl.java │ │ │ └── SystemOutputImpl.java │ │ └── Log.java │ │ ├── merge │ │ ├── MergeClassVisitor.java │ │ ├── MergeEngine.java │ │ └── MergeHandler.java │ │ └── rename │ │ ├── RenameClassVisistor.java │ │ ├── RenameEngine.java │ │ ├── RenameHandler.java │ │ └── RenameKey.java │ └── resources │ └── META-INF │ └── gradle-plugins │ └── me.zane.easyrouter-merge.properties ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md ├── router ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── zane │ │ └── router │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zane │ │ │ └── router │ │ │ ├── Constant.java │ │ │ ├── EasyRouter.java │ │ │ ├── EasyRouterSet.java │ │ │ ├── converter │ │ │ ├── Converter.java │ │ │ └── GsonConventerFactory.java │ │ │ ├── exception │ │ │ └── ConverterExpection.java │ │ │ ├── hook │ │ │ ├── Hooker.java │ │ │ ├── OnCreatHooker.java │ │ │ ├── OnCreatListener.java │ │ │ └── RouterInstrumentation.java │ │ │ ├── inject │ │ │ ├── Inject.java │ │ │ └── InjectMan.java │ │ │ ├── message │ │ │ ├── Message.java │ │ │ └── MessageBuilder.java │ │ │ ├── result │ │ │ ├── ActivityResultEngine.java │ │ │ ├── HookFragment.java │ │ │ └── OnActivityResultListener.java │ │ │ ├── router │ │ │ ├── ActivityRouter.java │ │ │ ├── BaseRouter.java │ │ │ ├── HttpRouter.java │ │ │ └── Table.java │ │ │ └── utils │ │ │ └── ZLog.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── example │ └── zane │ └── router │ └── ExampleUnitTest.java ├── sample-library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── zane │ │ └── sample_library │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── me │ │ │ └── zane │ │ │ └── sample_library │ │ │ └── LibraryActivityOne.java │ └── res │ │ ├── layout │ │ └── activity_library_main.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── me │ └── zane │ └── sample_library │ └── ExampleUnitTest.java ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── zane │ │ └── easyrouter │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zane │ │ │ └── easyrouter │ │ │ ├── ActivityThree.java │ │ │ ├── ActivityTwo.java │ │ │ ├── App.java │ │ │ ├── FragmentTwo.java │ │ │ ├── MainActivity.java │ │ │ └── Person.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_three.xml │ │ ├── activity_two.xml │ │ └── fragment_two.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── zane │ └── easyrouter │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/android 2 | 3 | ### Android ### 4 | # Built application files 5 | *.apk 6 | *.ap_ 7 | 8 | # Files for the Dalvik VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # Generated files 15 | bin/ 16 | gen/ 17 | out/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | 23 | # Local configuration file (sdk path, etc) 24 | local.properties 25 | 26 | # Proguard folder generated by Eclipse 27 | proguard/ 28 | 29 | # Log Files 30 | *.log 31 | 32 | # Android Studio Navigation editor temp files 33 | .navigation/ 34 | 35 | # Android Studio captures folder 36 | captures/ 37 | 38 | # Intellij 39 | *.iml 40 | .idea 41 | 42 | # Keystore files 43 | *.jks 44 | 45 | ### Android Patch ### 46 | gen-external-apklibs 47 | Contact GitHub API Training Shop Blog About 48 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.0.1' 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 11 | classpath 'com.novoda:bintray-release:0.8.1' 12 | classpath 'com.zane:easyrouterMerge:1.0.0' 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | google() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /easyrouter-annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /easyrouter-annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | apply plugin: 'maven' 4 | group='com.github.Zane96' 5 | 6 | dependencies { 7 | compile fileTree(dir: 'libs', include: ['*.jar']) 8 | } 9 | 10 | sourceCompatibility = "1.7" 11 | targetCompatibility = "1.7" 12 | -------------------------------------------------------------------------------- /easyrouter-annotation/src/main/java/com/zane/easyrouter_annotation/Param.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Created by Zane on 2016/12/28. 11 | * Email: zanebot96@gmail.com 12 | * Blog: zane96.github.io 13 | */ 14 | 15 | @Documented 16 | @Target(ElementType.FIELD) 17 | @Retention(RetentionPolicy.CLASS) 18 | public @interface Param { 19 | String value(); 20 | } 21 | -------------------------------------------------------------------------------- /easyrouter-annotation/src/main/java/com/zane/easyrouter_annotation/Result.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 用这个注解标记跳转的返回对象 11 | * 12 | * Created by Zane on 2017/4/17. 13 | * Email: zanebot96@gmail.com 14 | * Blog: zane96.github.io 15 | */ 16 | 17 | @Documented 18 | @Target(ElementType.FIELD) 19 | @Retention(RetentionPolicy.CLASS) 20 | public @interface Result { 21 | String value(); 22 | } 23 | -------------------------------------------------------------------------------- /easyrouter-annotation/src/main/java/com/zane/easyrouter_annotation/Route.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Created by Zane on 2016/12/23. 11 | * Email: zanebot96@gmail.com 12 | * Blog: zane96.github.io 13 | */ 14 | 15 | @Documented 16 | @Target(ElementType.TYPE) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | public @interface Route{ 19 | /** 20 | * 某个类的路由信息 21 | * @return 22 | */ 23 | String value() default "null"; 24 | } 25 | -------------------------------------------------------------------------------- /easyrouter-compiler/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /easyrouter-compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group='com.github.Zane96' 4 | 5 | dependencies { 6 | compile fileTree(include: ['*.jar'], dir: 'libs') 7 | compile 'com.squareup:javapoet:1.8.0' 8 | compile 'com.google.auto.service:auto-service:1.0-rc2' 9 | compile project(':easyrouter-annotation') 10 | testCompile 'junit:junit:4.12' 11 | testCompile 'org.mockito:mockito-core:2.+' 12 | } 13 | 14 | sourceCompatibility = "1.7" 15 | targetCompatibility = "1.7" 16 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/Constant.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler; 2 | 3 | /** 4 | * Created by Zane on 2017/4/10. 5 | * Email: zanebot96@gmail.com 6 | * Blog: zane96.github.io 7 | */ 8 | 9 | public final class Constant { 10 | public final static String GENERATED_PACKAGENAME = "com.zane.easyrouter_generated"; 11 | public final static String INJECT_PACKAGENAME = "com.zane.router.inject"; 12 | public final static String MESSAGE_PACKAGE = "com.zane.router.message"; 13 | public final static String BASEROUTER_PACKAGE = "com.zane.router.router"; 14 | public final static String COVERTER_PACKAGE = "com.zane.router.converter"; 15 | public final static String EASYROUTERSET_PACKAGE = "com.zane.router"; 16 | public final static String EXCEPTION_PACKAGE = "com.zane.router.exception"; 17 | public final static String ZLOG_PACKAGE = "com.zane.router.utils"; 18 | } 19 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/entity/ParameAnnotationClass.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.entity; 2 | 3 | import com.zane.easyrouter_annotation.Param; 4 | import com.zane.easyrouter_annotation.Result; 5 | import com.squareup.javapoet.ClassName; 6 | import com.squareup.javapoet.TypeName; 7 | 8 | import javax.lang.model.element.Element; 9 | import javax.lang.model.element.VariableElement; 10 | 11 | /** 12 | * Created by Zane on 2016/12/28. 13 | * Email: zanebot96@gmail.com 14 | * Blog: zane96.github.io 15 | */ 16 | 17 | public class ParameAnnotationClass { 18 | 19 | //key值 20 | private String key; 21 | //数据变量名 22 | private String parameName; 23 | //数据的类型 24 | private TypeName typeName; 25 | //被传递参数的元素 26 | private VariableElement mParameElement; 27 | //是返回的变量还是传递的变量 28 | private boolean isResult; 29 | 30 | public ParameAnnotationClass(Element mParameElement, boolean isResult){ 31 | this.mParameElement = (VariableElement) mParameElement; 32 | if (isResult) { 33 | Result result = mParameElement.getAnnotation(Result.class); 34 | key = result.value(); 35 | } else { 36 | Param parame = mParameElement.getAnnotation(Param.class); 37 | key = parame.value(); 38 | } 39 | parameName = this.mParameElement.getSimpleName().toString(); 40 | typeName = convertClass(this.mParameElement.asType().toString()); 41 | this.isResult = isResult; 42 | } 43 | 44 | public boolean isResult() { 45 | return isResult; 46 | } 47 | 48 | public String getKey() { 49 | return key; 50 | } 51 | 52 | public String getParameName() { 53 | return parameName; 54 | } 55 | 56 | public VariableElement getmParameElement() { 57 | return mParameElement; 58 | } 59 | 60 | public TypeName getTypeName() { 61 | return typeName; 62 | } 63 | 64 | private static TypeName convertClass(String originClazz) { 65 | switch (originClazz) { 66 | case "int": 67 | return TypeName.INT; 68 | case "long": 69 | return TypeName.LONG; 70 | case "float": 71 | return TypeName.FLOAT; 72 | case "double": 73 | return TypeName.DOUBLE; 74 | case "short": 75 | return TypeName.SHORT; 76 | case "boolean": 77 | return TypeName.BOOLEAN; 78 | case "char": 79 | return TypeName.CHAR; 80 | default: 81 | return ClassName.bestGuess(originClazz); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/entity/ParameAnnotationGenerator.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.entity; 2 | 3 | import com.zane.easyrouter_compiler.Constant; 4 | import com.squareup.javapoet.ClassName; 5 | import com.squareup.javapoet.JavaFile; 6 | import com.squareup.javapoet.MethodSpec; 7 | import com.squareup.javapoet.TypeSpec; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | import javax.annotation.processing.Filer; 18 | import javax.lang.model.element.Modifier; 19 | import javax.lang.model.util.Elements; 20 | 21 | /** 22 | * Created by Zane on 2016/12/28. 23 | * Email: zanebot96@gmail.com 24 | * Blog: zane96.github.io 25 | */ 26 | 27 | public class ParameAnnotationGenerator { 28 | 29 | //保持一个Activity名字和ParameAnnotationClass的映射关系 30 | private Map> mParameAnnotationMap; 31 | private Map> mResultAnnotationMap; 32 | private Set allClass; 33 | 34 | private ParameAnnotationGenerator(){ 35 | mParameAnnotationMap = new HashMap<>(); 36 | mResultAnnotationMap = new HashMap<>(); 37 | allClass = new HashSet<>(); 38 | } 39 | 40 | private static class SingletonHolder{ 41 | private static final ParameAnnotationGenerator instance = new ParameAnnotationGenerator(); 42 | } 43 | 44 | public static ParameAnnotationGenerator getInstance(){ 45 | return SingletonHolder.instance; 46 | } 47 | 48 | /** 49 | * 添加到映射数据的集合里面 50 | * @param className 51 | * @param parame 52 | */ 53 | public void put(ClassName className, ParameAnnotationClass parame){ 54 | allClass.add(className); 55 | List parames; 56 | 57 | if (parame.isResult()) { 58 | parames = mResultAnnotationMap.get(className); 59 | } else { 60 | parames = mParameAnnotationMap.get(className); 61 | } 62 | 63 | if (parames == null){ 64 | parames = new ArrayList<>(); 65 | } 66 | parames.add(parame); 67 | 68 | if (parame.isResult()) { 69 | mResultAnnotationMap.put(className, parames); 70 | } else { 71 | mParameAnnotationMap.put(className, parames); 72 | } 73 | } 74 | 75 | /** 76 | * 获取映射的集合数据 77 | * @param className 78 | * @return 79 | */ 80 | public List getParame(ClassName className){ 81 | return mParameAnnotationMap.get(className); 82 | } 83 | 84 | public List getResult(ClassName className) { 85 | return mResultAnnotationMap.get(className); 86 | } 87 | 88 | /** 89 | * 清除 90 | */ 91 | public void clear(){ 92 | mParameAnnotationMap.clear(); 93 | mResultAnnotationMap.clear(); 94 | allClass.clear(); 95 | } 96 | 97 | public void generateCode(Elements elementUtils, Filer filer) throws IOException{ 98 | for (ClassName className : allClass) { 99 | MethodSpec injectParam = getInjectMethod(mParameAnnotationMap.get(className), className, false); 100 | MethodSpec injectResult = getInjectMethod(mResultAnnotationMap.get(className), className, true); 101 | TypeSpec.Builder injectClassBuilder = getClassType(className, injectParam, injectResult); 102 | 103 | String packageName = Constant.GENERATED_PACKAGENAME; 104 | JavaFile javaFile = JavaFile.builder(packageName, injectClassBuilder.build()).build(); 105 | javaFile.writeTo(filer); 106 | } 107 | } 108 | 109 | private TypeSpec.Builder getClassType(ClassName className, MethodSpec injectParam, MethodSpec injectResult) { 110 | ClassName inject = ClassName.get(Constant.INJECT_PACKAGENAME, "Inject"); 111 | String urlClassName = RouterAnnotationGenerator.getInstance().getUrl(className.toString()); 112 | TypeSpec.Builder injectClassBuilder = TypeSpec.classBuilder(String.format("%s$$Inject", urlClassName)) 113 | .addSuperinterface(inject) 114 | .addModifiers(Modifier.PUBLIC); 115 | 116 | injectClassBuilder 117 | .addMethod(injectParam) 118 | .addMethod(injectResult); 119 | return injectClassBuilder; 120 | } 121 | 122 | /** 123 | * public void injectData(Activity activity){ 124 | ((xxxActivity) activity).data = activity.getIntent().getString("data"); 125 | } 126 | */ 127 | private MethodSpec getInjectMethod(List parameAnnatationClasses, ClassName className, boolean isResult) { 128 | ClassName activity = ClassName.get("android.app", "Activity"); 129 | ClassName message = ClassName.get(Constant.MESSAGE_PACKAGE, "Message"); 130 | ClassName baseRouter = ClassName.get(Constant.BASEROUTER_PACKAGE, "BaseRouter"); 131 | ClassName datas = ClassName.get(Map.class); 132 | ClassName converter = ClassName.get(Constant.COVERTER_PACKAGE, "Converter"); 133 | ClassName easyRouterSet = ClassName.get(Constant.EASYROUTERSET_PACKAGE, "EasyRouterSet"); 134 | ClassName convertException = ClassName.get(Constant.EXCEPTION_PACKAGE, "ConverterExpection"); 135 | ClassName zlog = ClassName.get(Constant.ZLOG_PACKAGE, "ZLog"); 136 | ClassName intent = ClassName.get("android.content", "Intent"); 137 | 138 | 139 | MethodSpec.Builder injectDataBuilder; 140 | 141 | if (isResult) { 142 | injectDataBuilder = MethodSpec.methodBuilder("injectResult"); 143 | } else { 144 | injectDataBuilder = MethodSpec.methodBuilder("injectParam"); 145 | } 146 | 147 | injectDataBuilder.addAnnotation(Override.class) 148 | .addModifiers(Modifier.PUBLIC) 149 | .addParameter(activity, "activity") 150 | .addParameter(intent, "intent"); 151 | 152 | if (parameAnnatationClasses != null) { 153 | 154 | injectDataBuilder.addStatement("$T message = intent.getParcelableExtra($T.ROUTER_MESSAGE)", message, baseRouter) 155 | .addStatement("$T.Body body = message.getBody()", message) 156 | .addStatement("$T datas = body.getDatas()", datas) 157 | .addStatement("$T.Factory factory = $T.getConverterFactory()", converter, easyRouterSet) 158 | .beginControlFlow("try"); 159 | 160 | for (ParameAnnotationClass parameAnnotationClass : parameAnnatationClasses) { 161 | injectDataBuilder.addStatement("(($T) activity).$N = ($T) factory.decodeConverter($T.class).convert(datas.get($S))", 162 | className, 163 | parameAnnotationClass.getParameName(), 164 | parameAnnotationClass.getTypeName(), 165 | parameAnnotationClass.getTypeName(), 166 | parameAnnotationClass.getKey()); 167 | } 168 | 169 | injectDataBuilder.endControlFlow() 170 | .beginControlFlow("catch ($T e)", convertException) 171 | .addStatement("$T.e($S + e.getMessage())", zlog, className.toString() + "$$Inject") 172 | .endControlFlow(); 173 | } 174 | 175 | return injectDataBuilder.build(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/entity/RouterAnnotationClass.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.entity; 2 | 3 | import com.zane.easyrouter_annotation.Route; 4 | import com.squareup.javapoet.ClassName; 5 | 6 | import javax.lang.model.element.Element; 7 | import javax.lang.model.element.TypeElement; 8 | 9 | /** 10 | * 储存每一个被Route注解了的Activity的class信息 11 | * Created by Zane on 2016/12/20. 12 | * Email: zanebot96@gmail.com 13 | * Blog: zane96.github.io 14 | */ 15 | 16 | public class RouterAnnotationClass { 17 | 18 | //源码中的类元素 19 | private TypeElement anntatedClassElement; 20 | 21 | //这个activity的url路由 22 | private String url; 23 | 24 | //这个类的Class 25 | private ClassName className; 26 | 27 | public RouterAnnotationClass(Element anntatedClassElement) { 28 | 29 | this.anntatedClassElement = (TypeElement) anntatedClassElement; 30 | 31 | // List annotationMirrors = anntatedClassElement.getAnnotationMirrors(); 32 | // for (AnnotationMirror annotationMirror : annotationMirrors){ 33 | // Map elementValues = annotationMirror.getElementValues(); 34 | // for (Map.Entry entry : elementValues.entrySet()) { 35 | // Object value = entry.getValue().getValue(); 36 | // url = (String) value; 37 | // className = this.anntatedClassElement.getSimpleName(); 38 | // } 39 | // } 40 | 41 | //总结,class 42 | Route rawRoute = anntatedClassElement.getAnnotation(Route.class); 43 | //如果是编译了的.class就不会抛出异常 44 | String rawUrl = rawRoute.value(); 45 | url = rawUrl; 46 | className = ClassName.bestGuess(((TypeElement) anntatedClassElement).getQualifiedName().toString()); 47 | 48 | //------------------------------如果格式正确------------------------------ 49 | } 50 | 51 | public String getUrl() { 52 | return url; 53 | } 54 | 55 | public ClassName getClassName() { 56 | return className; 57 | } 58 | 59 | public TypeElement getElement(){ 60 | return anntatedClassElement; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/entity/RouterAnnotationGenerator.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.entity; 2 | 3 | import com.zane.easyrouter_compiler.Constant; 4 | import com.squareup.javapoet.ClassName; 5 | import com.squareup.javapoet.FieldSpec; 6 | import com.squareup.javapoet.JavaFile; 7 | import com.squareup.javapoet.MethodSpec; 8 | import com.squareup.javapoet.ParameterSpec; 9 | import com.squareup.javapoet.TypeSpec; 10 | 11 | import java.io.IOException; 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.Iterator; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.NoSuchElementException; 18 | import java.util.Set; 19 | 20 | import javax.annotation.processing.Filer; 21 | import javax.lang.model.element.Modifier; 22 | import javax.lang.model.util.Elements; 23 | 24 | /** 25 | * RouterAnntationClass的holder类 26 | * Created by Zane on 2016/12/20. 27 | * Email: zanebot96@gmail.com 28 | * Blog: zane96.github.io 29 | */ 30 | 31 | public class RouterAnnotationGenerator { 32 | 33 | private Map routers; 34 | private List datas; 35 | 36 | private RouterAnnotationGenerator(){ 37 | routers = new HashMap<>(); 38 | datas = new ArrayList<>(); 39 | } 40 | 41 | private static class SingltonHokder{ 42 | public static final RouterAnnotationGenerator instance = new RouterAnnotationGenerator(); 43 | } 44 | 45 | public static RouterAnnotationGenerator getInstance(){return SingltonHokder.instance;} 46 | 47 | /** 48 | * 添加到map中 49 | */ 50 | public void put(RouterAnnotationClass data){ 51 | datas.add(data); 52 | String url = data.getUrl(); 53 | ClassName target = data.getClassName(); 54 | routers.put(url, target); 55 | } 56 | 57 | /** 58 | * 在map中查询 59 | * @param url 60 | * @return 61 | */ 62 | public ClassName get(String url){ 63 | return routers.get(url); 64 | } 65 | 66 | /** 67 | * 这个方法给参数注入的Holder类持有类给调用 68 | * 回去到类对应的url,用url去构建注入类。方便开发者自定义url 69 | * @param className 70 | * @return 71 | */ 72 | public String getUrl(String className) { 73 | Set> routerSet = routers.entrySet(); 74 | Iterator> iterator = routerSet.iterator(); 75 | String url = ""; 76 | while (iterator.hasNext()) { 77 | Map.Entry entry = iterator.next(); 78 | if (entry.getValue().toString().equals(className)) { 79 | url = getAuthority(entry.getKey()); 80 | break; 81 | } 82 | } 83 | return url; 84 | } 85 | 86 | /** 87 | * 清空 88 | */ 89 | public void clear(){ 90 | routers.clear(); 91 | } 92 | 93 | /** 94 | * 根据之前收集到的数据信息(String url, Class target) 95 | * 生成路由表的.java文件代码 96 | * @param elementUtils 97 | * @param filer 98 | * @throws IOException 99 | */ 100 | public void generateCode(Elements elementUtils, Filer filer) throws IOException{ 101 | if (datas.size() > 0){ 102 | FieldSpec routerTable = getTableField(); 103 | MethodSpec queryTable = getQueryMethod(); 104 | MethodSpec initTable = getInjectMethod(); 105 | MethodSpec constructor = getConstructorMethod(routerTable, initTable); 106 | TypeSpec routerTableClass = getClassType(routerTable, queryTable, initTable, constructor); 107 | 108 | String packageName = Constant.GENERATED_PACKAGENAME; 109 | JavaFile javaFile = JavaFile.builder(packageName, routerTableClass).build(); 110 | javaFile.writeTo(filer); 111 | } 112 | } 113 | 114 | private TypeSpec getClassType(FieldSpec routerTable, MethodSpec queryTable, MethodSpec initTable, MethodSpec constructor) { 115 | return TypeSpec.classBuilder("EasyRouterTable") 116 | .addModifiers(Modifier.PUBLIC) 117 | .addMethod(constructor) 118 | .addMethod(initTable) 119 | .addMethod(queryTable) 120 | .addField(routerTable) 121 | .build(); 122 | } 123 | 124 | /** 125 | * 构造函数 126 | * private RouterTableExample() { 127 | routerTable = new HashMap<>(); 128 | initTable(); 129 | } 130 | */ 131 | private MethodSpec getConstructorMethod(FieldSpec routerTable, MethodSpec initTable) { 132 | ClassName hashMap = ClassName.get(HashMap.class); 133 | ClassName activity = ClassName.get("android.app", "Activity"); 134 | return MethodSpec.constructorBuilder() 135 | .addModifiers(Modifier.PUBLIC) 136 | .addStatement("$N = new $T>()", routerTable, hashMap, activity) 137 | .addStatement("$N()", initTable) 138 | .build(); 139 | } 140 | 141 | /** 142 | * private void initTable(){ 143 | //添加信息到routerTable中 144 | } 145 | */ 146 | private MethodSpec getInjectMethod() { 147 | MethodSpec.Builder initTableBuilder = MethodSpec.methodBuilder("initTable") 148 | .addModifiers(Modifier.PRIVATE); 149 | Set> routerSet = routers.entrySet(); 150 | Iterator> iterator = routerSet.iterator(); 151 | while (iterator.hasNext()) { 152 | Map.Entry entry = iterator.next(); 153 | initTableBuilder.addStatement("routerTable.put($S, $T.class)", entry.getKey(), entry.getValue()); 154 | } 155 | return initTableBuilder.build(); 156 | } 157 | 158 | /** 159 | *表查询函数 160 | * public Class queryTable(String url){ 161 | Class target = routerTable.get(url); 162 | if (target == null){ 163 | return null; 164 | } 165 | return target; 166 | } 167 | */ 168 | private MethodSpec getQueryMethod() { 169 | ParameterSpec url = ParameterSpec.builder(String.class, "url").build(); 170 | return MethodSpec.methodBuilder("queryTable") 171 | .addModifiers(Modifier.PUBLIC) 172 | .addParameter(url) 173 | .addStatement("Class target = (Class) routerTable.get(url)") 174 | .beginControlFlow("if (target == null)") 175 | .addStatement("throw new $T(url)", NoSuchElementException.class) 176 | .endControlFlow() 177 | .addStatement("return target") 178 | .returns(Class.class) 179 | .build(); 180 | } 181 | 182 | private FieldSpec getTableField() { 183 | return FieldSpec.builder(Map.class, "routerTable") 184 | .addModifiers(Modifier.PRIVATE) 185 | .build(); 186 | } 187 | 188 | /** 189 | * 获取url的authority 190 | * @param url 191 | * @return 192 | */ 193 | private String getAuthority(String url){ 194 | return url.substring(url.indexOf(":") + 3, url.length()); 195 | } 196 | } -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/exception/NoSuchUrlException.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.exception; 2 | 3 | /** 4 | * Created by Zane on 2016/12/23. 5 | * Email: zanebot96@gmail.com 6 | * Blog: zane96.github.io 7 | */ 8 | 9 | public class NoSuchUrlException extends Exception{ 10 | 11 | public NoSuchUrlException(String url) { 12 | super(String.format("No such url in RouterTable's map, %s", url)); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/exception/OnlyClassException.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.exception; 2 | 3 | /** 4 | * Created by Zane on 2016/12/23. 5 | * Email: zanebot96@gmail.com 6 | * Blog: zane96.github.io 7 | */ 8 | 9 | public class OnlyClassException extends Exception{ 10 | 11 | public OnlyClassException(String kind) { 12 | super(String.format("@Route shouldn't use in %s", kind)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/process/BaseProcess.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.process; 2 | 3 | import javax.annotation.processing.AbstractProcessor; 4 | import javax.annotation.processing.Filer; 5 | import javax.annotation.processing.Messager; 6 | import javax.annotation.processing.ProcessingEnvironment; 7 | import javax.lang.model.SourceVersion; 8 | import javax.lang.model.element.Element; 9 | import javax.lang.model.util.Elements; 10 | import javax.lang.model.util.Types; 11 | import javax.tools.Diagnostic; 12 | 13 | /** 14 | * APT基类 15 | * Created by Zane on 2016/12/28. 16 | * Email: zanebot96@gmail.com 17 | * Blog: zane96.github.io 18 | */ 19 | 20 | public abstract class BaseProcess extends AbstractProcessor{ 21 | 22 | Types typeUtils; 23 | Elements elementUtils; 24 | Filer filer; 25 | Messager messager; 26 | 27 | @Override 28 | public synchronized void init(ProcessingEnvironment processingEnv) { 29 | super.init(processingEnv); 30 | typeUtils = processingEnv.getTypeUtils(); 31 | elementUtils = processingEnv.getElementUtils(); 32 | filer = processingEnv.getFiler(); 33 | messager = processingEnv.getMessager(); 34 | } 35 | 36 | @Override 37 | public SourceVersion getSupportedSourceVersion() { 38 | return SourceVersion.latestSupported(); 39 | } 40 | 41 | void printError(Element e, String msg, Object... args){ 42 | messager.printMessage(Diagnostic.Kind.ERROR, 43 | String.format(msg, args), 44 | e); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/process/RouterProcess.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.process; 2 | 3 | import com.zane.easyrouter_annotation.Route; 4 | import com.zane.easyrouter_compiler.entity.RouterAnnotationClass; 5 | import com.zane.easyrouter_compiler.entity.RouterAnnotationGenerator; 6 | import com.google.auto.service.AutoService; 7 | 8 | import java.io.IOException; 9 | import java.util.LinkedHashSet; 10 | import java.util.Set; 11 | 12 | import javax.annotation.processing.Processor; 13 | import javax.annotation.processing.RoundEnvironment; 14 | import javax.lang.model.element.Element; 15 | import javax.lang.model.element.TypeElement; 16 | 17 | /** 18 | * Created by Zane on 2016/12/20. 19 | * Email: zanebot96@gmail.com 20 | * Blog: zane96.github.io 21 | */ 22 | 23 | @AutoService(Processor.class) 24 | public class RouterProcess extends BaseProcess { 25 | 26 | @Override 27 | public boolean process(Set annotations, RoundEnvironment roundEnv){ 28 | RouterAnnotationGenerator.getInstance().clear(); 29 | 30 | for (Element anntatedElement : roundEnv.getElementsAnnotatedWith(Route.class)){ 31 | //如果不是类类型的话就要报错 32 | if (!Validator.isClass(anntatedElement)){ 33 | printError(anntatedElement, "Only class can be annotated with @Route"); 34 | return true; 35 | } else if (Validator.isAbstractClass(anntatedElement)) { 36 | printError(anntatedElement, "Class can't be abstract with @Route"); 37 | return true; 38 | } 39 | RouterAnnotationClass routerAnntationClass = new RouterAnnotationClass(anntatedElement); 40 | RouterAnnotationGenerator.getInstance().put(routerAnntationClass); 41 | } 42 | 43 | try { 44 | RouterAnnotationGenerator.getInstance().generateCode(elementUtils, filer); 45 | } catch (IOException e) { 46 | } 47 | 48 | return true; 49 | } 50 | 51 | @Override 52 | public Set getSupportedAnnotationTypes() { 53 | Set anntations = new LinkedHashSet<>(); 54 | anntations.add(Route.class.getCanonicalName()); 55 | return anntations; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/process/Validator.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.process; 2 | 3 | import java.util.Set; 4 | 5 | import javax.annotation.processing.ProcessingEnvironment; 6 | import javax.lang.model.element.Element; 7 | import javax.lang.model.element.ElementKind; 8 | import javax.lang.model.element.Modifier; 9 | import javax.lang.model.element.TypeElement; 10 | 11 | /** 12 | * @author Saeed Masoumi (saeed@6thsolution.com) 13 | */ 14 | final class Validator { 15 | 16 | static boolean isClass(Element element) { 17 | return element.getKind() == ElementKind.CLASS; 18 | } 19 | 20 | static boolean isAbstractClass(Element element) { 21 | return isClass(element) && getModifiers(element).contains(Modifier.ABSTRACT); 22 | } 23 | 24 | static boolean isField(Element element) { 25 | return element.getKind() == ElementKind.FIELD; 26 | } 27 | 28 | static boolean isNotAbstractClass(Element element) { 29 | return isClass(element) && !getModifiers(element).contains(Modifier.ABSTRACT); 30 | } 31 | 32 | static boolean isSubType(Element child, String parentCanonicalName, 33 | ProcessingEnvironment procEnv) { 34 | return procEnv.getTypeUtils() 35 | .isSubtype(child.asType(), getTypeElement(procEnv, parentCanonicalName).asType()); 36 | } 37 | 38 | static boolean isPrivate(Element element) { 39 | return getModifiers(element).contains(Modifier.PRIVATE); 40 | } 41 | 42 | static boolean isMethod(Element element) { 43 | return ElementKind.METHOD == element.getKind(); 44 | } 45 | 46 | static TypeElement getTypeElement(ProcessingEnvironment procEnv, String canonicalName) { 47 | return procEnv.getElementUtils().getTypeElement(canonicalName); 48 | } 49 | 50 | static Set getModifiers(Element element) { 51 | return element.getModifiers(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/main/java/com/zane/easyrouter_compiler/process/zParameProcess.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_compiler.process; 2 | 3 | import com.zane.easyrouter_annotation.Param; 4 | import com.zane.easyrouter_annotation.Result; 5 | import com.zane.easyrouter_compiler.entity.ParameAnnotationClass; 6 | import com.zane.easyrouter_compiler.entity.ParameAnnotationGenerator; 7 | import com.google.auto.service.AutoService; 8 | import com.squareup.javapoet.ClassName; 9 | 10 | import java.io.IOException; 11 | import java.util.LinkedHashSet; 12 | import java.util.Set; 13 | 14 | import javax.annotation.processing.Processor; 15 | import javax.annotation.processing.RoundEnvironment; 16 | import javax.lang.model.element.Element; 17 | import javax.lang.model.element.TypeElement; 18 | 19 | /** 20 | * 参数数据自动注入的APT 21 | * Created by Zane on 2016/12/28. 22 | * Email: zanebot96@gmail.com 23 | * Blog: zane96.github.io 24 | */ 25 | 26 | @AutoService(Processor.class) 27 | public class zParameProcess extends BaseProcess { 28 | 29 | @Override 30 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 31 | ParameAnnotationGenerator.getInstance().clear(); 32 | 33 | for (Element parameElement : roundEnv.getElementsAnnotatedWith(Param.class)){ 34 | if (!judgeKind(parameElement)) { 35 | generatedFiled(parameElement, false); 36 | } 37 | } 38 | 39 | for (Element resultElement : roundEnv.getElementsAnnotatedWith(Result.class)) { 40 | if (!judgeKind(resultElement)) { 41 | generatedFiled(resultElement, true); 42 | } 43 | } 44 | 45 | try { 46 | ParameAnnotationGenerator.getInstance().generateCode(elementUtils, filer); 47 | } catch (IOException e) { 48 | } 49 | 50 | return true; 51 | } 52 | 53 | private void generatedFiled(Element element, boolean isResult) { 54 | ParameAnnotationClass parameAnnotationClass = new ParameAnnotationClass(element, isResult); 55 | ParameAnnotationGenerator.getInstance().put(getClassName(element), parameAnnotationClass); 56 | } 57 | 58 | private boolean judgeKind(Element element) { 59 | if (!Validator.isField(element)){ 60 | printError(element, "Only field can be annotated with @Param"); 61 | return true; 62 | } else if (Validator.isPrivate(element)) { 63 | printError(element, "Field can't be the private with @Param"); 64 | return true; 65 | } 66 | return false; 67 | } 68 | 69 | //获取Activity或者Fragment的类名 70 | private ClassName getClassName(Element element){ 71 | //获取class type 72 | TypeElement classElement = (TypeElement) element.getEnclosingElement(); 73 | return ClassName.bestGuess(classElement.getQualifiedName().toString()); 74 | } 75 | 76 | @Override 77 | public Set getSupportedAnnotationTypes() { 78 | Set anntations = new LinkedHashSet<>(); 79 | anntations.add(Param.class.getCanonicalName()); 80 | anntations.add(Result.class.getCanonicalName()); 81 | return anntations; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/test/java/com/example/ParameAnntationClassesTest.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.zane.easyrouter_compiler.entity.ParameAnnotationClass; 4 | import com.zane.easyrouter_compiler.entity.ParameAnnotationGenerator; 5 | 6 | import org.junit.Test; 7 | import org.mockito.Mockito; 8 | 9 | import static org.mockito.Mockito.*; 10 | 11 | /** 12 | * Created by Zane on 2017/1/2. 13 | * Email: zanebot96@gmail.com 14 | * Blog: zane96.github.io 15 | */ 16 | 17 | public class ParameAnntationClassesTest { 18 | 19 | @Test 20 | public void testPut() throws Exception { 21 | ParameAnnotationGenerator parameAnnotationClasses = Mockito.mock(ParameAnnotationGenerator.class); 22 | ParameAnnotationClass parameAnnotationClass = Mockito.mock(ParameAnnotationClass.class); 23 | 24 | verify(parameAnnotationClasses).put("ActivityTwo", parameAnnotationClass); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /easyrouter-compiler/src/test/java/com/example/RouterUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | /** 7 | * Created by Zane on 2017/1/2. 8 | * Email: zanebot96@gmail.com 9 | * Blog: zane96.github.io 10 | */ 11 | 12 | public class RouterUtilTest { 13 | @Test 14 | public void testGetScheme() throws Exception{ 15 | //String scheme = RouterUtil.getScheme("activity://two"); 16 | //assertEquals("activity", scheme); 17 | } 18 | 19 | @Test 20 | public void testGetAuthority() throws Exception{ 21 | //String authority = RouterUtil.getAuthority("activity://two"); 22 | //assertEquals("two", authority); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /easyrouter-generated/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /easyrouter-generated/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '26.0.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | } 26 | -------------------------------------------------------------------------------- /easyrouter-generated/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Zane/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /easyrouter-generated/src/androidTest/java/com/example/zane/easyrouter_generated/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.zane.easyrouter_generated; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.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 | * Instrumentation 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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.zane.easyrouter_generated.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /easyrouter-generated/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /easyrouter-generated/src/main/java/com/zane/easyrouter_generated/EasyRouterTable.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter_generated; 2 | 3 | /** 4 | * 占坑类 5 | * 6 | * Created by Zane on 2017/4/9. 7 | * Email: zanebot96@gmail.com 8 | * Blog: zane96.github.io 9 | */ 10 | 11 | public class EasyRouterTable{ 12 | 13 | public EasyRouterTable() { 14 | } 15 | private void initTable() { 16 | } 17 | 18 | public Class queryTable(String url) { 19 | return null; 20 | } 21 | } -------------------------------------------------------------------------------- /easyrouter-generated/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | easyrouter-generated 3 | 4 | -------------------------------------------------------------------------------- /easyrouter-generated/src/test/java/com/example/zane/easyrouter_generated/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.zane.easyrouter_generated; 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() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /easyrouter-merge/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | publish { 5 | userOrg = 'zane96' 6 | groupId = 'com.zane' 7 | artifactId = 'easyrouter-merge' 8 | publishVersion = '1.0.0' 9 | desc = 'one gradle plugin supporting component development in EasyRouter' 10 | website = 'https://github.com/Zane96/EasyRouter' 11 | } 12 | 13 | //uploadArchives { 14 | // repositories.mavenDeployer { 15 | // repository(url: LOCAL_REPO_URL) 16 | // pom.groupId = PROJ_GROUP 17 | // pom.artifactId = PROJ_ARTIFACTID 18 | // pom.version = PROJ_VERSION } 19 | //} 20 | 21 | dependencies { 22 | compile fileTree(include: ['*.jar'], dir: 'libs') 23 | compile gradleApi() 24 | compile localGroovy() 25 | compileOnly "com.android.tools.build:gradle:3.0.1" 26 | compile "com.android.tools.build:transform-api:1.5.0" 27 | compile "org.ow2.asm:asm-all:5.1" 28 | } 29 | 30 | sourceCompatibility = "1.8" 31 | targetCompatibility = "1.8" 32 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/ApplicationTransform.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | import com.android.build.api.transform.Transform; 5 | import com.android.build.api.transform.TransformException; 6 | import com.android.build.api.transform.TransformInvocation; 7 | import com.android.build.gradle.internal.pipeline.TransformManager; 8 | 9 | import java.io.IOException; 10 | import java.util.Set; 11 | 12 | import me.zane.easyrouter_merge.bean.MergeInfo; 13 | import me.zane.easyrouter_merge.collect.CollectEngine; 14 | import me.zane.easyrouter_merge.framework.TransformContext; 15 | import me.zane.easyrouter_merge.log.Log; 16 | import me.zane.easyrouter_merge.merge.MergeEngine; 17 | 18 | /** 19 | * Created by Zane on 2018/5/17. 20 | * Email: zanebot96@gmail.com 21 | */ 22 | public class ApplicationTransform extends Transform{ 23 | private static final String TAG = ApplicationTransform.class.getSimpleName(); 24 | 25 | @Override 26 | public String getName() { 27 | return TAG; 28 | } 29 | 30 | @Override 31 | public Set getInputTypes() { 32 | return TransformManager.CONTENT_CLASS; 33 | } 34 | 35 | @Override 36 | public Set getScopes() { 37 | return TransformManager.SCOPE_FULL_PROJECT; 38 | } 39 | 40 | @Override 41 | public boolean isIncremental() { 42 | return false; 43 | } 44 | 45 | @Override 46 | public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { 47 | TransformContext context = new TransformContext(transformInvocation); 48 | 49 | CollectEngine collectEngine = new CollectEngine(context); 50 | MergeInfo mergeInfo = collectEngine.startCollect(); 51 | 52 | Log.i("merinfo size: " + mergeInfo.toString()); 53 | MergeEngine mergeEngine = new MergeEngine(context); 54 | mergeEngine.startMerge(mergeInfo); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/LibraryTransform.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | import com.android.build.api.transform.Transform; 5 | import com.android.build.api.transform.TransformException; 6 | import com.android.build.api.transform.TransformInvocation; 7 | import com.android.build.gradle.internal.pipeline.TransformManager; 8 | import com.google.common.collect.Sets; 9 | 10 | import org.gradle.api.Project; 11 | 12 | import java.io.IOException; 13 | import java.util.Set; 14 | 15 | import me.zane.easyrouter_merge.framework.TransformContext; 16 | import me.zane.easyrouter_merge.rename.RenameEngine; 17 | 18 | /** 19 | * Created by Zane on 2018/5/17. 20 | * Email: zanebot96@gmail.com 21 | */ 22 | public class LibraryTransform extends Transform{ 23 | private static final String TAG = LibraryTransform.class.getSimpleName(); 24 | private Project project; 25 | 26 | public LibraryTransform(Project project) { 27 | this.project = project; 28 | } 29 | 30 | @Override 31 | public String getName() { 32 | return TAG; 33 | } 34 | 35 | @Override 36 | public Set getInputTypes() { 37 | return TransformManager.CONTENT_CLASS; 38 | } 39 | 40 | @Override 41 | public Set getScopes() { 42 | return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT); 43 | } 44 | 45 | @Override 46 | public boolean isIncremental() { 47 | return false; 48 | } 49 | 50 | @Override 51 | public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { 52 | TransformContext context = new TransformContext(transformInvocation); 53 | RenameEngine engine = new RenameEngine(context); 54 | engine.stratRename(project); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/RouterModulePlugin.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge; 2 | 3 | import com.android.build.gradle.BaseExtension; 4 | 5 | import org.gradle.api.Project; 6 | import org.gradle.api.Plugin; 7 | import org.gradle.api.ProjectConfigurationException; 8 | 9 | /** 10 | * Created by Zane on 2018/4/19. 11 | * Email: zanebot96@gmail.com 12 | */ 13 | public class RouterModulePlugin implements Plugin { 14 | 15 | @Override 16 | public void apply(Project project) { 17 | BaseExtension androidExtension = (BaseExtension) project.getExtensions().getByName("android"); 18 | 19 | if (project.getPlugins().findPlugin("com.android.application") != null) { 20 | androidExtension.registerTransform(new ApplicationTransform()); 21 | } else if (project.getPlugins().findPlugin("com.android.library") != null) { 22 | androidExtension.registerTransform(new LibraryTransform(project)); 23 | } else { 24 | throw new ProjectConfigurationException("Need android application/library plugin to be applied first", null); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/bean/MergeInfo.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.bean; 2 | 3 | import org.objectweb.asm.tree.MethodNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Zane on 2018/5/17. 11 | * Email: zanebot96@gmail.com 12 | */ 13 | public class MergeInfo { 14 | public List mMapMethods = Collections.synchronizedList(new ArrayList<>()); 15 | 16 | public List getMapMethods() { 17 | return mMapMethods; 18 | } 19 | 20 | public void addMapMethods(MethodNode mMapMethod) { 21 | this.mMapMethods.add(mMapMethod); 22 | } 23 | 24 | public void combine(MergeInfo info) { 25 | mMapMethods.addAll(info.mMapMethods); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "mMapMethods" + mMapMethods.size() + "\n"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/collect/CollectClassVisitor.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.collect; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.MethodNode; 7 | 8 | import me.zane.easyrouter_merge.bean.MergeInfo; 9 | import me.zane.easyrouter_merge.log.Log; 10 | 11 | /** 12 | * Created by Zane on 2018/5/17. 13 | * 14 | * 在这里通过分析jar中modular的EasyRouterTable方法来获取函数的方法Node 15 | * Email: zanebot96@gmail.com 16 | */ 17 | public class CollectClassVisitor extends ClassVisitor{ 18 | private MergeInfo mergeInfo; 19 | private boolean isFindTarget; 20 | 21 | public CollectClassVisitor() { 22 | super(Opcodes.ASM5); 23 | mergeInfo = new MergeInfo(); 24 | } 25 | 26 | public boolean isFindTarget() { 27 | return isFindTarget; 28 | } 29 | 30 | public MergeInfo getMergeInfo() { 31 | return mergeInfo; 32 | } 33 | 34 | @Override 35 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 36 | if (name.matches("com/zane/easyrouter.*/EasyRouterTable")) { 37 | isFindTarget = true; 38 | } 39 | super.visit(version, access, name, signature, superName, interfaces); 40 | } 41 | 42 | @Override 43 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 44 | MethodNode methodNode = new MethodNode(access, name, desc, signature, exceptions); 45 | if ("initTable".equals(name)) { 46 | mergeInfo.addMapMethods(methodNode); 47 | return methodNode; 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/collect/CollectEngine.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.collect; 2 | 3 | import java.io.IOException; 4 | 5 | import me.zane.easyrouter_merge.bean.MergeInfo; 6 | import me.zane.easyrouter_merge.framework.ContextReader; 7 | import me.zane.easyrouter_merge.framework.JarContentProvider; 8 | import me.zane.easyrouter_merge.framework.TransformContext; 9 | import me.zane.easyrouter_merge.log.Log; 10 | 11 | /** 12 | * Created by Zane on 2018/5/17. 13 | * Email: zanebot96@gmail.com 14 | */ 15 | public class CollectEngine { 16 | private TransformContext context; 17 | 18 | public CollectEngine(TransformContext context) { 19 | this.context = context; 20 | } 21 | 22 | public MergeInfo startCollect() { 23 | ContextReader contextReader = new ContextReader(context, new JarContentProvider()); 24 | CollectHandler handler = new CollectHandler(context); 25 | 26 | try { 27 | contextReader.accept(handler); 28 | } catch (Exception e) { 29 | Log.e("exception in collect modular file: " + e.getLocalizedMessage()); 30 | } 31 | 32 | return handler.getMergeInfo(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/collect/CollectHandler.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.collect; 2 | 3 | import com.android.build.api.transform.JarInput; 4 | import com.android.build.api.transform.QualifiedContent; 5 | import com.android.build.api.transform.Status; 6 | import com.android.utils.FileUtils; 7 | import com.google.common.io.Files; 8 | 9 | import org.objectweb.asm.ClassReader; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | import me.zane.easyrouter_merge.bean.MergeInfo; 17 | import me.zane.easyrouter_merge.framework.ClassHandler; 18 | import me.zane.easyrouter_merge.framework.JarWriter; 19 | import me.zane.easyrouter_merge.framework.TransformContext; 20 | import me.zane.easyrouter_merge.log.Log; 21 | 22 | /** 23 | * Created by Zane on 2018/5/17. 24 | * Email: zanebot96@gmail.com 25 | */ 26 | public class CollectHandler implements ClassHandler{ 27 | private Map map = new ConcurrentHashMap<>(); 28 | private TransformContext context; 29 | private MergeInfo mergeInfo; 30 | 31 | public CollectHandler(TransformContext context) { 32 | this.context = context; 33 | mergeInfo = new MergeInfo(); 34 | } 35 | 36 | public MergeInfo getMergeInfo() { 37 | return mergeInfo; 38 | } 39 | 40 | @Override 41 | public boolean onStart(QualifiedContent content) throws IOException { 42 | if (content instanceof JarInput) { 43 | JarInput jarInput = (JarInput) content; 44 | File targetFile = context.getRelativeFile(content); 45 | switch (jarInput.getStatus()) { 46 | case REMOVED: 47 | FileUtils.deleteIfExists(targetFile); 48 | return false; 49 | case CHANGED: 50 | FileUtils.deleteIfExists(targetFile); 51 | default: 52 | Files.createParentDirs(targetFile); 53 | map.put(content, new JarWriter(targetFile)); 54 | } 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | @Override 61 | public void onClassFetch(QualifiedContent content, Status status, String relativePath, byte[] bytes) throws IOException { 62 | //收集完毕之后不用写入output 63 | if (relativePath.endsWith(".class") && relativePath.contains("com/zane/easyrouter")) { 64 | ClassReader classReader = new ClassReader(bytes); 65 | CollectClassVisitor collectClassVisitor = new CollectClassVisitor(); 66 | classReader.accept(collectClassVisitor, 0); 67 | 68 | if (collectClassVisitor.isFindTarget()) { 69 | mergeInfo.combine(collectClassVisitor.getMergeInfo()); 70 | } else { 71 | JarWriter jarWriter = map.get(content); 72 | jarWriter.write(relativePath,bytes); 73 | } 74 | } else { 75 | JarWriter jarWriter = map.get(content); 76 | jarWriter.write(relativePath,bytes); 77 | } 78 | } 79 | 80 | @Override 81 | public void onComplete(QualifiedContent content) throws IOException { 82 | if (content instanceof JarInput && ((JarInput) content).getStatus() != Status.REMOVED) { 83 | map.get(content).close(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/ClassHandler.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | import com.android.build.api.transform.Status; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * The Fetcher to fetch each class in QualifiedContent 10 | */ 11 | public interface ClassHandler { 12 | 13 | /** 14 | * begin unzip a QualifiedContent. 15 | * @param content the Jar or Dir QualifiedContent. 16 | * @return whether the Fetcher can accept this QualifiedContent. 17 | * @throws IOException 18 | */ 19 | boolean onStart(QualifiedContent content) throws IOException; 20 | 21 | /** 22 | * fetch each class in QualifiedContent. each invoke will in one thread. 23 | * @param content 24 | * @param status 25 | * @param relativePath 26 | * @param bytes 27 | * @throws IOException 28 | */ 29 | void onClassFetch(QualifiedContent content, Status status, String relativePath, byte[] bytes) throws IOException; 30 | 31 | /** 32 | * has finished fetch class in this QualifiedContent 33 | * @param content 34 | * @throws IOException 35 | */ 36 | void onComplete(QualifiedContent content) throws IOException; 37 | } 38 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/ContextReader.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.JarInput; 4 | import com.android.build.api.transform.QualifiedContent; 5 | 6 | import java.io.IOException; 7 | import java.util.Collection; 8 | import java.util.List; 9 | import java.util.concurrent.Callable; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Future; 13 | import java.util.concurrent.LinkedBlockingQueue; 14 | import java.util.concurrent.ThreadPoolExecutor; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | import me.zane.easyrouter_merge.log.Log; 20 | 21 | /** 22 | * Created by gengwanpeng on 17/5/2. 23 | * 24 | * This class will unzip all jars,and accept all class with input ClassHandler in thread pool. 25 | * Used in pre-analysis and formal analysis. 26 | * 27 | * 28 | */ 29 | public class ContextReader { 30 | private TransformContext context; 31 | private TargetedQualifiedContentProvider provider; 32 | private ExecutorService service = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), 33 | 0L, TimeUnit.MILLISECONDS, 34 | new LinkedBlockingQueue<>(), (r, executor) -> { 35 | Log.i("partial parse failed, executor has been shutdown"); 36 | }); 37 | 38 | public ContextReader(TransformContext context,TargetedQualifiedContentProvider provider) { 39 | this.context = context; 40 | this.provider = provider; 41 | } 42 | 43 | /** 44 | * read the classes in thread pool and send class to fetcher. 45 | * @param fetcher the fetcher to visit classes 46 | * @throws IOException 47 | * @throws InterruptedException 48 | */ 49 | public void accept(ClassHandler fetcher) throws IOException, InterruptedException { 50 | // get all jars 51 | Collection jars = context.getAllJars(); 52 | // accept the jar in thread pool 53 | List> tasks = Stream.concat(jars.stream(), context.getAllDirs().stream()) 54 | .filter(t -> provider.accepted(t)) 55 | .map(q -> new QualifiedContentTask(q, fetcher)) 56 | .map(t -> service.submit(t)) 57 | .collect(Collectors.toList()); 58 | 59 | // block until all task has finish. 60 | for (Future future : tasks) { 61 | try { 62 | future.get(); 63 | } catch (ExecutionException e) { 64 | Throwable cause = e.getCause(); 65 | if (cause instanceof IOException) { 66 | throw (IOException) cause; 67 | } else if (cause instanceof InterruptedException) { 68 | throw (InterruptedException) cause; 69 | } else { 70 | throw new RuntimeException(e.getCause()); 71 | } 72 | } 73 | } 74 | 75 | } 76 | 77 | /** 78 | * Task to accept target QualifiedContent 79 | */ 80 | private class QualifiedContentTask implements Callable { 81 | 82 | private QualifiedContent content; 83 | private ClassHandler fetcher; 84 | 85 | QualifiedContentTask(QualifiedContent content, ClassHandler fetcher) { 86 | this.content = content; 87 | this.fetcher = fetcher; 88 | } 89 | 90 | @Override 91 | public Void call() throws Exception { 92 | provider.forEach(content, fetcher); 93 | return null; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/DirectoryContentProvider.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.DirectoryInput; 4 | import com.android.build.api.transform.QualifiedContent; 5 | import com.android.build.api.transform.Status; 6 | import com.google.common.io.Files; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.net.URI; 11 | 12 | import me.zane.easyrouter_merge.log.Log; 13 | 14 | /** 15 | * Created by gengwanpeng on 17/4/28. 16 | */ 17 | public class DirectoryContentProvider extends TargetedQualifiedContentProvider { 18 | 19 | 20 | public DirectoryContentProvider() { 21 | } 22 | 23 | @Override 24 | public void forEach(QualifiedContent content, ClassHandler processor) throws IOException { 25 | if (processor.onStart(content)) { 26 | Log.i("start trans dir "+content.getName()); 27 | 28 | File root = content.getFile(); 29 | URI base = root.toURI(); 30 | for (File f : Files.fileTreeTraverser().preOrderTraversal(root)) { 31 | if (f.isFile()) { 32 | byte[] data = Files.toByteArray(f); 33 | String relativePath = base.relativize(f.toURI()).toString(); 34 | processor.onClassFetch(content, Status.ADDED, relativePath, data); 35 | } 36 | } 37 | } 38 | processor.onComplete(content); 39 | } 40 | 41 | @Override 42 | public boolean accepted(QualifiedContent qualifiedContent) { 43 | return qualifiedContent instanceof DirectoryInput; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/DirectoryWriter.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.google.common.io.Files; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | public class DirectoryWriter { 9 | 10 | public void write(File relativeRoot, String relativePath, byte[] bytes) throws IOException { 11 | if (bytes != null) { 12 | File target = toSystemDependentFile(relativeRoot, relativePath); 13 | Files.createParentDirs(target); 14 | Files.write(bytes, target); 15 | } 16 | 17 | } 18 | 19 | public static File toSystemDependentFile(File parent, String relativePath) { 20 | return new File(parent, relativePath.replace('/', File.separatorChar)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/JarContentProvider.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.JarInput; 4 | import com.android.build.api.transform.QualifiedContent; 5 | import com.google.common.io.ByteStreams; 6 | 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import java.io.BufferedInputStream; 10 | import java.io.EOFException; 11 | import java.io.FileInputStream; 12 | import java.io.IOException; 13 | import java.util.zip.ZipEntry; 14 | import java.util.zip.ZipInputStream; 15 | 16 | import me.zane.easyrouter_merge.log.Log; 17 | 18 | /** 19 | * Created by gengwanpeng on 17/4/28. 20 | */ 21 | public class JarContentProvider extends TargetedQualifiedContentProvider { 22 | 23 | @Override 24 | public void forEach(QualifiedContent content, ClassHandler processor) throws IOException { 25 | forActualInput((JarInput) content, processor); 26 | } 27 | 28 | private void forActualInput(JarInput jarInput, ClassHandler processor) throws IOException { 29 | if (processor.onStart(jarInput)) { 30 | //Log.i("start trans jar "+ jarInput.getStatus() + " " + jarInput.getName() + " " + jarInput.getFile()); 31 | ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(jarInput.getFile()))); 32 | ZipEntry entry; 33 | while ((entry = zis.getNextEntry()) != null) { 34 | if (entry.isDirectory()) { 35 | continue; 36 | } 37 | try { 38 | //Log.i("start trans class haha " + entry.getName()); 39 | byte[] data = ByteStreams.toByteArray(zis); 40 | processor.onClassFetch(jarInput, jarInput.getStatus(), entry.getName(), data); 41 | } catch (EOFException e){ 42 | break; 43 | } 44 | } 45 | IOUtils.closeQuietly(zis); 46 | } 47 | processor.onComplete(jarInput); 48 | } 49 | 50 | @Override 51 | public boolean accepted(QualifiedContent qualifiedContent) { 52 | return qualifiedContent instanceof JarInput; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/JarWriter.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.Closeable; 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.util.jar.JarOutputStream; 9 | import java.util.zip.ZipEntry; 10 | 11 | public class JarWriter implements Closeable{ 12 | 13 | private final JarOutputStream jos; 14 | 15 | public JarWriter(File targetFile) throws IOException { 16 | this.jos = new JarOutputStream( 17 | new BufferedOutputStream(new FileOutputStream(targetFile))); 18 | } 19 | 20 | public void write(String relativePath, byte[] bytes) throws IOException { 21 | ZipEntry entry = new ZipEntry(relativePath); 22 | jos.putNextEntry(entry); 23 | jos.write(bytes); 24 | } 25 | 26 | public void close() throws IOException { 27 | jos.close(); 28 | } 29 | } -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/QualifiedContentProvider.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created by gengwanpeng on 17/4/28. 9 | * 10 | * Unzip QualifiedContent and provide single class for inout ClassHandler. 11 | * QualifiedContent may be one of {@link com.android.build.api.transform.JarInput} and {@link com.android.build.api.transform.DirectoryInput}. 12 | * So there are tow child of QualifiedContentProvider {@link JarContentProvider} and {@link DirectoryContentProvider} 13 | * 14 | */ 15 | public interface QualifiedContentProvider { 16 | 17 | /** 18 | * start accept the classes 19 | * @param content 20 | * @param processor 21 | * @throws IOException 22 | */ 23 | void forEach(QualifiedContent content, ClassHandler processor) throws IOException; 24 | } 25 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/TargetedQualifiedContentProvider.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | 5 | /** 6 | * Created by gengwanpeng on 17/4/28. 7 | * {@inheritDoc} 8 | */ 9 | public abstract class TargetedQualifiedContentProvider implements QualifiedContentProvider { 10 | 11 | /** 12 | * Judge the QualifiedContent type 13 | * @param qualifiedContent {@link com.android.build.api.transform.JarInput} or {@link com.android.build.api.transform.DirectoryInput} 14 | * @return can this provider accept this QualifiedContent. 15 | */ 16 | public abstract boolean accepted(QualifiedContent qualifiedContent); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/framework/TransformContext.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.framework; 2 | 3 | import com.android.build.api.transform.DirectoryInput; 4 | import com.android.build.api.transform.Format; 5 | import com.android.build.api.transform.JarInput; 6 | import com.android.build.api.transform.QualifiedContent; 7 | import com.android.build.api.transform.TransformInvocation; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.Collections; 14 | 15 | /** 16 | * Created by gengwanpeng on 17/4/26. 17 | * 18 | * A data sets collect all jar info and pre-analysis result. 19 | * 20 | */ 21 | public class TransformContext { 22 | 23 | private TransformInvocation invocation; 24 | 25 | private Collection allJars; 26 | private Collection addedJars; 27 | private Collection removedJars; 28 | private Collection changedJars; 29 | private Collection allDirs; 30 | 31 | 32 | public TransformContext(TransformInvocation invocation) { 33 | this.invocation = invocation; 34 | init(); 35 | } 36 | 37 | /** 38 | * start collect. 39 | */ 40 | private void init() { 41 | allJars = new ArrayList<>(invocation.getInputs().size()); 42 | addedJars = new ArrayList<>(invocation.getInputs().size()); 43 | changedJars = new ArrayList<>(invocation.getInputs().size()); 44 | removedJars = new ArrayList<>(invocation.getInputs().size()); 45 | allDirs = new ArrayList<>(invocation.getInputs().size()); 46 | invocation.getInputs().forEach(it -> { 47 | it.getJarInputs().forEach(j -> { 48 | allJars.add(j); 49 | if (invocation.isIncremental()) { 50 | switch (j.getStatus()) { 51 | case ADDED: 52 | addedJars.add(j); 53 | break; 54 | case REMOVED: 55 | removedJars.add(j); 56 | break; 57 | case CHANGED: 58 | changedJars.add(j); 59 | } 60 | } 61 | }); 62 | allDirs.addAll(it.getDirectoryInputs()); 63 | }); 64 | } 65 | 66 | 67 | public boolean isIncremental() { 68 | return invocation.isIncremental(); 69 | } 70 | 71 | public Collection getAllJars() { 72 | return Collections.unmodifiableCollection(allJars); 73 | } 74 | 75 | public Collection getAllDirs() { 76 | return Collections.unmodifiableCollection(allDirs); 77 | } 78 | 79 | public Collection getAddedJars() { 80 | return Collections.unmodifiableCollection(addedJars); 81 | } 82 | 83 | public Collection getChangedJars() { 84 | return Collections.unmodifiableCollection(changedJars); 85 | } 86 | 87 | public Collection getRemovedJars() { 88 | return Collections.unmodifiableCollection(removedJars); 89 | } 90 | 91 | public File getRelativeFile(QualifiedContent content) { 92 | return invocation.getOutputProvider().getContentLocation(content.getName(), content.getContentTypes(), content.getScopes(), 93 | (content instanceof JarInput ? Format.JAR : Format.DIRECTORY)); 94 | } 95 | 96 | public void clear() throws IOException { 97 | invocation.getOutputProvider().deleteAll(); 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "TransformContext{" + 103 | "allJars=" + allJars + 104 | ", addedJars=" + addedJars + 105 | ", removedJars=" + removedJars + 106 | ", changedJars=" + changedJars + 107 | ", allDirs=" + allDirs + 108 | '}'; 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/ILogger.java: -------------------------------------------------------------------------------- 1 | // 2 | // _ooOoo_ 3 | // o8888888o 4 | // 88" . "88 5 | // (| -_- |) 6 | // O\ = /O 7 | // ____/`---'\____ 8 | // . ' \\| |// `. 9 | // / \\||| : |||// \ 10 | // / _||||| -:- |||||- \ 11 | // | | \\\ - /// | | 12 | // | \_| ''\---/'' | | 13 | // \ .-\__ `-` ___/-. / 14 | // ___`. .' /--.--\ `. . __ 15 | // ."" '< `.___\_<|>_/___.' >'"". 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | // \ \ `-. \_ __\ /__ _/ .-` / / 18 | // ======`-.____`-.___\_____/___.-`____.-'====== 19 | // `=---=' 20 | // 21 | // ............................................. 22 | package me.zane.easyrouter_merge.log; 23 | 24 | /** 25 | * Created by gengwanpeng on 16/7/6. 26 | */ 27 | public interface ILogger { 28 | 29 | void d(String tag, String msg); 30 | 31 | void i(String tag, String msg); 32 | 33 | void w(String tag, String msg); 34 | 35 | void w(String tag, String msg, Throwable t); 36 | 37 | void e(String tag, String msg); 38 | 39 | void e(String tag, String msg, Throwable t); 40 | } 41 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Impl/BaseLogger.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.log.Impl; 2 | 3 | 4 | import org.gradle.api.logging.LogLevel; 5 | 6 | import java.io.PrintWriter; 7 | import java.io.StringWriter; 8 | 9 | import me.zane.easyrouter_merge.log.ILogger; 10 | 11 | 12 | /** 13 | * Created by gengwanpeng on 17/5/17. 14 | */ 15 | public abstract class BaseLogger implements ILogger { 16 | 17 | @Override 18 | public void d(String tag, String msg) { 19 | write(LogLevel.DEBUG,tag, msg, null); 20 | } 21 | 22 | @Override 23 | public void i(String tag, String msg) { 24 | write(LogLevel.INFO,tag, msg, null); 25 | } 26 | 27 | @Override 28 | public void w(String tag, String msg) { 29 | w(tag, msg, null); 30 | } 31 | 32 | @Override 33 | public void w(String tag, String msg, Throwable t) { 34 | write(LogLevel.WARN,tag, msg, t); 35 | } 36 | 37 | @Override 38 | public void e(String tag, String msg) { 39 | e(tag, msg, null); 40 | } 41 | 42 | @Override 43 | public void e(String tag, String msg, Throwable t) { 44 | write(LogLevel.WARN,tag, msg, t); 45 | } 46 | 47 | protected abstract void write(LogLevel level, String prefix, String msg, Throwable t); 48 | 49 | static String stackToString(Throwable t) { 50 | StringWriter sw = new StringWriter(128); 51 | PrintWriter ps = new PrintWriter(sw); 52 | t.printStackTrace(ps); 53 | ps.flush(); 54 | return sw.toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Impl/FileLoggerImpl.java: -------------------------------------------------------------------------------- 1 | // 2 | // _ooOoo_ 3 | // o8888888o 4 | // 88" . "88 5 | // (| -_- |) 6 | // O\ = /O 7 | // ____/`---'\____ 8 | // . ' \\| |// `. 9 | // / \\||| : |||// \ 10 | // / _||||| -:- |||||- \ 11 | // | | \\\ - /// | | 12 | // | \_| ''\---/'' | | 13 | // \ .-\__ `-` ___/-. / 14 | // ___`. .' /--.--\ `. . __ 15 | // ."" '< `.___\_<|>_/___.' >'"". 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | // \ \ `-. \_ __\ /__ _/ .-` / / 18 | // ======`-.____`-.___\_____/___.-`____.-'====== 19 | // `=---=' 20 | // 21 | // ............................................. 22 | package me.zane.easyrouter_merge.log.Impl; 23 | 24 | 25 | import org.gradle.api.logging.LogLevel; 26 | 27 | import java.io.FileNotFoundException; 28 | import java.io.FileOutputStream; 29 | import java.io.PrintWriter; 30 | 31 | 32 | /** 33 | * Created by gengwanpeng on 16/7/6. 34 | */ 35 | public class FileLoggerImpl extends BaseLogger { 36 | 37 | public static FileLoggerImpl of(String fileName) throws FileNotFoundException { 38 | PrintWriter pr = new PrintWriter(new FileOutputStream(fileName), true); 39 | return new FileLoggerImpl(pr); 40 | } 41 | 42 | private PrintWriter pr; 43 | 44 | private FileLoggerImpl(PrintWriter pr) { 45 | this.pr = pr; 46 | } 47 | 48 | @Override 49 | protected void write(LogLevel level, String prefix, String msg, Throwable t) { 50 | pr.println(String.format("%s [%-10s] %s",level.name(), prefix, msg)); 51 | if (t != null) { 52 | t.printStackTrace(pr); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Impl/NoLogger.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.log.Impl; 2 | 3 | import org.gradle.api.logging.LogLevel; 4 | 5 | /** 6 | * Created by Jude on 2017/12/8. 7 | */ 8 | 9 | public class NoLogger extends BaseLogger { 10 | @Override 11 | protected void write(LogLevel level, String prefix, String msg, Throwable t) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Impl/SystemLoggerImpl.java: -------------------------------------------------------------------------------- 1 | // 2 | // _ooOoo_ 3 | // o8888888o 4 | // 88" . "88 5 | // (| -_- |) 6 | // O\ = /O 7 | // ____/`---'\____ 8 | // . ' \\| |// `. 9 | // / \\||| : |||// \ 10 | // / _||||| -:- |||||- \ 11 | // | | \\\ - /// | | 12 | // | \_| ''\---/'' | | 13 | // \ .-\__ `-` ___/-. / 14 | // ___`. .' /--.--\ `. . __ 15 | // ."" '< `.___\_<|>_/___.' >'"". 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | // \ \ `-. \_ __\ /__ _/ .-` / / 18 | // ======`-.____`-.___\_____/___.-`____.-'====== 19 | // `=---=' 20 | // 21 | // ............................................. 22 | package me.zane.easyrouter_merge.log.Impl; 23 | 24 | import org.gradle.api.logging.LogLevel; 25 | import org.gradle.api.logging.Logger; 26 | import org.gradle.api.logging.Logging; 27 | 28 | /** 29 | * Created by gengwanpeng on 16/7/6. 30 | */ 31 | public class SystemLoggerImpl extends BaseLogger { 32 | 33 | private final Logger logger = Logging.getLogger("lancet"); 34 | 35 | @Override 36 | protected synchronized void write(LogLevel level, String prefix, String msg, Throwable t) { 37 | logger.log(level,String.format("[%-10s] %s", prefix, msg)); 38 | if (t != null) { 39 | logger.log(level,stackToString(t)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Impl/SystemOutputImpl.java: -------------------------------------------------------------------------------- 1 | // 2 | // _ooOoo_ 3 | // o8888888o 4 | // 88" . "88 5 | // (| -_- |) 6 | // O\ = /O 7 | // ____/`---'\____ 8 | // . ' \\| |// `. 9 | // / \\||| : |||// \ 10 | // / _||||| -:- |||||- \ 11 | // | | \\\ - /// | | 12 | // | \_| ''\---/'' | | 13 | // \ .-\__ `-` ___/-. / 14 | // ___`. .' /--.--\ `. . __ 15 | // ."" '< `.___\_<|>_/___.' >'"". 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | // \ \ `-. \_ __\ /__ _/ .-` / / 18 | // ======`-.____`-.___\_____/___.-`____.-'====== 19 | // `=---=' 20 | // 21 | // ............................................. 22 | package me.zane.easyrouter_merge.log.Impl; 23 | 24 | import org.gradle.api.logging.LogLevel; 25 | 26 | import java.io.PrintStream; 27 | 28 | /** 29 | * Created by gengwanpeng on 16/7/6. 30 | */ 31 | public class SystemOutputImpl extends BaseLogger { 32 | 33 | @Override 34 | protected void write(LogLevel level, String prefix, String msg, Throwable t) { 35 | PrintStream ps = System.out; 36 | if (prefix.charAt(0) == 'E' || prefix.charAt(0) == 'W') { 37 | ps = System.err; 38 | } 39 | ps.println((String.format("%s [%-10s] %s", level.name(),prefix, msg))); 40 | if (t != null) { 41 | ps.println(level.name()+" "+stackToString(t)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/log/Log.java: -------------------------------------------------------------------------------- 1 | // 2 | // _ooOoo_ 3 | // o8888888o 4 | // 88" . "88 5 | // (| -_- |) 6 | // O\ = /O 7 | // ____/`---'\____ 8 | // . ' \\| |// `. 9 | // / \\||| : |||// \ 10 | // / _||||| -:- |||||- \ 11 | // | | \\\ - /// | | 12 | // | \_| ''\---/'' | | 13 | // \ .-\__ `-` ___/-. / 14 | // ___`. .' /--.--\ `. . __ 15 | // ."" '< `.___\_<|>_/___.' >'"". 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | // \ \ `-. \_ __\ /__ _/ .-` / / 18 | // ======`-.____`-.___\_____/___.-`____.-'====== 19 | // `=---=' 20 | // 21 | // ............................................. 22 | package me.zane.easyrouter_merge.log; 23 | 24 | 25 | import me.zane.easyrouter_merge.log.Impl.SystemOutputImpl; 26 | 27 | /** 28 | * Created by gengwanpeng on 16/7/6. 29 | */ 30 | public class Log { 31 | private static ILogger logger = new SystemOutputImpl(); 32 | private static Level level = Level.INFO; 33 | public static final String DEFAULT_TAG = "EasyRouter"; 34 | 35 | public static void setLevel(Level l) { 36 | level = l; 37 | } 38 | 39 | public static void setImpl(ILogger l) { 40 | logger = l; 41 | } 42 | 43 | public static Tag tag(String tag) { 44 | return new Tag(tag); 45 | } 46 | 47 | public static void d(String msg) { 48 | tag(DEFAULT_TAG).d(msg); 49 | } 50 | 51 | public static void i(String msg) { 52 | tag(DEFAULT_TAG).i(msg); 53 | } 54 | 55 | public static void w(String msg) { 56 | w(msg, null); 57 | } 58 | 59 | public static void w(String msg, Throwable t) { 60 | tag(DEFAULT_TAG).w(msg, t); 61 | } 62 | 63 | public static void e(String msg) { 64 | e(msg, null); 65 | } 66 | 67 | public static void e(String msg, Throwable t) { 68 | tag(DEFAULT_TAG).e(msg, t); 69 | } 70 | 71 | public enum Level { 72 | DEBUG, INFO, WARN, ERROR 73 | } 74 | 75 | public static class Tag { 76 | private final String tag; 77 | 78 | Tag(String tag) { 79 | this.tag = tag; 80 | } 81 | 82 | public Tag d(String msg) { 83 | if (level.compareTo(Level.DEBUG) <= 0) { 84 | logger.d(tag, msg); 85 | } 86 | return this; 87 | } 88 | 89 | public Tag i(String msg) { 90 | if (level.compareTo(Level.INFO) <= 0) { 91 | logger.i(tag, msg); 92 | } 93 | return this; 94 | } 95 | 96 | public Tag w(String msg) { 97 | return w(msg, null); 98 | } 99 | 100 | public Tag w(String msg, Throwable t) { 101 | if (level.compareTo(Level.WARN) <= 0) { 102 | logger.w(tag, msg, t); 103 | } 104 | return this; 105 | } 106 | 107 | public Tag e(String msg) { 108 | return e(msg, null); 109 | } 110 | 111 | public Tag e(String msg, Throwable t) { 112 | if (level.compareTo(Level.ERROR) <= 0) { 113 | logger.e(tag, msg, t); 114 | } 115 | return this; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/merge/MergeClassVisitor.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.merge; 2 | 3 | import org.objectweb.asm.ClassVisitor; 4 | import org.objectweb.asm.MethodVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.AbstractInsnNode; 7 | import org.objectweb.asm.tree.MethodNode; 8 | 9 | import me.zane.easyrouter_merge.bean.MergeInfo; 10 | 11 | /** 12 | * Created by Zane on 2018/5/17. 13 | * Email: zanebot96@gmail.com 14 | */ 15 | public class MergeClassVisitor extends ClassVisitor{ 16 | private MergeInfo mergeInfo; 17 | 18 | public MergeClassVisitor(ClassVisitor cv, MergeInfo mergeInfo) { 19 | super(Opcodes.ASM5, cv); 20 | this.mergeInfo = mergeInfo; 21 | } 22 | 23 | @Override 24 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 25 | super.visit(version, access, name, signature, superName, interfaces); 26 | } 27 | 28 | @Override 29 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 30 | MethodVisitor next = super.visitMethod(access, name, desc, signature, exceptions); 31 | if ("initTable".equals(name)) { 32 | for (MethodNode node : mergeInfo.getMapMethods()) { 33 | insertMethod(node, next); 34 | } 35 | } 36 | 37 | return next; 38 | } 39 | 40 | private void insertMethod(MethodNode methodNode, MethodVisitor mv){ 41 | AbstractInsnNode insnNode = methodNode.instructions.getFirst(); 42 | while (insnNode!=null && insnNode.getOpcode() != Opcodes.RETURN){ 43 | insnNode.accept(mv); 44 | insnNode = insnNode.getNext(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/merge/MergeEngine.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.merge; 2 | 3 | import java.io.IOException; 4 | 5 | import me.zane.easyrouter_merge.bean.MergeInfo; 6 | import me.zane.easyrouter_merge.framework.ContextReader; 7 | import me.zane.easyrouter_merge.framework.DirectoryContentProvider; 8 | import me.zane.easyrouter_merge.framework.TransformContext; 9 | import me.zane.easyrouter_merge.log.Log; 10 | 11 | /** 12 | * Created by Zane on 2018/5/17. 13 | * Email: zanebot96@gmail.com 14 | */ 15 | public class MergeEngine { 16 | private TransformContext context; 17 | 18 | public MergeEngine(TransformContext context) { 19 | this.context = context; 20 | } 21 | 22 | public void startMerge(MergeInfo mergeInfo) { 23 | ContextReader cr = new ContextReader(context, new DirectoryContentProvider()); 24 | MergeHandler handler = new MergeHandler(context, mergeInfo); 25 | 26 | try { 27 | cr.accept(handler); 28 | } catch (Exception e) { 29 | Log.e("exception in merge modular file: " + e.getLocalizedMessage()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/merge/MergeHandler.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.merge; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | import com.android.build.api.transform.Status; 5 | 6 | import org.objectweb.asm.ClassReader; 7 | import org.objectweb.asm.ClassWriter; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import me.zane.easyrouter_merge.bean.MergeInfo; 13 | import me.zane.easyrouter_merge.framework.ClassHandler; 14 | import me.zane.easyrouter_merge.framework.DirectoryContentProvider; 15 | import me.zane.easyrouter_merge.framework.DirectoryWriter; 16 | import me.zane.easyrouter_merge.framework.TransformContext; 17 | import me.zane.easyrouter_merge.log.Log; 18 | 19 | import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; 20 | import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; 21 | 22 | /** 23 | * Created by Zane on 2018/5/17. 24 | * 25 | * 作用于主modular的dir input 26 | * Email: zanebot96@gmail.com 27 | */ 28 | public class MergeHandler implements ClassHandler{ 29 | private TransformContext context; 30 | private MergeInfo mergeInfo; 31 | private DirectoryWriter directoryWriter; 32 | 33 | public MergeHandler(TransformContext context, MergeInfo mergeInfo) { 34 | this.context = context; 35 | this.mergeInfo = mergeInfo; 36 | directoryWriter = new DirectoryWriter(); 37 | } 38 | 39 | @Override 40 | public boolean onStart(QualifiedContent content) throws IOException { 41 | return true; 42 | } 43 | 44 | @Override 45 | public void onClassFetch(QualifiedContent content, Status status, String relativePath, byte[] bytes) throws IOException { 46 | File outputFile = context.getRelativeFile(content); 47 | byte[] finalBytes = bytes; 48 | if (relativePath.endsWith(".class") && relativePath.contains("com/zane/easyrouter_generated")) { 49 | ClassReader classReader = new ClassReader(finalBytes); 50 | ClassWriter classWriter = new ClassWriter(0); 51 | MergeClassVisitor mergeClassVisitor = new MergeClassVisitor(classWriter, mergeInfo); 52 | classReader.accept(mergeClassVisitor, 0); 53 | finalBytes = classWriter.toByteArray(); 54 | } 55 | 56 | directoryWriter.write(outputFile, relativePath, finalBytes); 57 | } 58 | 59 | @Override 60 | public void onComplete(QualifiedContent content) throws IOException { 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/rename/RenameClassVisistor.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.rename; 2 | 3 | 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.Opcodes; 6 | 7 | /** 8 | * Created by Zane on 2018/5/17. 9 | * 10 | * 这个ASM visitor运作于修改modular中EasyRouterTable类的名字 11 | * 我们需要判断当前这个class是否是EasyRouterTable类 12 | * Email: zanebot96@gmail.com 13 | */ 14 | public class RenameClassVisistor extends ClassVisitor { 15 | private boolean isFindTarget; 16 | private String finalName; 17 | 18 | public RenameClassVisistor(ClassVisitor cw) { 19 | super(Opcodes.ASM5, cw); 20 | } 21 | 22 | public boolean isFindTarget() { 23 | return isFindTarget; 24 | } 25 | 26 | public String getFinalName() { 27 | return finalName; 28 | } 29 | 30 | @Override 31 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 32 | finalName = name; 33 | if ("com/zane/easyrouter_generated/EasyRouterTable".equals(name)) { 34 | isFindTarget = true; 35 | finalName = finalName.replace("easyrouter", "easyrouter_" + RenameKey.getKEY()); 36 | } 37 | super.visit(version, access, finalName, signature, superName, interfaces); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/rename/RenameEngine.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.rename; 2 | 3 | import org.gradle.api.Project; 4 | 5 | import java.io.IOException; 6 | 7 | import me.zane.easyrouter_merge.framework.ContextReader; 8 | import me.zane.easyrouter_merge.framework.DirectoryContentProvider; 9 | import me.zane.easyrouter_merge.framework.TransformContext; 10 | import me.zane.easyrouter_merge.log.Log; 11 | 12 | /** 13 | * Created by Zane on 2018/5/17. 14 | * Email: zanebot96@gmail.com 15 | */ 16 | public class RenameEngine { 17 | private TransformContext context; 18 | 19 | public RenameEngine(TransformContext context) { 20 | this.context = context; 21 | } 22 | 23 | public void stratRename(Project project) { 24 | RenameKey.init(project); 25 | ContextReader reader = new ContextReader(context, new DirectoryContentProvider()); 26 | RenameHandler handler = new RenameHandler(context); 27 | 28 | try { 29 | reader.accept(handler); 30 | } catch (Exception e) { 31 | Log.e("exception in rename modular file: " + e.getLocalizedMessage()); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/rename/RenameHandler.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.rename; 2 | 3 | import com.android.build.api.transform.QualifiedContent; 4 | import com.android.build.api.transform.Status; 5 | 6 | import org.objectweb.asm.ClassReader; 7 | import org.objectweb.asm.ClassWriter; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import me.zane.easyrouter_merge.framework.ClassHandler; 13 | import me.zane.easyrouter_merge.framework.DirectoryWriter; 14 | import me.zane.easyrouter_merge.framework.TransformContext; 15 | import me.zane.easyrouter_merge.log.Log; 16 | 17 | /** 18 | * Created by Zane on 2018/5/17. 19 | * 20 | * 接受transform异步的input输入,这里接受dir类型的input 21 | * 如果是我们需要的类,那么丢进ASM进行下一步操作 22 | * 在ASM跑完了之后,写入到output 23 | * 24 | * Email: zanebot96@gmail.com 25 | */ 26 | public class RenameHandler implements ClassHandler{ 27 | private TransformContext context; 28 | private DirectoryWriter directoryWriter; 29 | 30 | public RenameHandler(TransformContext context) { 31 | this.context = context; 32 | directoryWriter = new DirectoryWriter(); 33 | } 34 | 35 | @Override 36 | public boolean onStart(QualifiedContent content) throws IOException { 37 | return true; 38 | } 39 | 40 | @Override 41 | public void onClassFetch(QualifiedContent content, Status status, String relativePath, byte[] bytes) throws IOException { 42 | File outputFile = context.getRelativeFile(content); 43 | byte[] finalBytes = bytes; 44 | if (relativePath.endsWith(".class") && relativePath.contains("com/zane/easyrouter_generated")) { 45 | ClassReader classReader = new ClassReader(finalBytes); 46 | ClassWriter classWriter = new ClassWriter(0); 47 | RenameClassVisistor classVisistor = new RenameClassVisistor(classWriter); 48 | classReader.accept(classVisistor, 0); 49 | 50 | if (classVisistor.isFindTarget()) { 51 | relativePath = classVisistor.getFinalName() + ".class"; 52 | finalBytes = classWriter.toByteArray(); 53 | } 54 | } 55 | 56 | directoryWriter.write(outputFile, relativePath, finalBytes); 57 | } 58 | 59 | @Override 60 | public void onComplete(QualifiedContent content) throws IOException { 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/java/me/zane/easyrouter_merge/rename/RenameKey.java: -------------------------------------------------------------------------------- 1 | package me.zane.easyrouter_merge.rename; 2 | 3 | import org.gradle.api.Project; 4 | 5 | /** 6 | * Created by Zane on 2018/5/17. 7 | * Email: zanebot96@gmail.com 8 | */ 9 | public class RenameKey { 10 | public static String KEY; 11 | 12 | public static void init(Project project){ 13 | KEY = project.getGroup()+"_"+project.getName(); 14 | } 15 | 16 | public static String getKEY(){ 17 | return KEY; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /easyrouter-merge/src/main/resources/META-INF/gradle-plugins/me.zane.easyrouter-merge.properties: -------------------------------------------------------------------------------- 1 | implementation-class=me.zane.easyrouter_merge.RouterModulePlugin -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | isDebug=false 20 | 21 | PROJ_NAME=easyrouterMerge 22 | PROJ_ARTIFACTID=easyrouterMerge 23 | PROJ_POM_NAME=Remote Repository 24 | 25 | LOCAL_REPO_URL=file:///Users/Zane/programming/AndroidProject2/EasyRouter/ 26 | 27 | # 包信息 28 | PROJ_GROUP=me.zane.routermerge 29 | PROJ_VERSION=1.0.0 30 | 31 | # 项目的描述 32 | PROJ_WEBSITEURL=https://bugtags.com 33 | PROJ_ISSUETRACKERURL=https://github.com/bugtags/Bugtags-Android/issues 34 | PROJ_VCSURL=https://github.com/bugtags/Bugtags-Android.git 35 | PROJ_DESCRIPTION=Simple and effective bug & crash reporting tool for Android apps 36 | 37 | # Licence信息 38 | PROJ_LICENCE_NAME=The Apache Software License, Version 2.0 39 | PROJ_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 40 | PROJ_LICENCE_DEST=repo 41 | 42 | # Developer 信息 43 | DEVELOPER_ID=your-dev-id 44 | DEVELOPER_NAME=your-dev-name 45 | DEVELOPER_EMAIL=your-email@your-mailbox.com 46 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zane96/EasyRouter/29e8a3c00dffdb653aa0bab3f29ec2e1528a0c69/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Apr 19 13:18:52 CST 2018 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-4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 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 Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # EasyRouter 4 | 5 | 欢迎加入Android技术交流群,群号码:577953847 6 | 7 | EasyRouter是一个简易的使用字符串进行Activity,Browser跳转的路由框架,并支持组件化开发。 8 | 9 | ## Features 10 | + EasyRouter实现了通过字符串进行Activity之间跳转路由,通过APT在编译器实现路由表的构建,劫持了startActivity()进行动态路由 11 | + EasyRouter实现了Activity之间跳转,返回时的数据自动注入,完全屏蔽了原生的一套繁琐API 12 | + EasyRouter实现了通过字符串进程Browser的路由跳转 13 | + EasyRouter支持更换路由跳转时数据序列化的解析器,默认为Gson,可以通过EasyRouterSet进行更换 14 | + EasyRouter劫持了onActivityResult(),并将其改为接口回调 15 | + EasyRouter通过transform+ASM实现了一个gradle插件来支持组件化开发 16 | 17 | ## Usage 18 | 19 | 1.在Activity中自定义URL标识符,目前只支持单一的URL标识,URL的Scheme均为**activity://** 20 | 21 | ```java 22 | @Route("activity://main") 23 | public class MainActivity extends AppCompatActivity { 24 | } 25 | ``` 26 | 27 | 2.在Application中进行初始化 28 | 29 | ```java 30 | EasyRouter.init(this); 31 | ``` 32 | 33 | 3.跳转 34 | 35 | + Activity之间无参数传递跳转 36 | 37 | ```java 38 | Message message = new MessageBuilder().setAddress("activity://two").build(); 39 | EasyRouter.route(MainActivity.this, message); 40 | ``` 41 | 42 | + Activity之间有参数传递跳转 43 | ```java 44 | Message message = new MessageBuilder() 45 | .setAddress("activity://two") 46 | .addParam("data", "haha", String.class) 47 | .addParam("person", new Person(21, "Zane"), Person.class) 48 | .build(); 49 | EasyRouter.route(MainActivity.this, message); 50 | ``` 51 | 52 | 被启动Activity需要用@Param标记参数变量 53 | ```java 54 | @Route("activity://two") 55 | public class ActivityTwo extends AppCompatActivity{ 56 | 57 | @Param("data") 58 | public String data; 59 | @Param("person") 60 | public Person person; 61 | } 62 | ``` 63 | + Activity需要回调的启动(startActivityForResult()) 64 | ```java 65 | Message message = new MessageBuilder() 66 | .setAddress("activity://two") 67 | .addParam("data", "haha", String.class) 68 | .addParam("person", new Person(21, "Zane"), Person.class) 69 | .build(); 70 | EasyRouter.routeForResult(MainActivity.this, message, new OnActivityResultListener() { 71 | @Override 72 | public void onActivityResult(int resultCode, Intent data) { 73 | //dosomething 74 | } 75 | }); 76 | ``` 77 | 78 | 如果有返回的数据,需要通过@Result标记返回参数变量 79 | ```java 80 | @Route("activity://main") 81 | public class MainActivity extends AppCompatActivity { 82 | 83 | @Result("result_three") 84 | public String resultThree; 85 | @Result("result_two") 86 | public String resultTwo; 87 | } 88 | ``` 89 | 90 | + Activity的setResult() 91 | ```java 92 | EasyRouter.setResult(ActivityTwo.this, 0, new MessageBuilder() 93 | .addParam("result_two", "data from two", String.class) 94 | .build()); 95 | finish(); 96 | ``` 97 | 98 | + Web页面跳转 99 | 100 | ```java 101 | EasyRouter.route(MainActivity.this, new MessageBuilder() 102 | .setAddress("http://xzane.cc") 103 | .build()); 104 | ``` 105 | 106 | + 更改数据序列化的工具 107 | 108 | 首先需要实现一个序列化工具的工厂类,可以参考框架中的GsonConvertFactory 109 | 110 | ```java 111 | EasyRouterSet.setConverterFactory(GsonConventerFactory.creat()); 112 | ``` 113 | 114 | ## Principle 115 | 116 | + 使用动态代理生成**Instrumentation**代理类并通过反射替换ActivityThread中的Instrumentation变量,劫持execStartActivity()方法达到动态查找路由表并进行跳转的效果 117 | + 所有的参数注入代码以及路由表的代码生成均通过APT在编译期完成 118 | + onActivityResult方法的劫持是通过生成无View的Fragment达到的,借鉴了RxPermission 119 | + 通过registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback)去劫持所有Activity的onCreat()方法 120 | + 编译期通过transform获取编译产物,使用ASM进行AOP操作融合application和library的路由表 121 | 122 | ## Dependency 123 | 124 | + 在Project build.gradle中 125 | ```groovy 126 | allprojects { 127 | repositories { 128 | maven { url "https://jitpack.io" } 129 | } 130 | } 131 | ``` 132 | + 在Application build.gradle中 133 | ```groovy 134 | dependencies { 135 | compile 'com.github.Zane96.EasyRouter:router:v1.1.1' 136 | annotationProcessor 'com.github.Zane96.EasyRouter:easyrouter-compiler:v1.1.1' 137 | } 138 | ``` 139 | 140 | + 如果您希望支持Android组件化开发,那么还需要在Project的build.gradle中添加如下依赖: 141 | 142 | ```groovy 143 | buildscript { 144 | dependencies { 145 | classpath 'com.zane:easyrouterMerge:1.0.0' 146 | } 147 | } 148 | ``` 149 | 150 | 并在Application和Library的build.gradle中均添加如下插件: 151 | 152 | ```groovy 153 | apply plugin: 'me.zane.easyrouter-merge' 154 | ``` 155 | 156 | 157 | 158 | ## TODO 159 | 160 | + ~返回数据,自动注入~ 161 | + ~请求报文实体类封装,Builder类生成URL,头部,数据body~ 162 | + ~支持组件化开发~ 163 | + 跳转时候的参数应该不依赖key的值,要自动或者手动注入 164 | + 地址做成多个 165 | 166 | ## License 167 | 168 | Copyright 2017 Zane 169 | 170 | Licensed under the Apache License, Version 2.0 (the "License"); 171 | you may not use this file except in compliance with the License. 172 | You may obtain a copy of the License at 173 | 174 | http://www.apache.org/licenses/LICENSE-2.0 175 | 176 | Unless required by applicable law or agreed to in writing, software 177 | distributed under the License is distributed on an "AS IS" BASIS, 178 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 179 | See the License for the specific language governing permissions and 180 | limitations under the License. 181 | -------------------------------------------------------------------------------- /router/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /router/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group='com.github.Zane96' 4 | 5 | android { 6 | compileSdkVersion 24 7 | buildToolsVersion '26.0.2' 8 | 9 | defaultConfig { 10 | minSdkVersion 15 11 | targetSdkVersion 24 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(include: ['*.jar'], dir: 'libs') 28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 29 | exclude group: 'com.android.support', module: 'support-annotations' 30 | }) 31 | compile 'com.android.support:appcompat-v7:24.2.1' 32 | testCompile 'junit:junit:4.12' 33 | compile project(':easyrouter-annotation') 34 | provided project(':easyrouter-generated') 35 | compile 'com.google.code.gson:gson:2.7' 36 | } 37 | -------------------------------------------------------------------------------- /router/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Zane/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /router/src/androidTest/java/com/example/zane/router/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.zane.router; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.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 | * Instrumentation 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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.zane.router.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /router/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/Constant.java: -------------------------------------------------------------------------------- 1 | package com.zane.router; 2 | 3 | /** 4 | * Created by Zane on 2017/4/11. 5 | * Email: zanebot96@gmail.com 6 | * Blog: zane96.github.io 7 | */ 8 | 9 | public class Constant { 10 | //activity路由 11 | public static final String ACTIVITY = "activity"; 12 | //网页路由 13 | public static final String HTTP = "http"; 14 | public static final String HTTPS = "https"; 15 | public static final String GENERATED_PACKAGE = "com.zane.easyrouter_generated"; 16 | } 17 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/EasyRouter.java: -------------------------------------------------------------------------------- 1 | package com.zane.router; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.Application; 6 | import android.content.Context; 7 | 8 | import com.zane.router.router.ActivityRouter; 9 | import com.zane.router.router.BaseRouter; 10 | import com.zane.router.router.HttpRouter; 11 | import com.zane.router.converter.Converter; 12 | import com.zane.router.converter.GsonConventerFactory; 13 | import com.zane.router.hook.Hooker; 14 | import com.zane.router.message.Message; 15 | import com.zane.router.result.OnActivityResultListener; 16 | import com.zane.router.utils.ZLog; 17 | 18 | /** 19 | * 20 | * Created by Zane on 2016/12/23. 21 | * Email: zanebot96@gmail.com 22 | * Blog: zane96.github.io 23 | */ 24 | 25 | public class EasyRouter { 26 | 27 | public static void init(Application context){ 28 | init(context, GsonConventerFactory.creat()); 29 | ZLog.setDebug(false); 30 | } 31 | 32 | /** 33 | * 全局hook 34 | */ 35 | public static void init(Application context, Converter.Factory factory){ 36 | Hooker.hookRouter(context); 37 | EasyRouterSet.setConverterFactory(factory); 38 | } 39 | 40 | /** 41 | * 42 | * @param context 43 | * @param message 44 | */ 45 | public static void route(Context context, Message message){ 46 | BaseRouter router = findRouterFromScheme(message); 47 | router.route(context, message); 48 | } 49 | 50 | /** 51 | * 不带参数的startActivityForResult 52 | * @param context 53 | * @param message 54 | */ 55 | public static void routeForResult(Activity context, Message message, OnActivityResultListener listener){ 56 | ActivityRouter router = new ActivityRouter(); 57 | router.startActivityForResult(context, message, listener); 58 | } 59 | 60 | /** 61 | * 返回 62 | * @param activity 63 | * @param message 64 | */ 65 | public static void setResult(Activity activity, int resultCode, Message message) { 66 | ActivityRouter router = new ActivityRouter(); 67 | router.setResult(activity, resultCode, message); 68 | } 69 | 70 | private static BaseRouter findRouterFromScheme(Message message){ 71 | if (Constant.ACTIVITY.equals(message.getUrl().getScheme())){ 72 | return new ActivityRouter(); 73 | } 74 | return new HttpRouter(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/EasyRouterSet.java: -------------------------------------------------------------------------------- 1 | package com.zane.router; 2 | 3 | import com.zane.router.converter.Converter; 4 | 5 | /** 6 | * EasyRouter配置类 7 | * 8 | * Created by Zane on 2017/4/11. 9 | * Email: zanebot96@gmail.com 10 | * Blog: zane96.github.io 11 | */ 12 | 13 | public class EasyRouterSet { 14 | private static Converter.Factory converterFactory; 15 | 16 | public static Converter.Factory getConverterFactory() { 17 | return converterFactory; 18 | } 19 | 20 | public static void setConverterFactory(Converter.Factory converterFactory) { 21 | EasyRouterSet.converterFactory = converterFactory; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/converter/Converter.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.converter; 2 | 3 | import com.zane.router.exception.ConverterExpection; 4 | 5 | import java.io.IOException; 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * 数据传输过程中的数据序列化工具抽象 10 | * 11 | * Created by Zane on 2017/4/11. 12 | * Email: zanebot96@gmail.com 13 | * Blog: zane96.github.io 14 | */ 15 | 16 | public interface Converter { 17 | 18 | /** 19 | * T -> To, F -> From 20 | * @param value 21 | * @return 22 | * @throws IOException 23 | */ 24 | T convert(F value) throws ConverterExpection; 25 | 26 | abstract class Factory { 27 | 28 | /** 29 | * Creat encodeConventer 30 | * @param type 31 | * @return 32 | */ 33 | public abstract Converter encodeConverter(Type type); 34 | 35 | /** 36 | * Creat decodeConventer 37 | * @param type 38 | * @return 39 | */ 40 | public abstract Converter decodeConverter(Type type); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/converter/GsonConventerFactory.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.converter; 2 | 3 | import com.zane.router.exception.ConverterExpection; 4 | import com.google.gson.Gson; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * Created by Zane on 2017/4/11. 10 | * Email: zanebot96@gmail.com 11 | * Blog: zane96.github.io 12 | */ 13 | 14 | public class GsonConventerFactory extends Converter.Factory { 15 | 16 | private final Gson gson; 17 | 18 | public static Converter.Factory creat() { 19 | return new GsonConventerFactory(new Gson()); 20 | } 21 | 22 | private GsonConventerFactory(Gson gson) { 23 | this.gson = gson; 24 | } 25 | 26 | @Override 27 | public Converter encodeConverter(Type type) { 28 | return new GsonEncodeConverter(type); 29 | } 30 | 31 | @Override 32 | public Converter decodeConverter(Type type) { 33 | return new GsonDecodeConventer<>(type); 34 | } 35 | 36 | private class GsonEncodeConverter implements Converter { 37 | private Type type; 38 | 39 | public GsonEncodeConverter(Type type) { 40 | this.type = type; 41 | } 42 | 43 | @Override 44 | public String convert(T value) throws ConverterExpection { 45 | return gson.toJson(value, type); 46 | } 47 | } 48 | 49 | private class GsonDecodeConventer implements Converter { 50 | private Type type; 51 | 52 | public GsonDecodeConventer(Type type) { 53 | this.type = type; 54 | } 55 | 56 | @Override 57 | public T convert(String value) throws ConverterExpection { 58 | return gson.fromJson(value, type); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/exception/ConverterExpection.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.exception; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 传递数据的转换异常 7 | * Created by Zane on 2017/4/16. 8 | * Email: zanebot96@gmail.com 9 | * Blog: zane96.github.io 10 | */ 11 | 12 | public class ConverterExpection extends IOException{ 13 | public ConverterExpection(String message) { 14 | super(message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/hook/Hooker.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.hook; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.app.Instrumentation; 6 | import android.os.Bundle; 7 | 8 | import com.zane.router.inject.InjectMan; 9 | 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * Created by Zane on 2016/12/23. 15 | * Email: zanebot96@gmail.com 16 | * Blog: zane96.github.io 17 | */ 18 | 19 | public class Hooker { 20 | 21 | private static final InjectMan injectMan = new InjectMan(); 22 | 23 | /** 24 | * Hook startActivity/startActivityForResult两个方法 25 | * Hook activity的onCreat()方法 26 | */ 27 | public static void hookRouter(Application context){ 28 | hookStart(); 29 | OnCreatHooker.hookOnCreat(context, new OnCreatListener() { 30 | @Override 31 | public void beforeOnCreat(Activity activity, Bundle savedInstanceState) { 32 | injectMan.injectParam(activity); 33 | } 34 | }); 35 | } 36 | 37 | private static void hookStart() { 38 | //拿到app的单例ActivityThread实例 39 | Class activityThread = null; 40 | try { 41 | activityThread = Class.forName("android.app.ActivityThread"); 42 | Method method = activityThread.getDeclaredMethod("currentActivityThread"); 43 | method.setAccessible(true); 44 | Object mCurrentThread = method.invoke(null); 45 | 46 | Field mInstrumentation = activityThread.getDeclaredField("mInstrumentation"); 47 | mInstrumentation.setAccessible(true); 48 | Instrumentation mBase = (Instrumentation) mInstrumentation.get(mCurrentThread); 49 | RouterInstrumentation instrumenttation = new RouterInstrumentation(mBase); 50 | mInstrumentation.set(mCurrentThread, instrumenttation); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/hook/OnCreatHooker.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.hook; 2 | 3 | import android.app.Activity; 4 | import android.app.Application; 5 | import android.os.Bundle; 6 | 7 | /** 8 | * 专职回调onCreat()的回调,屏蔽一些不需要的监听 9 | * Created by zane on 2017/1/18. 10 | */ 11 | 12 | public class OnCreatHooker { 13 | 14 | public static void hookOnCreat(Application context, final OnCreatListener listener) { 15 | context.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { 16 | @Override 17 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 18 | listener.beforeOnCreat(activity, savedInstanceState); 19 | } 20 | @Override 21 | public void onActivityStarted(Activity activity) { 22 | } 23 | @Override 24 | public void onActivityResumed(Activity activity) { 25 | } 26 | @Override 27 | public void onActivityPaused(Activity activity) { 28 | } 29 | @Override 30 | public void onActivityStopped(Activity activity) { 31 | } 32 | @Override 33 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 34 | } 35 | @Override 36 | public void onActivityDestroyed(Activity activity) { 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/hook/OnCreatListener.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.hook; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | /** 7 | * Created by zane on 2017/1/18. 8 | */ 9 | 10 | public interface OnCreatListener { 11 | /** 12 | * onCreat()调用之前的回调 13 | * @param activity 14 | * @param savedInstanceState 15 | */ 16 | void beforeOnCreat(Activity activity, Bundle savedInstanceState); 17 | } 18 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/hook/RouterInstrumentation.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.hook; 2 | 3 | import android.app.Activity; 4 | import android.app.Instrumentation; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.IBinder; 9 | 10 | import com.zane.easyrouter_generated.EasyRouterTable; 11 | import com.zane.router.message.Message; 12 | import com.zane.router.router.BaseRouter; 13 | import com.zane.router.utils.ZLog; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | /** 18 | * Created by Zane on 2016/11/28. 19 | * Email: zanebot96@gmail.com 20 | * Blog: zane96.github.io 21 | */ 22 | 23 | public class RouterInstrumentation extends Instrumentation { 24 | 25 | private static final String TAG = RouterInstrumentation.class.getSimpleName(); 26 | 27 | private Instrumentation mBase; 28 | private EasyRouterTable routerTable; 29 | 30 | public RouterInstrumentation(Instrumentation mBase) { 31 | this.mBase = mBase; 32 | this.routerTable = new EasyRouterTable(); 33 | } 34 | 35 | public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity t, 36 | Intent rawIntent, int requestCode, Bundle options){ 37 | 38 | Message message = rawIntent.getParcelableExtra(BaseRouter.ROUTER_MESSAGE); 39 | Message.Url url = null; 40 | if (message != null) { 41 | url = message.getUrl(); 42 | } 43 | 44 | Method execStart = null; 45 | 46 | try { 47 | execStart = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, 48 | IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); 49 | execStart.setAccessible(true); 50 | } catch (NoSuchMethodException e) { 51 | ZLog.e("RouterInstrumentation " + e.getMessage()); 52 | } 53 | 54 | try { 55 | if (url != null){ 56 | Class target = routerTable.queryTable(url.toString()); 57 | Intent intent = new Intent(who, target); 58 | intent.putExtras(rawIntent.getExtras()); 59 | ZLog.i("RouterInstrumentation" + target); 60 | 61 | return (ActivityResult) execStart.invoke(mBase, who, contextThread, token, t, 62 | intent, requestCode, options); 63 | } else { 64 | return (ActivityResult) execStart.invoke(mBase, who, contextThread, token, t, 65 | rawIntent, requestCode, options); 66 | } 67 | }catch (Exception e) { 68 | ZLog.e("RouterInstrumentation " + e.getMessage()); 69 | } 70 | 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/inject/Inject.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.inject; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | 6 | /** 7 | * Created by Zane on 2016/12/29. 8 | * Email: zanebot96@gmail.com 9 | * Blog: zane96.github.io 10 | */ 11 | 12 | public interface Inject { 13 | /** 14 | * 注入正向传递的数据 15 | * @param activity 16 | * @param intent 17 | */ 18 | void injectParam(Activity activity, Intent intent); 19 | 20 | /** 21 | * 注入方向传递的数据 22 | * @param activity 23 | * @param intent 24 | */ 25 | void injectResult(Activity activity, Intent intent); 26 | } 27 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/inject/InjectMan.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.inject; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.zane.easyrouter_annotation.Route; 8 | import com.zane.router.Constant; 9 | import com.zane.router.message.Message; 10 | import com.zane.router.router.BaseRouter; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * 数据注入的始作俑者 17 | * Created by zane on 2017/1/18. 18 | */ 19 | 20 | public class InjectMan { 21 | 22 | //数据注入类的缓存 23 | private static final Map injectMap = new HashMap<>(); 24 | 25 | 26 | /** 27 | * 注入正向参数传递 28 | * @param activity 29 | */ 30 | public static void injectParam(Activity activity) { 31 | Intent intent = activity.getIntent(); 32 | Message message = intent.getParcelableExtra(BaseRouter.ROUTER_MESSAGE); 33 | if (message != null) { 34 | if (message.getBody() != null) { 35 | String className = getTargetClassName(message.getUrl().getBaseUrl()); 36 | String packageName = Constant.GENERATED_PACKAGE; 37 | Inject inject = getInject(className, packageName); 38 | 39 | if (inject != null) { 40 | inject.injectParam(activity, intent); 41 | } 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * 注入反向参数传递 48 | * @param activity 49 | * @param intent 50 | */ 51 | public static void injectResult(Activity activity, Intent intent) { 52 | if (intent != null) { 53 | Message message = intent.getParcelableExtra(BaseRouter.ROUTER_MESSAGE); 54 | if (message != null) { 55 | Route route = activity.getClass().getAnnotation(Route.class); 56 | String className = getTargetClassName(route.value()); 57 | String packageName = Constant.GENERATED_PACKAGE; 58 | Inject inject = getInject(className, packageName); 59 | 60 | if (inject != null) { 61 | inject.injectResult(activity, intent); 62 | } 63 | } 64 | } 65 | } 66 | 67 | private static Inject getInject(String className, String packageName) { 68 | Inject inject = injectMap.get(className); 69 | 70 | if (inject == null) { 71 | try { 72 | Class injectClass = Class.forName(String.format("%s.%s$$Inject", packageName, className)); 73 | inject = (Inject) injectClass.newInstance(); 74 | injectMap.put(className, inject); 75 | } catch (ClassNotFoundException e) { 76 | } catch (InstantiationException e) { 77 | } catch (IllegalAccessException e) { 78 | } 79 | } 80 | 81 | return inject; 82 | } 83 | 84 | /** 85 | * 从url中获取被启动组件的名字 86 | * @param url 87 | * @return 88 | */ 89 | private static String getTargetClassName(String url){ 90 | return url.substring(11, url.length()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/message/Message.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.message; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | /** 11 | * 报文的实体封装类 12 | * Url: activity:// xxxxxxxxx 13 | * scheme authority 14 | * ----------------------- 15 | * url 16 | * 17 | * Message 18 | * ---------------- 19 | * - URL - 20 | * ---------------- 21 | * - Header - 22 | * ---------------- 23 | * - Body - 24 | * ---------------- 25 | * 26 | * Created by Zane on 2017/4/10. 27 | * Email: zanebot96@gmail.com 28 | * Blog: zane96.github.io 29 | */ 30 | 31 | public class Message implements Parcelable { 32 | 33 | private Url url; 34 | private Header header; 35 | private Body body; 36 | 37 | public Message(MessageBuilder builder) { 38 | header = new Header(builder.headers); 39 | body = new Body(builder.params); 40 | url = new Url(builder.scheme, builder.authority); 41 | } 42 | 43 | public MessageBuilder newBuilder(){ 44 | MessageBuilder builder = new MessageBuilder(); 45 | builder.scheme = url.getScheme(); 46 | builder.authority = url.getAuthority(); 47 | builder.params = body.getDatas(); 48 | builder.headers = header.getHeaders(); 49 | return builder; 50 | } 51 | 52 | public Url getUrl() { 53 | return url; 54 | } 55 | 56 | public Header getHeader() { 57 | return header; 58 | } 59 | 60 | public Body getBody() { 61 | return body; 62 | } 63 | 64 | public void setUrl(Url url) { 65 | this.url = url; 66 | } 67 | 68 | public void setHeader(Header header) { 69 | this.header = header; 70 | } 71 | 72 | public void setBody(Body body) { 73 | this.body = body; 74 | } 75 | 76 | public void removeUrl() { 77 | url = null; 78 | } 79 | 80 | public void removeHeader() { 81 | header = null; 82 | } 83 | 84 | public void removeBody() { 85 | body = null; 86 | } 87 | 88 | //---------------------------------------------------------------------------------------------- 89 | public static class Url implements Parcelable { 90 | 91 | private String baseUrl; 92 | private String scheme; 93 | private String authority; 94 | 95 | public Url(String scheme, String authority) { 96 | this.scheme = scheme; 97 | this.authority = authority; 98 | this.baseUrl = scheme + MessageBuilder.TAG_SCHEME_SUFFIX + authority; 99 | } 100 | 101 | public String getBaseUrl() { 102 | return baseUrl; 103 | } 104 | 105 | public String getScheme() { 106 | return scheme; 107 | } 108 | 109 | public String getAuthority() { 110 | return authority; 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | return baseUrl; 116 | } 117 | 118 | @Override 119 | public int describeContents() { 120 | return 0; 121 | } 122 | 123 | @Override 124 | public void writeToParcel(Parcel dest, int flags) { 125 | dest.writeString(this.baseUrl); 126 | dest.writeString(this.scheme); 127 | dest.writeString(this.authority); 128 | } 129 | 130 | protected Url(Parcel in) { 131 | this.baseUrl = in.readString(); 132 | this.scheme = in.readString(); 133 | this.authority = in.readString(); 134 | } 135 | 136 | public static final Creator CREATOR = new Creator() { 137 | @Override 138 | public Url createFromParcel(Parcel source) { 139 | return new Url(source); 140 | } 141 | 142 | @Override 143 | public Url[] newArray(int size) { 144 | return new Url[size]; 145 | } 146 | }; 147 | } 148 | 149 | //---------------------------------------------------------------------------------------------- 150 | public static class Header implements Parcelable { 151 | private Map headers; 152 | 153 | public Header(Map headers) { 154 | this.headers = headers; 155 | } 156 | 157 | public Map getHeaders() { 158 | return headers; 159 | } 160 | 161 | @Override 162 | public String toString() { 163 | return wrapString(headers); 164 | } 165 | 166 | @Override 167 | public int describeContents() { 168 | return 0; 169 | } 170 | 171 | @Override 172 | public void writeToParcel(Parcel dest, int flags) { 173 | dest.writeInt(this.headers.size()); 174 | for (Map.Entry entry : this.headers.entrySet()) { 175 | dest.writeString(entry.getKey()); 176 | dest.writeString(entry.getValue()); 177 | } 178 | } 179 | 180 | protected Header(Parcel in) { 181 | int headersSize = in.readInt(); 182 | this.headers = new HashMap(headersSize); 183 | for (int i = 0; i < headersSize; i++) { 184 | String key = in.readString(); 185 | String value = in.readString(); 186 | this.headers.put(key, value); 187 | } 188 | } 189 | 190 | public static final Creator
CREATOR = new Creator
() { 191 | @Override 192 | public Header createFromParcel(Parcel source) { 193 | return new Header(source); 194 | } 195 | 196 | @Override 197 | public Header[] newArray(int size) { 198 | return new Header[size]; 199 | } 200 | }; 201 | } 202 | 203 | //---------------------------------------------------------------------------------------------- 204 | 205 | public static class Body implements Parcelable { 206 | private Map datas; 207 | 208 | public Body(Map datas) { 209 | this.datas = datas; 210 | } 211 | 212 | public Map getDatas() { 213 | return datas; 214 | } 215 | 216 | @Override 217 | public String toString() { 218 | return wrapString(datas); 219 | } 220 | 221 | @Override 222 | public int describeContents() { 223 | return 0; 224 | } 225 | 226 | @Override 227 | public void writeToParcel(Parcel dest, int flags) { 228 | dest.writeInt(this.datas.size()); 229 | for (Map.Entry entry : this.datas.entrySet()) { 230 | dest.writeString(entry.getKey()); 231 | dest.writeString(entry.getValue()); 232 | } 233 | } 234 | 235 | protected Body(Parcel in) { 236 | int datasSize = in.readInt(); 237 | this.datas = new HashMap(datasSize); 238 | for (int i = 0; i < datasSize; i++) { 239 | String key = in.readString(); 240 | String value = in.readString(); 241 | this.datas.put(key, value); 242 | } 243 | } 244 | 245 | public static final Creator CREATOR = new Creator() { 246 | @Override 247 | public Body createFromParcel(Parcel source) { 248 | return new Body(source); 249 | } 250 | 251 | @Override 252 | public Body[] newArray(int size) { 253 | return new Body[size]; 254 | } 255 | }; 256 | } 257 | 258 | //--------------------------------------------------------------------------------------------- 259 | 260 | private static String getString(String value) { 261 | return value == null ? "" : value; 262 | } 263 | 264 | private static boolean isEmpty(String value){ 265 | return value == null || value.isEmpty(); 266 | } 267 | 268 | private static String wrapString(Map datas) { 269 | StringBuilder sb = new StringBuilder(); 270 | Set> entrySet = datas.entrySet(); 271 | for (Map.Entry entry : entrySet) { 272 | sb.append(entry.getKey()) 273 | .append(MessageBuilder.TAG_PARAMS_OPERATOR) 274 | .append(entry.getValue()) 275 | .append(MessageBuilder.TAG_PARAMS_DIVIDER); 276 | } 277 | return sb.toString(); 278 | } 279 | 280 | // /** 281 | // * String转URL 282 | // * @param urlStr 283 | // * @return 284 | // */ 285 | // public static Message parse(String urlStr) { 286 | // Message.Builder urlBuilder = new Message.Builder(); 287 | // 288 | // //寻找address的结尾地址 289 | // int addressEndPos = 0; 290 | // int paramsStartPos = 0; 291 | // if (!urlStr.contains(TAG_ADDRESS_SUFFIX)) { 292 | // if (urlStr.contains(TAG_PARAMS_OPERATOR)){ 293 | // addressEndPos = 0; 294 | // paramsStartPos = 0; 295 | // }else { 296 | // addressEndPos = urlStr.length(); 297 | // paramsStartPos = addressEndPos; 298 | // } 299 | // }else { 300 | // addressEndPos = urlStr.indexOf(TAG_ADDRESS_SUFFIX); 301 | // paramsStartPos = addressEndPos+TAG_ADDRESS_SUFFIX.length(); 302 | // } 303 | // 304 | // //寻找scheme的结束地址 与 authority的起始地址 305 | // int schemeEndPos = urlStr.indexOf(TAG_SCHEME_SUFFIX); 306 | // int authorityStartPos = schemeEndPos + TAG_SCHEME_SUFFIX.length(); 307 | // if (!urlStr.contains(TAG_SCHEME_SUFFIX)) { 308 | // schemeEndPos = 0; 309 | // authorityStartPos = 0; 310 | // } 311 | // 312 | // String scheme = urlStr.substring(0, schemeEndPos); 313 | // String authority = urlStr.substring(authorityStartPos, addressEndPos); 314 | // String params = urlStr.substring(paramsStartPos, urlStr.length()); 315 | // 316 | // urlBuilder.setScheme(scheme); 317 | // urlBuilder.setAuthority(authority); 318 | // 319 | // if (params != null && !params.isEmpty()) { 320 | // String[] paramsEntries = params.split(TAG_PARAMS_DIVIDER); 321 | // for (String entry : paramsEntries) { 322 | // if (entry.contains(TAG_PARAMS_OPERATOR)) { 323 | // int operatorPos = entry.indexOf(TAG_PARAMS_OPERATOR); 324 | // urlBuilder.addParam(entry.substring(0,operatorPos), entry.substring(operatorPos+1,entry.length())); 325 | // } else { 326 | // throw new IllegalArgumentException("body format error"); 327 | // } 328 | // } 329 | // } 330 | // 331 | // return urlBuilder.build(); 332 | // } 333 | 334 | @Override 335 | public String toString() { 336 | return url.toString() + header.toString() + MessageBuilder.TAG_ADDRESS_SUFFIX + body.toString(); 337 | } 338 | 339 | @Override 340 | public int describeContents() { 341 | return 0; 342 | } 343 | 344 | @Override 345 | public void writeToParcel(Parcel dest, int flags) { 346 | dest.writeParcelable(this.url, flags); 347 | dest.writeParcelable(this.header, flags); 348 | dest.writeParcelable(this.body, flags); 349 | } 350 | 351 | protected Message(Parcel in) { 352 | this.url = in.readParcelable(Url.class.getClassLoader()); 353 | this.header = in.readParcelable(Header.class.getClassLoader()); 354 | this.body = in.readParcelable(Body.class.getClassLoader()); 355 | } 356 | 357 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 358 | @Override 359 | public Message createFromParcel(Parcel source) { 360 | return new Message(source); 361 | } 362 | 363 | @Override 364 | public Message[] newArray(int size) { 365 | return new Message[size]; 366 | } 367 | }; 368 | } 369 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/message/MessageBuilder.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.message; 2 | 3 | import com.zane.router.exception.ConverterExpection; 4 | import com.zane.router.EasyRouterSet; 5 | import com.zane.router.utils.ZLog; 6 | 7 | import java.lang.reflect.Type; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created by Zane on 2017/4/16. 13 | * Email: zanebot96@gmail.com 14 | * Blog: zane96.github.io 15 | */ 16 | 17 | public class MessageBuilder { 18 | 19 | static final String TAG_SCHEME_SUFFIX = "://"; 20 | static final String TAG_PARAMS_DIVIDER = "&"; 21 | static final String TAG_PARAMS_OPERATOR = "="; 22 | static final String TAG_ADDRESS_SUFFIX = "?"; 23 | 24 | String scheme; 25 | String authority; 26 | String address; 27 | Map params; 28 | Map headers; 29 | 30 | public MessageBuilder() { 31 | params = new HashMap<>(); 32 | headers = new HashMap<>(); 33 | } 34 | 35 | public MessageBuilder addParam(String key, Object value, Type type) { 36 | try { 37 | String encodeValue = EasyRouterSet.getConverterFactory().encodeConverter(type).convert(value); 38 | params.put(key, encodeValue); 39 | ZLog.i("testparam " + key + " " + encodeValue); 40 | } catch (ConverterExpection e) { 41 | ZLog.e("Message$Builder" + e.getMessage()); 42 | } 43 | return this; 44 | } 45 | 46 | public MessageBuilder addHeader(String key, String value) { 47 | headers.put(key, value); 48 | return this; 49 | } 50 | 51 | public MessageBuilder setScheme(String scheme) { 52 | this.scheme = scheme; 53 | return this; 54 | } 55 | 56 | public MessageBuilder setAuthority(String authority) { 57 | this.authority = authority; 58 | return this; 59 | } 60 | 61 | public MessageBuilder setAddress(String address) { 62 | //寻找scheme的结束地址 与 authority的起始地址 63 | int schemeEndPos = address.indexOf(TAG_SCHEME_SUFFIX); 64 | int authorityStartPos = schemeEndPos + TAG_SCHEME_SUFFIX.length(); 65 | if (!address.contains(TAG_SCHEME_SUFFIX)) { 66 | schemeEndPos = 0; 67 | authorityStartPos = 0; 68 | } 69 | setScheme(address.substring(0,schemeEndPos)); 70 | setAuthority(address.substring(authorityStartPos,address.length())); 71 | return this; 72 | } 73 | 74 | public Message build() { 75 | return new Message(this); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/result/ActivityResultEngine.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.result; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.support.v4.app.FragmentActivity; 6 | import android.support.v4.app.FragmentManager; 7 | 8 | /** 9 | * Created by zane on 2017/1/11. 10 | * 负责startActivityForResult()的hook引擎启动 11 | */ 12 | 13 | public class ActivityResultEngine{ 14 | 15 | private static final String TAG = "ActivityResultEngine"; 16 | 17 | /** 18 | * 适用于startActivityForResult()的两个重写的方法的hook启动 19 | * @param activity 20 | */ 21 | public static void startHookFragment(Activity activity, int requestCode, Intent rawIntent, OnActivityResultListener listener){ 22 | HookFragment fragment = getValidFragment((FragmentActivity) activity); 23 | fragment.startActivityForResult(requestCode, rawIntent, listener); 24 | } 25 | 26 | private static HookFragment getValidFragment(FragmentActivity activity) { 27 | FragmentManager manager = activity.getSupportFragmentManager(); 28 | HookFragment mHookFragment = (HookFragment) manager.findFragmentByTag(TAG); 29 | boolean isNewInstance = mHookFragment == null; 30 | 31 | if (isNewInstance) { 32 | mHookFragment = new HookFragment(); 33 | manager.beginTransaction() 34 | .add(mHookFragment, TAG) 35 | .commit(); 36 | manager.executePendingTransactions(); 37 | } 38 | 39 | return mHookFragment; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/result/HookFragment.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.result; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.app.Fragment; 5 | 6 | import com.zane.router.inject.InjectMan; 7 | 8 | /** 9 | * Created by zhuchenxi on 16/11/28. 10 | * 11 | * changeby Zane 12 | */ 13 | 14 | public class HookFragment extends Fragment { 15 | 16 | private OnActivityResultListener listener; 17 | 18 | void startActivityForResult(int requestCode, Intent intent, OnActivityResultListener listener){ 19 | startActivityForResult(intent, requestCode); 20 | this.listener = listener; 21 | } 22 | 23 | @Override 24 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 25 | InjectMan.injectResult(getActivity(), data); 26 | listener.onActivityResult(resultCode, data); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/result/OnActivityResultListener.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.result; 2 | 3 | import android.content.Intent; 4 | 5 | /** 6 | * Created by zhuchenxi on 2017/1/11. 7 | */ 8 | 9 | public interface OnActivityResultListener { 10 | 11 | /** 12 | * 只需要关心resultCode,因为都是一对一定向启动Activity的 13 | * @param resultCode 14 | * @param data 15 | */ 16 | void onActivityResult(int resultCode, Intent data); 17 | } 18 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/router/ActivityRouter.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.router; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.zane.router.message.Message; 8 | import com.zane.router.result.OnActivityResultListener; 9 | import com.zane.router.result.ActivityResultEngine; 10 | 11 | /** 12 | * Created by Zane on 2016/12/23. 13 | * Email: zanebot96@gmail.com 14 | * Blog: zane96.github.io 15 | */ 16 | 17 | public class ActivityRouter extends BaseRouter{ 18 | 19 | @Override 20 | void startRoute(Context context, Intent rawIntent) { 21 | context.startActivity(rawIntent); 22 | } 23 | 24 | /** 25 | * startActivityForResult() 26 | * 这里不应该再是activity去启动组件而是由fragment去启动并hook 27 | * @param activity 可以确定是Activity的context 28 | * @param message 29 | */ 30 | public void startActivityForResult(Activity activity, Message message, OnActivityResultListener listener){ 31 | Intent intent = new Intent(); 32 | intent.putExtra(ROUTER_MESSAGE, message); 33 | ActivityResultEngine.startHookFragment(activity, 0, intent, listener); 34 | } 35 | 36 | /** 37 | * setResult() 38 | * @param activity 39 | * @param message 40 | */ 41 | public void setResult(Activity activity, int resultCode, Message message) { 42 | Intent intent = new Intent(); 43 | intent.putExtra(ROUTER_MESSAGE, message); 44 | activity.setResult(resultCode, intent); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/router/BaseRouter.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.router; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import com.zane.router.message.Message; 8 | 9 | /** 10 | * Created by Zane on 2016/12/23. 11 | * Email: zanebot96@gmail.com 12 | * Blog: zane96.github.io 13 | */ 14 | 15 | public abstract class BaseRouter { 16 | 17 | public static final String ROUTER_MESSAGE = "router_message"; 18 | 19 | public void route (Context context, Message message){ 20 | Intent intent = new Intent(); 21 | intent.putExtra(ROUTER_MESSAGE, message); 22 | isAppContext(context, intent); 23 | startRoute(context, intent); 24 | } 25 | 26 | private void isAppContext(Context context, Intent intent){ 27 | if (context instanceof Application){ 28 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 29 | } 30 | } 31 | 32 | abstract void startRoute(Context context, Intent rawIntent); 33 | } 34 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/router/HttpRouter.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.router; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | 7 | import com.zane.router.message.Message; 8 | 9 | /** 10 | * Created by Zane on 2016/12/23. 11 | * Email: zanebot96@gmail.com 12 | * Blog: zane96.github.io 13 | */ 14 | 15 | public class HttpRouter extends BaseRouter{ 16 | @Override 17 | void startRoute(Context context, Intent rawIntent) { 18 | Message message = rawIntent.getParcelableExtra(ROUTER_MESSAGE); 19 | String url = message.getUrl().getBaseUrl(); 20 | rawIntent.setAction(Intent.ACTION_VIEW); 21 | Uri contentUrl = Uri.parse(url); 22 | rawIntent.setData(contentUrl); 23 | context.startActivity(Intent.createChooser(rawIntent, "选择一款浏览器")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/router/Table.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.router; 2 | 3 | import android.app.Activity; 4 | 5 | /** 6 | * Created by Zane on 2016/12/23. 7 | * Email: zanebot96@gmail.com 8 | * Blog: zane96.github.io 9 | */ 10 | 11 | public interface Table { 12 | Class queryTable(String url); 13 | } 14 | -------------------------------------------------------------------------------- /router/src/main/java/com/zane/router/utils/ZLog.java: -------------------------------------------------------------------------------- 1 | package com.zane.router.utils; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by Zane on 2017/4/16. 7 | * Email: zanebot96@gmail.com 8 | * Blog: zane96.github.io 9 | */ 10 | 11 | public final class ZLog { 12 | private static final String TAG = "easyrouter"; 13 | private static boolean DEBUG = true; 14 | 15 | private ZLog(){} 16 | 17 | public static void setDebug(boolean DEBUG) { 18 | ZLog.DEBUG = DEBUG; 19 | } 20 | 21 | public static void i(String message) { 22 | if (DEBUG) { 23 | Log.i(TAG, message); 24 | } 25 | } 26 | 27 | public static void e(String message) { 28 | Log.e(TAG, message); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /router/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | router 3 | 4 | -------------------------------------------------------------------------------- /router/src/test/java/com/example/zane/router/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.zane.router; 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() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /sample-library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample-library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'me.zane.easyrouter-merge' 3 | //if (isDebug) { 4 | // apply plugin: 'com.android.application' 5 | //} else { 6 | // apply plugin: 'com.android.library' 7 | // apply plugin: 'me.zane.easyrouter-merge' 8 | //} 9 | 10 | android { 11 | compileSdkVersion 26 12 | // buildToolsVersion "26.0.2" 13 | 14 | defaultConfig { 15 | minSdkVersion 21 16 | targetSdkVersion 26 17 | versionCode 1 18 | versionName "1.0" 19 | 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | // defaultConfig { 22 | // // 作为library时不能有applicationId,只有作为一个独立应用时才能够如下设置 23 | // if (isDebug.toBoolean()){ 24 | // applicationId "com.example.zane.easyrouter" 25 | // } 26 | // } 27 | } 28 | 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | 36 | compileOptions { 37 | sourceCompatibility JavaVersion.VERSION_1_8 38 | targetCompatibility JavaVersion.VERSION_1_8 39 | } 40 | 41 | // sourceSets { 42 | // main { 43 | // if (false) { 44 | // manifest.srcFile 'src/main/debug/AndroidManifest.xml' 45 | // } else { 46 | // manifest.srcFile 'src/main/release/AndroidManifest.xml' 47 | // java { 48 | // //release 时 debug 目录下文件不需要合并到主工程 49 | // exclude 'debug/**' 50 | // } 51 | // } 52 | // } 53 | // } 54 | } 55 | 56 | dependencies { 57 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 58 | compile fileTree(include: ['*.jar'], dir: 'libs') 59 | compile 'com.android.support:appcompat-v7:26.1.0' 60 | testCompile 'junit:junit:4.12' 61 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 62 | exclude group: 'com.android.support', module: 'support-annotations' 63 | }) 64 | compile project(':router') 65 | annotationProcessor project(':easyrouter-compiler') 66 | } 67 | -------------------------------------------------------------------------------- /sample-library/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 | -------------------------------------------------------------------------------- /sample-library/src/androidTest/java/me/zane/sample_library/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package me.zane.sample_library; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("me.zane.sample_library.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample-library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample-library/src/main/java/me/zane/sample_library/LibraryActivityOne.java: -------------------------------------------------------------------------------- 1 | package me.zane.sample_library; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.widget.TextView; 7 | 8 | import com.zane.easyrouter_annotation.Param; 9 | import com.zane.easyrouter_annotation.Route; 10 | 11 | /** 12 | * Created by Zane on 2018/4/18. 13 | * Email: zanebot96@gmail.com 14 | */ 15 | 16 | @Route("activity://library_acitivity_one") 17 | public class LibraryActivityOne extends AppCompatActivity{ 18 | @Param("data") 19 | public String data; 20 | 21 | @Override 22 | protected void onCreate(@Nullable Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_library_main); 25 | 26 | TextView mText = findViewById(R.id.text_data); 27 | mText.setText(data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sample-library/src/main/res/layout/activity_library_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /sample-library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | sample-library 3 | 4 | -------------------------------------------------------------------------------- /sample-library/src/test/java/me/zane/sample_library/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.zane.sample_library; 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() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.zane.easyrouter-merge' 3 | 4 | android { 5 | compileSdkVersion 26 6 | buildToolsVersion '26.0.2' 7 | defaultConfig { 8 | applicationId "com.example.zane.easyrouter" 9 | minSdkVersion 21 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(include: ['*.jar'], dir: 'libs') 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:26.1.0' 33 | testCompile 'junit:junit:4.12' 34 | compile project(':router') 35 | annotationProcessor project(':easyrouter-compiler') 36 | compile project(':sample-library') 37 | } 38 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Zane/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/example/zane/easyrouter/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.zane.easyrouter; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.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 | * Instrumentation 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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.zane.easyrouter", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/ActivityThree.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import com.example.zane.easyrouter.R; 10 | import com.zane.easyrouter_annotation.Param; 11 | import com.zane.easyrouter_annotation.Route; 12 | import com.zane.router.EasyRouter; 13 | import com.zane.router.message.MessageBuilder; 14 | 15 | /** 16 | * Created by Zane on 2016/12/25. 17 | * Email: zanebot96@gmail.com 18 | * Blog: zane96.github.io 19 | */ 20 | 21 | @Route("activity://three") 22 | public class ActivityThree extends AppCompatActivity{ 23 | 24 | @Param("data") 25 | public String data; 26 | 27 | @Override 28 | protected void onCreate(@Nullable Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_three); 31 | Toast.makeText(this, data, Toast.LENGTH_SHORT).show(); 32 | 33 | findViewById(R.id.button_return_main).setOnClickListener(new View.OnClickListener() { 34 | @Override 35 | public void onClick(View v) { 36 | EasyRouter.setResult(ActivityThree.this, 0, new MessageBuilder() 37 | .addParam("result_three", "data from three", String.class) 38 | .build()); 39 | ActivityThree.this.finish(); 40 | } 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/ActivityTwo.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.app.FragmentTransaction; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | 12 | import com.example.zane.easyrouter.R; 13 | import com.zane.easyrouter_annotation.Param; 14 | import com.zane.easyrouter_annotation.Result; 15 | import com.zane.easyrouter_annotation.Route; 16 | import com.zane.router.EasyRouter; 17 | import com.zane.router.message.MessageBuilder; 18 | 19 | /** 20 | * Created by Zane on 2016/12/25. 21 | * Email: zanebot96@gmail.com 22 | * Blog: zane96.github.io 23 | */ 24 | 25 | @Route("activity://two") 26 | public class ActivityTwo extends AppCompatActivity{ 27 | 28 | @Param("data") 29 | public String data; 30 | @Param("person") 31 | public Person person; 32 | 33 | @Result("result_three") 34 | public String result; 35 | 36 | @Override 37 | protected void onCreate(@Nullable Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_two); 40 | 41 | Toast.makeText(this, data, Toast.LENGTH_SHORT).show(); 42 | TextView mText = (TextView) findViewById(R.id.text_person); 43 | mText.setText("age: " + person.getAge() + " name: " + person.getName()); 44 | 45 | findViewById(R.id.button_two).setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | EasyRouter.setResult(ActivityTwo.this, 0, new MessageBuilder() 49 | .addParam("result_two", "data from two", String.class) 50 | .build()); 51 | ActivityTwo.this.finish(); 52 | } 53 | }); 54 | 55 | FragmentTransaction transaction = getFragmentManager().beginTransaction(); 56 | FragmentTwo fragmentTwo = new FragmentTwo(); 57 | fragmentTwo.setListener(new FragmentTwo.OnResultListener() { 58 | @Override 59 | public void onResult() { 60 | Toast.makeText(ActivityTwo.this, result, Toast.LENGTH_SHORT).show(); 61 | } 62 | }); 63 | transaction.add(R.id.fragment_activity_two, fragmentTwo).commit(); 64 | } 65 | 66 | @Override 67 | public void onBackPressed() { 68 | Intent intent = new Intent(); 69 | intent.putExtra("result_two", "data from two"); 70 | setResult(0, intent); 71 | finish(); 72 | } 73 | 74 | @Override 75 | protected void onDestroy() { 76 | super.onDestroy(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/App.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.app.Application; 4 | 5 | import com.zane.router.EasyRouter; 6 | import com.zane.router.EasyRouterSet; 7 | import com.zane.router.converter.GsonConventerFactory; 8 | 9 | 10 | /** 11 | * Created by Zane on 2016/12/25. 12 | * Email: zanebot96@gmail.com 13 | * Blog: zane96.github.io 14 | */ 15 | 16 | public class App extends Application{ 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | EasyRouter.init(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/FragmentTwo.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.app.Fragment; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.support.annotation.Nullable; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | 13 | import com.example.zane.easyrouter.R; 14 | import com.zane.router.EasyRouter; 15 | import com.zane.router.message.MessageBuilder; 16 | import com.zane.router.result.OnActivityResultListener; 17 | 18 | /** 19 | * Created by zane on 2017/1/15. 20 | */ 21 | 22 | public class FragmentTwo extends Fragment{ 23 | private OnResultListener listener; 24 | 25 | public interface OnResultListener { 26 | void onResult(); 27 | } 28 | 29 | public void setListener(OnResultListener listener) { 30 | this.listener = listener; 31 | } 32 | 33 | @Nullable 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | return inflater.inflate(R.layout.fragment_two, container, false); 37 | } 38 | 39 | @Override 40 | public void onViewCreated(View view, Bundle savedInstanceState) { 41 | super.onViewCreated(view, savedInstanceState); 42 | view.findViewById(R.id.button_fragment_two).setOnClickListener(new View.OnClickListener() { 43 | @Override 44 | public void onClick(View v) { 45 | EasyRouter.routeForResult(getActivity(), new MessageBuilder().setAddress("activity://three") 46 | .addParam("data", "data from two", String.class) 47 | .build(), new OnActivityResultListener() { 48 | @Override 49 | public void onActivityResult(int resultCode, Intent data) { 50 | listener.onResult(); 51 | } 52 | }); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import com.example.zane.easyrouter.R; 10 | import com.zane.easyrouter_annotation.Result; 11 | import com.zane.easyrouter_annotation.Route; 12 | import com.zane.router.EasyRouter; 13 | import com.zane.router.message.Message; 14 | import com.zane.router.message.MessageBuilder; 15 | import com.zane.router.result.OnActivityResultListener; 16 | 17 | @Route("activity://main") 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | @Result("result_three") 21 | public String resultThree; 22 | @Result("result_two") 23 | public String resultTwo; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | 30 | findViewById(R.id.button_start_library_activity).setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | EasyRouter.route(MainActivity.this, new MessageBuilder() 34 | .setAddress("activity://library_acitivity_one") 35 | .addParam("data", "data from application", String.class) 36 | .build()); 37 | } 38 | }); 39 | 40 | findViewById(R.id.button_start_activitytwo_foresult).setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | Message message = new MessageBuilder() 44 | .setAddress("activity://two") 45 | .addParam("data", "haha", String.class) 46 | .addParam("person", new Person(21, "Zane"), Person.class) 47 | .build(); 48 | EasyRouter.routeForResult(MainActivity.this, message, new OnActivityResultListener() { 49 | @Override 50 | public void onActivityResult(int resultCode, Intent data) { 51 | Toast.makeText(MainActivity.this, resultTwo, Toast.LENGTH_SHORT).show(); 52 | } 53 | }); 54 | } 55 | }); 56 | 57 | findViewById(R.id.button_start_activitythree_foresult).setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View v) { 60 | EasyRouter.routeForResult(MainActivity.this, new MessageBuilder() 61 | .setAddress("activity://three") 62 | .build(), new OnActivityResultListener() { 63 | @Override 64 | public void onActivityResult(int resultCode, Intent data) { 65 | Toast.makeText(MainActivity.this, resultThree, Toast.LENGTH_SHORT).show(); 66 | } 67 | }); 68 | } 69 | }); 70 | 71 | 72 | 73 | findViewById(R.id.button_start_activitytwo).setOnClickListener(new View.OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | Message message = new MessageBuilder() 77 | .setAddress("activity://two") 78 | .addParam("data", "haha", String.class) 79 | .addParam("person", new Person(21, "Zane"), Person.class) 80 | .build(); 81 | EasyRouter.route(MainActivity.this, message); 82 | } 83 | }); 84 | 85 | findViewById(R.id.button_browser).setOnClickListener(new View.OnClickListener() { 86 | @Override 87 | public void onClick(View v) { 88 | EasyRouter.route(MainActivity.this, new MessageBuilder() 89 | .setAddress("http://www.baidu.com") 90 | .build()); 91 | } 92 | }); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sample/src/main/java/com/zane/easyrouter/Person.java: -------------------------------------------------------------------------------- 1 | package com.zane.easyrouter; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by Zane on 2017/4/17. 8 | * Email: zanebot96@gmail.com 9 | * Blog: zane96.github.io 10 | */ 11 | 12 | public class Person implements Parcelable { 13 | private int age; 14 | private String name; 15 | 16 | public Person(int age, String name) { 17 | this.age = age; 18 | this.name = name; 19 | } 20 | 21 | public int getAge() { 22 | return age; 23 | } 24 | 25 | public void setAge(int age) { 26 | this.age = age; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | @Override 38 | public int describeContents() { 39 | return 0; 40 | } 41 | 42 | @Override 43 | public void writeToParcel(Parcel dest, int flags) { 44 | dest.writeInt(this.age); 45 | dest.writeString(this.name); 46 | } 47 | 48 | public Person() { 49 | } 50 | 51 | protected Person(Parcel in) { 52 | this.age = in.readInt(); 53 | this.name = in.readString(); 54 | } 55 | 56 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 57 | @Override 58 | public Person createFromParcel(Parcel source) { 59 | return new Person(source); 60 | } 61 | 62 | @Override 63 | public Person[] newArray(int size) { 64 | return new Person[size]; 65 | } 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |