├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── router-annotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── github │ └── beansoftapp │ └── android │ └── router │ └── annotation │ ├── Action.java │ ├── Mapping.java │ └── Router.java ├── router-compiler ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── github │ └── beansoftapp │ └── android │ └── router │ └── compiler │ └── RouterProcessor.java ├── router-demo-app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── beansoftapp │ │ └── android │ │ └── router │ │ └── demo │ │ └── app │ │ ├── ActivityWithResult.java │ │ ├── DemoLoginInterceptor.java │ │ ├── LoginActivity.java │ │ ├── MainActivity.java │ │ ├── TestActivity.java │ │ └── action │ │ ├── IMLogoutAction.java │ │ └── TestAction.java │ └── res │ ├── layout │ ├── activity_login.xml │ ├── activity_main.xml │ ├── activity_test.xml │ └── activity_with_result.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 ├── router-demo-module1 ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── beansoftapp │ │ └── android │ │ └── router │ │ └── demo │ │ └── module1 │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_module1_main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ └── strings.xml ├── router-demo-module2 ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── beansoftapp │ │ └── android │ │ └── router │ │ └── demo │ │ └── module2 │ │ ├── MainActivity.java │ │ └── TestAction.java │ └── res │ ├── layout │ └── activity_module2_main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ └── strings.xml ├── router-library ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── beansoftapp │ │ └── android │ │ └── router │ │ ├── HMapping.java │ │ ├── HPath.java │ │ ├── HRouter.java │ │ ├── action │ │ ├── HAbstractAction.java │ │ ├── HAbstractCallback.java │ │ ├── HAction.java │ │ ├── HActionExecutor.java │ │ ├── HActionMapping.java │ │ └── HCallback.java │ │ ├── interceptor │ │ ├── AbstractInterceptor.java │ │ ├── DefaultLoginInterceptor.java │ │ ├── Interceptor.java │ │ ├── Invoker.java │ │ └── JumpInvoker.java │ │ └── util │ │ ├── BuildConfig.java │ │ ├── BundleUtil.java │ │ ├── ContextCast.java │ │ ├── DeviceHelper.java │ │ ├── LogUtil.java │ │ └── MapQuery.java │ └── res │ └── values │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /.idea/ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BAndroidRouter 2 | BAndroidRouter is an multi module enabled router library and in-app data transfer framework. 多模块路由总线和数据交换框架. 3 | 4 | 作者: 刘长炯 BeanSoft@126.com (微信号 weblogic ). 5 | 6 | 简介: 把项目拆成一堆小library, 互相之间不用知道Activity名字也能跳转, 也能进行数据交换. 目前此项目已用于河狸家安卓App的开发. 7 | 8 | 9 | 本项目从Gradle插件2.2版本起(需要Java8编译器)不再依赖已经停止更新的第三方android-apt(https://bitbucket.org/hvisser/android-apt)插件, 而采用Google官方的Gradle插件, 简化了代码结构. 如果您需要用Java7打包, 请使用参考分支 apt-version. 10 | 11 | ## 路由框架的意义 12 | 13 | 1. 在一些复杂的业务场景下(比如电商),灵活性比较强,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,但如果事先做了约定,提前做好页面映射,便可以自由配置跳转。 14 | 15 | 2. 随着业务量的增长,客户端必然随之膨胀,开发人员的工作量越来越大,比如64K问题,比如协作开发问题。App一般都会走向组件化、插件化的道路,而组件化、插件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系。 16 | 17 | 3. 模块化拆分之后面临的另一个问题就是数据交换解耦, 在不同的模块之间解耦之前通过startActivityForResult来实现的数据传输, 以及不同模块间的网络调用互通. 这些都可以通过路由框架解决. 18 | 19 | ## 核心用法浓缩 20 | 21 | ```java 22 | @Router("client/module1/test") 23 | 
// 配置映射路径
 24 | public class MainActivity extends Activity { 25 | // ... 26 | } 27 | 28 | // 使用映射路径跳转 29 | HRouter.open(context, "app://client/module1/test?a=b&name=张三"); 30 | 31 | // 使用映射路径发起 startActivityForResult, 参数为requestCode !!!New 32 | HRouter.startActivityForResult(context, "app://client/module1/test?a=b&name=张三", 1); 33 | 34 | // 开发和配置动作映射 35 | @Action("action/test") 36 | public class TestAction extends HAbstractAction { 37 | // 同步模式, 无参数 38 | public String action() { 39 | return "TestAction同步调用无参数"; 40 | } 41 | 42 | // 异步模式+回调+参数 43 | public void action(Object param, HCallback callback) { 44 | callback.start(); 45 | callback.ok(action(param), null); 46 | callback.complete(); 47 | } 48 | } 49 | 50 | // 使用HRouter来调用动作 51 | Object value = HRouter.action("haction://action/test?a=b"); 52 | Toast.makeText(this, "Action带参数执行结果:" + value, Toast.LENGTH_SHORT).show(); 53 | 54 | HRouter.action("haction://action/test?a=b", new HAbstractCallback() { 55 | @Override 56 | public void start() { 57 | Toast.makeText(MainActivity.this, "Action执行开始", Toast.LENGTH_SHORT).show(); 58 | } 59 | 60 | @Override 61 | public void complete() { 62 | Toast.makeText(MainActivity.this, "Action执行结束", Toast.LENGTH_SHORT).show(); 63 | } 64 | 65 | @Override 66 | public void ok(String o, Object response) { 67 | System.out.println("异步调用结束!"); 68 | } 69 | }); 70 | ``` 71 | # New! 自动载入映射列表 72 | 多aar动态增减成为可能!! 73 | 每个子module项目中的gradle配置增加新参数: 74 | ```gradle 75 | assetsDir : "$projectDir/src/main/assets".toString() // 可选, 配置时会自动生成assets/modules 76 | ``` 77 | 78 | ```java 79 | HRouter.setupFromAssets(this);// 自动载入所有AAR映射关系 80 | ``` 81 | 82 | ## 代码结构 83 | router-demo-app 主演示入口App
84 | router-demo-module1 主演示模块, 仅依赖模块router-compiler和 router-library
85 | router-annotation @Router 路由Java注解(Java库)
86 | router-compiler APT插件库(Java库),用于根据注解自动生成单个模块下所有Activity的映射关系类
87 | router-library Android库, 实现了Activity的路由逻辑 88 | ## 如何增加和使用路由关系 89 | ### 子组件(模块)的开发和设置 90 | 模块必须设置module名称: 91 | 参考build.gradle 92 | 93 | ```gradle 94 | android {
 defaultConfig {
 javaCompileOptions {
 annotationProcessorOptions {
 arguments = [ targetModuleName : 'Base', // 模块名, 用于加载和生成映射关系
 assetsDir : "$projectDir/src/main/assets".toString() // 可选, 配置时会自动生成assets/modules
 ]
 }
 }
 } 95 | ``` 96 | 97 | 在需要支持跳转的Activity类名上加注解: 98 | 99 | ```java 100 | @Router("client/module1/test")
public class MainActivity extends Activity { 101 | //... 102 | } 103 | ``` 104 | 105 | 如果需要登陆后才能跳转到指定UI, 需要三个步骤: 106 | 1) 加入login属性: 107 | ```java 108 | @Router(value = {"client/module1/test"}, login = true 109 | ) 110 | ``` 111 | 2) 需要开发一个登陆状态检测拦截器, 具体请参考代码:DemoLoginInterceptor 112 | 3) 将拦截器和Router相关联. 113 | ```java 114 | HRouter.setLoginInterceptor(new DemoLoginInterceptor(getApplicationContext())); 115 | ``` 116 | 117 | ### 主App的设置 118 | ####初始化Router 119 | 可以设置跳转支持的Schema, 以及需要加载的模块中的跳转规则类(用APT插件自动生成), 如下面代码中 Other 即为新增模块中的 targetModuleName, 120 | ``` 121 | HRouter.setScheme("app");// 设置跳转的schema 122 | HRouter.setup("Base", "Other");// 载入映射关系 123 | ``` 124 | #### 发起路由跳转 125 | 跳转到模块内和模块外的Activity的方式都是统一的, 如下所示: 126 | 127 | ```java 128 | String path = "app://client/module1/test?a=b&name=张三"; 129 | if(!HRouter.open(this, path)) { 130 | Toast.makeText(this, "没有跳转成功, 请检查跳转路径 " + path, Toast.LENGTH_SHORT).show(); 131 | } else { 132 | Toast.makeText(this, "成功跳转到 " + HRouter.getActivityName(path).getCanonicalName(), Toast.LENGTH_SHORT).show(); 133 | } 134 | ``` 135 | a=b&name=张三 这样的字符串会转换为Bundle信息. 136 | 137 | ### 组件化 138 | 由于跳转关系不需要关注具体的类, 所以主App可以只是一个壳工程, 依赖一堆子模块aar, 甚至每个子模块都可以有自己的版本库, 即可happy的跳转了. 139 | 140 | ###跨模块的数据传递 141 | 一个Action可以实现HAction接口, 并加入@Action注解后, 就可以通过路径的方式进行跨模块的调用. 142 | ####同步模式 143 | 144 | 145 | ### 产生映射和Action的列表文档 146 | build之后, 会自动产生一个doc目录, 下面分模块会生成不同的映射文件列表, 147 | 便于研发人员查看, 在Android Stuido中点击Rebuild会更新生成. 148 | 149 | ## 待完成功能(TODO) 150 | ### 单元测试覆盖 151 | ### 多拦截器的支持 152 | ### 降级方案, Native跳转失败时使用Http代替 153 | ### WebView互操作和外部浏览器的支持 154 | ### 多个参数值的支持, MultiValueMap的调研 155 | 目前尚在开发之中 156 | 157 | ## 模块化常见问题 158 | Q: 如何去掉烦人的new Intent(getActivity(), XXXXActivity.class) 的Activity类名强耦合关系? 159 | 160 | A: 修改为 Intent intent = new Intent(getActivity(), HRouter.getActivityName("app://xxxxpath")); 这样即使不用 161 | EventBus, 也能轻松解耦了. 162 | 163 | Q: 如何去掉烦人的 startActivityForResult 的Activity类名强耦合关系? 164 | 165 | A: 修改为 HRouter.startActivityForResult(this, "app://xxxxpath"), TEST_REQUEST_CODE); 166 | 167 | Q: startActivityForResult 支持Fragment吗? 168 | 169 | A: 最新版已经支持传入Fragment进行跳转了. 170 | 171 | Q: 如何启动一个Service类? 172 | 173 | A: 创建一个HAction并增加Action路径, 然后在Action中启动Service, 174 | 最后通过 HRouter.action("haction://action/startXXService") 即可完成. 175 | 176 | Q: 参数如何传递呢? 177 | 178 | A: 通过url中增加get参数, 这些参数会自动转成两种格式: 179 | 1. 对于跳转协议, 会封装为Bundle参数(目前只支持字符串格式); 180 | 2. 对于Action执行, 会封装为Map参数传递并执行. 181 | 182 | Q: 复杂参数怎么处理? 除了String 还有别的复杂对象. 183 | A: 这个需要分情况处理: 184 | 1. 对于跳转协议, 可使用 Router.open(Context context, String url, Bundle bundle), 第三个参数可自行定义Bundle数据; 185 | 2. 对于Action执行, 可使用 Router.action(String str, Object param) 方法, 第二个参数是自定义的任意对象. 186 | 187 | 188 | ## Changelog 189 | 2016-04-27 允许传入Fragment和普通Context来执行跳转逻辑和startActivityForResult; 增加调试日志开关; 190 | 2016-12-27 增加自动载入映射列表的功能 191 | 2017-01-04 增加startActivityForResult 192 | 的功能支持, 去除android-apt第三方插件支持, 简化文档和使用代码 193 | 2017-02-06 修复 HRouter.open 带自定义bundle丢失的BUG 194 | 2017-04-23 允许传入Fragment和普通Context来执行跳转逻辑; 195 | bug修复: 196 | startActivityForResult在Fragment中失效 197 | HAction子类中方法反射调用参数错误导致必须声明一个空方法 198 | 199 | 200 | #License 201 | Copyright 2016 BeanSoft@126.com. 202 | 203 | Licensed under the Apache License, Version 2.0 (the "License"); 204 | you may not use this file except in compliance with the License. 205 | You may obtain a copy of the License at 206 | 207 | http://www.apache.org/licenses/LICENSE-2.0 208 | 209 | Unless required by applicable law or agreed to in writing, software 210 | distributed under the License is distributed on an "AS IS" BASIS, 211 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 212 | See the License for the specific language governing permissions and 213 | limitations under the License. 214 | 215 | -------------------------------------------------------------------------------- /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 | mavenLocal() 6 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } 7 | maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'} 8 | maven { 9 | url 'https://maven.google.com/' 10 | name 'Google' 11 | } 12 | } 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:4.1.0' 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | mavenLocal() 23 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } 24 | maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'} 25 | jcenter() 26 | maven { url('http://maven.aliyun.com/nexus/content/groups/public/') } 27 | //jcenter() 使用国内镜像加速jcenter 28 | maven { url('http://maven.aliyun.com/nexus/content/repositories/gradle-plugin/') } 29 | maven { url('https://oss.sonatype.org/content/repositories/releases/') } 30 | maven { url('https://maven.aliyun.com/repository/central/') } 31 | maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' } 32 | maven { url "https://jitpack.io" } 33 | maven { url 'http://developer.huawei.com/repo' } 34 | maven { 35 | url 'https://maven.google.com/' 36 | name 'Google' 37 | } 38 | } 39 | } 40 | 41 | //rootProject.buildDir = './build' 42 | 43 | subprojects { 44 | project.buildDir = "${rootProject.buildDir}/${project.name}" 45 | } 46 | 47 | task clean(type: Delete) { 48 | delete rootProject.buildDir 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beansoft/BAndroidRouter/fe2c73b2c989502ba29b30b419cb8bcc69de089d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 04 07:49:22 CST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-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 | -------------------------------------------------------------------------------- /router-annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build/ 3 | -------------------------------------------------------------------------------- /router-annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | sourceCompatibility = "1.7" 8 | targetCompatibility = "1.7" 9 | -------------------------------------------------------------------------------- /router-annotation/src/main/java/com/github/beansoftapp/android/router/annotation/Action.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.TYPE}) 9 | @Retention(RetentionPolicy.SOURCE) 10 | 11 | /** 12 | * @Action(value = {"app/show_update"}) 13 | public class ShowUpdateAction extends HAbstractAction { 14 | public Object action() { 15 | return null; 16 | } 17 | } 18 | */ 19 | // 此类暂时未用 20 | public @interface Action { 21 | String[] value(); 22 | } -------------------------------------------------------------------------------- /router-annotation/src/main/java/com/github/beansoftapp/android/router/annotation/Mapping.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.TYPE}) 9 | @Retention(RetentionPolicy.SOURCE) 10 | // 此类暂时未用 11 | public @interface Mapping { 12 | String name() default "HRouterMapping"; 13 | } 14 | -------------------------------------------------------------------------------- /router-annotation/src/main/java/com/github/beansoftapp/android/router/annotation/Router.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.TYPE}) 9 | @Retention(RetentionPolicy.SOURCE) 10 | /** 11 | * @Router(value = {"path1", "path2", "a/b/c"}) 12 | * 或 @Router("path1") 13 | * 最终生成的代码: 14 | * HRouterMapping模块名 15 | * 16 | public class HRouterMappingBase { 17 | public static final void map() { 18 | com.github.beansoftapp.android.router.HRouter.map("client/aa2", TestActivity.class, false, "1.0", true, "", ""); 19 | com.github.beansoftapp.android.router.HRouter.map("client/bb", TestActivity.class, false, "1.0", true, "", ""); 20 | com.github.beansoftapp.android.router.HRouter.map("client/my/test", TestActivity.class, false, "1.0", true, "", ""); 21 | } 22 | } 23 | * 24 | * 25 | */ 26 | public @interface Router { 27 | boolean isPublic() default true; 28 | 29 | boolean login() default false;// 是否需要登录验证 30 | 31 | String[] value(); 32 | 33 | String version() default "1.0.0"; 34 | } 35 | -------------------------------------------------------------------------------- /router-compiler/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build/ 3 | -------------------------------------------------------------------------------- /router-compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | //apply plugin: 'com.github.dcendents.android-maven' 3 | sourceCompatibility = 1.7 4 | targetCompatibility = 1.7 5 | 6 | dependencies { 7 | compile fileTree(include: ['*.jar'], dir: 'libs') 8 | compile 'com.google.auto.service:auto-service:1.0-rc2' 9 | compile 'com.squareup:javapoet:1.7.0' 10 | compile project(':router-annotation') 11 | } -------------------------------------------------------------------------------- /router-compiler/src/main/java/com/github/beansoftapp/android/router/compiler/RouterProcessor.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.compiler; 2 | 3 | import com.github.beansoftapp.android.router.annotation.Action; 4 | import com.github.beansoftapp.android.router.annotation.Router; 5 | import com.google.auto.service.AutoService; 6 | import com.squareup.javapoet.JavaFile; 7 | import com.squareup.javapoet.MethodSpec; 8 | import com.squareup.javapoet.TypeSpec; 9 | 10 | import java.io.File; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | import javax.annotation.processing.AbstractProcessor; 17 | import javax.annotation.processing.Messager; 18 | import javax.annotation.processing.ProcessingEnvironment; 19 | import javax.annotation.processing.Processor; 20 | import javax.annotation.processing.RoundEnvironment; 21 | import javax.annotation.processing.SupportedAnnotationTypes; 22 | import javax.annotation.processing.SupportedOptions; 23 | import javax.annotation.processing.SupportedSourceVersion; 24 | import javax.lang.model.SourceVersion; 25 | import javax.lang.model.element.Element; 26 | import javax.lang.model.element.Modifier; 27 | import javax.lang.model.element.TypeElement; 28 | import javax.lang.model.util.Elements; 29 | import javax.tools.Diagnostic; 30 | 31 | @AutoService(Processor.class) 32 | @SupportedAnnotationTypes({"com.github.beansoftapp.android.router.annotation.Router", 33 | "com.github.beansoftapp.android.router.annotation.Action"}) 34 | @SupportedOptions({"targetModuleName", "assetsDir"}) 35 | @SupportedSourceVersion(SourceVersion.RELEASE_7) 36 | public class RouterProcessor extends AbstractProcessor { 37 | private String targetModuleName = "";// 目标模块名 38 | private String assetsDir = "";// assets目录 39 | 40 | private StringBuilder routerMappings = new StringBuilder(); 41 | private StringBuilder actionMappings = new StringBuilder(); 42 | 43 | // 此方法的代码可以用Annotation @SupportedAnnotationTypes 来简化 44 | // @Override 45 | // public Set getSupportedAnnotationTypes() { 46 | // HashSet annotationTypes = new HashSet(); 47 | // annotationTypes.add(Router.class.getCanonicalName()); 48 | // annotationTypes.add(Action.class.getCanonicalName()); 49 | // return annotationTypes; 50 | // } 51 | 52 | public RouterProcessor() { 53 | System.out.println(" ****** RouterProcessor 创建 ");// 只会构造一次. 54 | } 55 | 56 | @Override 57 | /** 58 | * Processes a set of annotation types on type elements 59 | * originating from the prior round and returns whether or not 60 | * these annotation types are claimed by this processor. If {@code 61 | * true} is returned, the annotation types are claimed and subsequent 62 | * processors will not be asked to process them; if {@code false} 63 | * is returned, the annotation types are unclaimed and subsequent 64 | * processors may be asked to process them. A processor may 65 | * always return the same boolean value or may vary the result 66 | * based on chosen criteria. 67 | * 68 | *

The input set will be empty if the processor supports {@code 69 | * "*"} and the root elements have no annotations. A {@code 70 | * Processor} must gracefully handle an empty set of annotations. 71 | * 72 | * @param annotations the annotation types requested to be processed 73 | * @param roundEnv environment for information about the current and prior round 74 | * @return whether or not the set of annotation types are claimed by this processor 75 | */ 76 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 77 | System.out.println(" ****** 处理 handle RouterProcessor 数量: "+annotations.size()); 78 | System.out.println(" ****** 当前路径: " + new File(".").getAbsolutePath()); 79 | // 每个模块都会处理结束一次 80 | System.out.println(" ****** RouterProcessor 处理结束 " + roundEnv.processingOver()); 81 | 82 | if(targetModuleName.length() == 0) { 83 | throw new RuntimeException("targetModuleName为空"); 84 | } 85 | 86 | if(roundEnv.processingOver()) { 87 | System.out.println(" ****** actionMappings: " + actionMappings); 88 | System.out.println(" ****** routerMappings: " + routerMappings); 89 | 90 | // 自动创建文件 assets/modules/$targetModuleName 91 | if(assetsDir.length() > 0) { 92 | String modulesPath = assetsDir + "/modules"; 93 | if(!new File(modulesPath).exists()) { 94 | new File(modulesPath).mkdirs(); 95 | } 96 | try { 97 | new File(modulesPath + "/" + targetModuleName).createNewFile(); 98 | } catch (IOException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | 104 | if(!new File("./doc").exists()) { 105 | new File("./doc").mkdir(); 106 | } 107 | 108 | if(actionMappings.length() > 0) { 109 | try { 110 | FileWriter fileWriter = new FileWriter("./doc/" + targetModuleName + "_actions.txt"); 111 | fileWriter.write(actionMappings.toString()); 112 | fileWriter.close(); 113 | } catch (IOException e) { 114 | e.printStackTrace(); 115 | } 116 | } else { 117 | new File("./doc/" + targetModuleName + "_actions.txt").delete(); 118 | } 119 | 120 | if(routerMappings.length() > 0) { 121 | try { 122 | FileWriter fileWriter = new FileWriter("./doc/" + targetModuleName + "_routers.txt"); 123 | fileWriter.write(routerMappings.toString()); 124 | fileWriter.close(); 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } 128 | } else { 129 | new File("./doc/" + targetModuleName + "_routers.txt").delete(); 130 | } 131 | } 132 | 133 | 134 | if (annotations.size() == 0) { 135 | return false; 136 | } 137 | 138 | // 调试, 打印处理中的类, 可以在Gradle Console中看到如下消息: 139 | // 注: 处理类: com.github.beansoftapp.android.router.demo.app.TestActivity 140 | // 注: 处理类: com.github.beansoftapp.android.router.demo.app.action.TestAction 141 | 142 | Messager messager = processingEnv.getMessager(); 143 | for (TypeElement te : annotations) { 144 | for (Element e : roundEnv.getElementsAnnotatedWith(te)) { 145 | messager.printMessage(Diagnostic.Kind.NOTE, "处理类: " + e.toString()); 146 | } 147 | } 148 | 149 | Set elements = roundEnv.getElementsAnnotatedWith(Router.class); 150 | TypeSpec.Builder typeSpec = TypeSpec.classBuilder("HRouterMapping" + (targetModuleName.length() == 0 ? "Base" : targetModuleName)) 151 | .addModifiers(Modifier.PUBLIC); 152 | 153 | 154 | MethodSpec.Builder mapMethodBuild = MethodSpec.methodBuilder("map") 155 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 156 | .returns(void.class); 157 | 158 | 159 | for (Element element : elements) { 160 | Router routerActivity = element.getAnnotation(Router.class); 161 | TypeElement typeElement = (TypeElement) element; 162 | 163 | // TODO 检查路由路径重复问题, 编译时报错 164 | if(routerActivity != null) { 165 | String[] values = routerActivity.value(); 166 | if(values != null) { 167 | routerMappings.append(typeElement.asType()).append(" <--- "); 168 | for(String value : values) { 169 | mapMethodBuild.addCode("com.github.beansoftapp.android.router.HRouter.map($S, $T.class, $L, $S, $L, \"\", \"\");\n", 170 | value, typeElement.asType(), routerActivity.login(), "1.0", routerActivity.isPublic() ); 171 | routerMappings.append(value).append("\t\t"); 172 | } 173 | routerMappings.append("\n"); 174 | } 175 | } 176 | } 177 | 178 | typeSpec.addMethod(mapMethodBuild.build()); 179 | 180 | MethodSpec.Builder mapActionMethodBuild = MethodSpec.methodBuilder("mapAction") 181 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 182 | .returns(void.class); 183 | 184 | 185 | Set elementsAction = roundEnv.getElementsAnnotatedWith(Action.class); 186 | 187 | System.out.println("handle Action elements " + elementsAction.size()); 188 | 189 | for (Element element : elementsAction) { 190 | Action actionClass = element.getAnnotation(Action.class); 191 | TypeElement typeElement = (TypeElement) element; 192 | 193 | // TODO 检查路由路径重复问题, 编译时报错 194 | if(actionClass != null) { 195 | String[] values = actionClass.value(); 196 | 197 | if (values != null) { 198 | actionMappings.append(typeElement.asType()).append(" <--- "); 199 | for (String value : values) { 200 | mapActionMethodBuild.addCode("com.github.beansoftapp.android.router.HRouter.mapAction($S, $T.class);\n", 201 | value, typeElement.asType()); 202 | actionMappings.append(value).append("\t\t");; 203 | } 204 | actionMappings.append("\n"); 205 | } 206 | } 207 | } 208 | 209 | typeSpec.addMethod(mapActionMethodBuild.build()); 210 | 211 | JavaFile javaFile = JavaFile.builder("com.github.beansoftapp.android.router", typeSpec.build()).build(); 212 | try { 213 | javaFile.writeTo(processingEnv.getFiler()); 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } 217 | 218 | return false; 219 | } 220 | 221 | 222 | 223 | @Override 224 | public synchronized void init(ProcessingEnvironment processingEnv) { 225 | super.init(processingEnv); 226 | Elements elementUtils = processingEnv.getElementUtils(); 227 | Map map = processingEnv.getOptions(); 228 | Set keys = map.keySet(); 229 | for (String key : keys) { 230 | if ("targetModuleName".equals(key)) { 231 | this.targetModuleName = map.get(key); 232 | } else if("assetsDir".equals(key)) { 233 | this.assetsDir = map.get(key); 234 | } 235 | System.out.println(key + " = 模块 " + map.get(key)); 236 | } 237 | } 238 | 239 | } -------------------------------------------------------------------------------- /router-demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /build/ 3 | -------------------------------------------------------------------------------- /router-demo-app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion '25.0.0' 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | javaCompileOptions { 14 | annotationProcessorOptions { 15 | arguments = [ targetModuleName : 'Base', // 模块名, 用于加载和生成映射关系 16 | assetsDir : "$projectDir/src/main/assets".toString() // 可选, 配置时会自动生成assets/modules 17 | ] 18 | } 19 | } 20 | 21 | } 22 | 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | 28 | repositories { 29 | mavenCentral() 30 | jcenter() 31 | maven { url "https://jitpack.io" } 32 | flatDir { dirs '../libs' } 33 | } 34 | 35 | // compile (name: 'router-demo-module1-debug', ext: 'aar')// 此代码从主libs下面获取module编译生成的aar 36 | compile project(':router-demo-module1') 37 | compile project(':router-demo-module2') 38 | compile project(':router-library') 39 | 40 | provided project(':router-annotation')// 最终项目中不出现Annotation, 增加安全性 41 | // apt project(':router-compiler') // 项目式依赖 42 | annotationProcessor project(':router-compiler') 43 | // 以下方式为导入时的文件式依赖 44 | // annotationProcessor files('../libs/router-compiler.jar') 45 | // provided files('../libs/router-annotation.jar') 46 | // compile files('../libs/router-library.jar') 47 | // provided 'com.google.auto.service:auto-service:1.0-rc2' 48 | // provided 'com.squareup:javapoet:1.7.0' 49 | } -------------------------------------------------------------------------------- /router-demo-app/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/beansoft/Documents/Java/android-sdk-macosx/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-demo-app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/ActivityWithResult.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | 8 | import com.github.beansoftapp.android.router.annotation.Router; 9 | 10 | @Router("client/my/testReturn") 11 | public class ActivityWithResult extends Activity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_with_result); 16 | } 17 | 18 | public void backData(View view) { 19 | onResultOkClick(); 20 | this.finish(); 21 | } 22 | 23 | private void onResultOkClick() { 24 | Intent data = new Intent(); 25 | data.putExtra("data", "测试startActivityForResult"); 26 | setResult(RESULT_OK, data); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/DemoLoginInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app; 2 | 3 | import android.content.Context; 4 | 5 | import com.github.beansoftapp.android.router.HRouter; 6 | import com.github.beansoftapp.android.router.interceptor.AbstractInterceptor; 7 | 8 | /** 9 | * 判断用户是否已登陆, 需要App开发者自己定制. 10 | */ 11 | public class DemoLoginInterceptor extends AbstractInterceptor { 12 | private static final String TARGET_LOGIN = "app://user/login"; 13 | private boolean isLogined; 14 | 15 | public DemoLoginInterceptor(Context context) { 16 | super(context); 17 | } 18 | 19 | public boolean login() { 20 | this.isLogined = isLoginned(realContext); 21 | if (this.needLogin) { 22 | return this.isLogined; 23 | } 24 | return true; 25 | } 26 | 27 | public Class getBridgeClass() { 28 | return HRouter.getActivityName(TARGET_LOGIN); 29 | } 30 | 31 | public static boolean isLoginned(Context context) { 32 | return true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import com.github.beansoftapp.android.router.annotation.Router; 7 | 8 | @Router("user/login") 9 | public class LoginActivity extends Activity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_login); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import com.github.beansoftapp.android.router.HRouter; 10 | import com.github.beansoftapp.android.router.action.HAbstractCallback; 11 | 12 | 13 | public class MainActivity extends Activity { 14 | private static final int TEST_REQUEST_CODE = 1; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_main); 20 | HRouter.setEnableDebugLog(false); 21 | HRouter.setScheme("app");// 设置跳转的schema 22 | HRouter.setupFromAssets(this);// 自动根据assets目录载入 23 | // HRouter.setup("Base", "Other"); 手工设置要载入的模块列表 24 | HRouter.setLoginInterceptor(new DemoLoginInterceptor(getApplicationContext())); 25 | 26 | HRouter.action("haction://IMLogoutAction"); 27 | 28 | Object value = HRouter.action("haction://action/test"); 29 | Toast.makeText(this, "Action执行结果:" + value, Toast.LENGTH_SHORT).show(); 30 | 31 | value = HRouter.action("haction://action/test", "参数直接传递,例如JSON"); 32 | Toast.makeText(this, "Action执行结果:" + value, Toast.LENGTH_SHORT).show(); 33 | 34 | value = HRouter.action("haction://action/test?a=b"); 35 | Toast.makeText(this, "Action带参数执行结果:" + value, Toast.LENGTH_SHORT).show(); 36 | 37 | HRouter.action("haction://action/test?a=b", new HAbstractCallback() { 38 | @Override 39 | public void start() { 40 | Toast.makeText(MainActivity.this, "Action执行开始", Toast.LENGTH_SHORT).show(); 41 | } 42 | 43 | @Override 44 | public void complete() { 45 | Toast.makeText(MainActivity.this, "Action执行结束", Toast.LENGTH_SHORT).show(); 46 | } 47 | 48 | @Override 49 | public void ok(String o, Object response) { 50 | System.out.println("异步调用结束!"); 51 | } 52 | }); 53 | } 54 | 55 | public void testRouterApp(View view) { 56 | String path = "app://client/my/test?a=b&name=%E5%88%98Sir"; 57 | if (!HRouter.open(this, path)) { 58 | Toast.makeText(this, "没有跳转成功, 请检查跳转路径 " + path, Toast.LENGTH_SHORT).show(); 59 | } else { 60 | Toast.makeText(this, "成功跳转到 " + HRouter.getActivityName(path).getCanonicalName(), Toast.LENGTH_SHORT).show(); 61 | } 62 | } 63 | 64 | // 发起 startActivityForResult, 参考类: ActivityWithResult.java 65 | public void testStartActivityForResult(View view) { 66 | String path = "app://client/my/testReturn"; 67 | if (!HRouter.startActivityForResult(this, path, TEST_REQUEST_CODE)) { 68 | Toast.makeText(this, "没有跳转成功, 请检查跳转路径 " + path, Toast.LENGTH_SHORT).show(); 69 | } else { 70 | Toast.makeText(this, "成功跳转到 " + HRouter.getActivityName(path).getCanonicalName(), Toast.LENGTH_SHORT).show(); 71 | } 72 | } 73 | 74 | // 跳转到模块1 75 | public void testRouterModule1(View view) { 76 | String path = "app://client/module1/test?a=b&name=张三"; 77 | Bundle bundle = new Bundle(); 78 | bundle.putInt("test_bundle_int", 1234); 79 | if (!HRouter.open(this, path, bundle)) { 80 | Toast.makeText(this, "没有跳转成功, 请检查跳转路径 " + path, Toast.LENGTH_SHORT).show(); 81 | } else { 82 | Toast.makeText(this, "成功跳转到 " + HRouter.getActivityName(path).getCanonicalName(), Toast.LENGTH_SHORT).show(); 83 | } 84 | } 85 | 86 | @Override 87 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 88 | super.onActivityResult(requestCode, resultCode, data); 89 | System.out.println("数据回传:" + requestCode); 90 | if(requestCode == TEST_REQUEST_CODE && resultCode == Activity.RESULT_OK && null != data){ 91 | Toast.makeText(this, "数据回传成功: " + data.getStringExtra("data"), Toast.LENGTH_SHORT).show(); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.widget.TextView; 6 | 7 | import com.github.beansoftapp.android.router.annotation.Router; 8 | import com.github.beansoftapp.android.router.util.BundleUtil; 9 | 10 | @Router(value = {"client/a", "client/b", "client/my/test"}, login = true) 11 | public class TestActivity extends Activity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_test); 17 | 18 | Bundle bundle = getIntent().getExtras(); 19 | 20 | ((TextView)findViewById(R.id.bundleContent)).setText(this.getClass() + 21 | "\n" + BundleUtil.bundle2string(bundle)); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/action/IMLogoutAction.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app.action; 2 | 3 | import com.github.beansoftapp.android.router.action.HAbstractAction; 4 | import com.github.beansoftapp.android.router.annotation.Action; 5 | 6 | /** 7 | * IM 登出动作. 8 | * Created by beansoft on 16/12/22. 9 | */ 10 | @Action("IMLogoutAction") 11 | public class IMLogoutAction extends HAbstractAction { 12 | public Void action() { 13 | System.out.println( "IMLogoutAction" ); 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /router-demo-app/src/main/java/com/github/beansoftapp/android/router/demo/app/action/TestAction.java: -------------------------------------------------------------------------------- 1 | package com.github.beansoftapp.android.router.demo.app.action; 2 | 3 | import com.github.beansoftapp.android.router.action.HAbstractAction; 4 | import com.github.beansoftapp.android.router.action.HCallback; 5 | import com.github.beansoftapp.android.router.annotation.Action; 6 | 7 | /** 8 | * 测试的动作. 9 | * 本类也可以直接实现 HAction 接口. 10 | * Created by beansoft on 16/12/20. 11 | */ 12 | @Action({"action/test", "action/test2"}) 13 | public class TestAction extends HAbstractAction { 14 | // 同步模式 15 | public String action() {// 无参数的调用应该只考虑这一个 16 | return "TestAction同步调用无参数, router_target=" + router_target; 17 | } 18 | 19 | // 同步模式+参数,参数可直接传递 20 | public String action(Object param) { 21 | return "TestAction同步调用有参数:" + param 22 | + " router_target=" + router_target; 23 | } 24 | 25 | // 异步模式+回调 26 | public void action(HCallback callback) { 27 | action(null, callback); 28 | } 29 | 30 | // 异步模式+回调+参数 31 | public void action(Object param, HCallback callback) { 32 | callback.start(); 33 | callback.ok(action(param), null); 34 | callback.complete(); 35 | } 36 | } -------------------------------------------------------------------------------- /router-demo-app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 24 | 25 | -------------------------------------------------------------------------------- /router-demo-app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 19 | 20 |