Repository: HCDarren/DRouter
Branch: master
Commit: a2b02ee10977
Files: 106
Total size: 117.8 KB
Directory structure:
gitextract_w0jpccmc/
├── .gitignore
├── .idea/
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── darren/
│ │ └── drouter/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── darren/
│ │ │ └── drouter/
│ │ │ ├── BaseApplication.java
│ │ │ └── MainActivity.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── darren/
│ └── drouter/
│ └── ExampleUnitTest.kt
├── base-core/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── base/
│ │ └── core/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── base/
│ └── core/
│ └── ExampleUnitTest.java
├── build.gradle
├── circle-module/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── example/
│ │ └── circle_module/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── circle_module/
│ │ │ ├── CircleAction.java
│ │ │ ├── CircleActivity.java
│ │ │ ├── CircleInterceptor.java
│ │ │ ├── CircleInterceptor1.java
│ │ │ └── CircleInterceptor2.java
│ │ └── res/
│ │ ├── layout/
│ │ │ └── activity_circle.xml
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── circle_module/
│ └── ExampleUnitTest.java
├── drouter-api/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── drouter/
│ │ └── api/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── drouter/
│ │ │ └── api/
│ │ │ ├── action/
│ │ │ │ ├── IRouterAction.java
│ │ │ │ ├── IRouterInterceptor.java
│ │ │ │ └── IRouterModule.java
│ │ │ ├── core/
│ │ │ │ ├── DRouter.java
│ │ │ │ └── RouterForward.java
│ │ │ ├── exception/
│ │ │ │ ├── InitException.java
│ │ │ │ ├── RouterActionException.java
│ │ │ │ └── RouterModuleException.java
│ │ │ ├── extra/
│ │ │ │ ├── ActionWrapper.java
│ │ │ │ ├── Consts.java
│ │ │ │ ├── DefaultLogger.java
│ │ │ │ ├── ErrorActionWrapper.java
│ │ │ │ └── ILogger.java
│ │ │ ├── interceptor/
│ │ │ │ ├── ActionInterceptor.java
│ │ │ │ ├── ActionInterceptorChain.java
│ │ │ │ ├── CallActionInterceptor.java
│ │ │ │ └── ErrorActionInterceptor.java
│ │ │ ├── result/
│ │ │ │ ├── ActionCallback.java
│ │ │ │ └── RouterResult.java
│ │ │ ├── thread/
│ │ │ │ ├── ActionPost.java
│ │ │ │ ├── ActionPostQueue.java
│ │ │ │ ├── AsyncPoster.java
│ │ │ │ ├── BackgroundPoster.java
│ │ │ │ ├── HandlerPoster.java
│ │ │ │ ├── Poster.java
│ │ │ │ └── PosterSupport.java
│ │ │ └── utils/
│ │ │ ├── ClassUtils.java
│ │ │ └── MapUtils.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── drouter/
│ └── api/
│ └── ExampleUnitTest.java
├── drouter-base/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── drouter/
│ └── base/
│ ├── ThreadMode.java
│ └── annotation/
│ ├── Action.java
│ └── Interceptor.java
├── drouter-compiler/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── drouter/
│ └── compiler/
│ ├── Consts.java
│ ├── InterceptorProcessor.java
│ ├── ModuleProcessor.java
│ └── util/
│ └── TextUtils.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── login-module/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── login/
│ │ └── module/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── login/
│ │ │ └── module/
│ │ │ ├── LoginAction.java
│ │ │ └── LoginActivity.java
│ │ └── res/
│ │ ├── layout/
│ │ │ └── activity_login.xml
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── login/
│ └── module/
│ └── ExampleUnitTest.java
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
1.8
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/runConfigurations.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: README.md
================================================
# Android 平台多模块多组件开发的路由库
### **一. DRouter 基本介绍**
**1.** 该库所涉及到的类大概在 30 个左右,源码并不多相信我们都能读懂里面的内容,这里罗列一下源码中所涉及到的一些知识点:
(1) 编译时注解自动生成 Module、Action 和 Intercepter
(2) 线程、线程池、线程同步异步和 Handler
(3) 责任链模式、享元模式、策略模式、模板模式 ...
**2.** 作为一个多模块的路由通信库,相信它已支持了所有跨模块通信的使用场景,功能介绍如下:
(1) 支持依赖注入,可单独作为依赖注入框架使用
(2) 支持线程切换和调度(原始线程,主线程,同步,异步)
(3) 支持多模块工程下的所有跨模块通信使用场景
(4) 支持添加多个拦截器,可根据优先级自定义拦截顺序
(5) 支持权限和网络检测、登录拦截跳转和数据埋点等功能
**3.** 笔者阅读了大量的开源库源码,本库的所有代码思想都来自其中,很感激这些大牛的开源和分享精神:
[(1) ARouter](https://github.com/alibaba/ARouter)
[(2) butterknife](https://github.com/JakeWharton/butterknife)
[(3) okhttp](https://github.com/square/okhttp)
[(4) EventBus](https://github.com/greenrobot/EventBus)
[(5) RxJava](https://github.com/ReactiveX/RxJava)
[(6) retrofit](https://github.com/square/retrofit)
### **二. DRouter 基本使用**
1. 在需要跨模块通信的Module中添加依赖和配置
```
defaultConfig {
......
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
dependencies {
.......
annotationProcessor project(':drouter-compiler')
}
```
2. 初始化 SDK
```
public class BaseApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
// 开启 debug
DRouter.openDebug();
// 初始化且只能初始化一次,参数必须是 Application
DRouter.getInstance().init(this);
}
}
```
3. 在 Module 中创建需要执行的 Action
```
// path 必须是以在 gradle 中配置的 moduleName + "/" 开头,否则编译通不过。
// threadMode 支持 POSTING 、MAIN、BACKGROUND、ASYNC 默认情况下是 POSTING(原始线程)
@Action(path = "login/action", threadMode = ThreadMode.MAIN)
public class LoginAction implements IRouterAction {
@Override
public RouterResult invokeAction(Context context, Map requestData) {
// 通信执行方法支持所有场景,启动 Activity,Service,Provider,弹框,缓存数据,获取 Fragment 等等等等
Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra("key", (String) requestData.get("key"));
context.startActivity(intent);
return new RouterResult.Builder().success().object(100).build();
}
}
```
4. 可在任意 Module 中执行跳转
```
// 根据 action 查询只执行对应方法,不处理返回回调,参数携带随意
DRouter.getInstance()
.action("login/action")
.context(this)
.param("key", "value")
.invokeAction();
// 根据 action 查询执行对应方法,并处理返回回调
DRouter.getInstance()
.action("circlemodule/test")
.context(this)
.invokeAction(new ActionCallback() {
@Override
public void onInterrupt() {
Log.e("TAG", "被拦截了");
}
@Override
public void onResult(RouterResult result) {
// 注意该方法的执行线程是由 Action 的 threadMode 决定的,也就是说和 Action 在同一个线程
Log.e("TAG", "result = " + result.toString());
}
});
```
5. 在任意模块下都可添加拦截
```
// priority 优先级越高,拦截器执行越优先
@Interceptor(priority = 18)
public class CircleInterceptor implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
// 圈子详情页必须是要登录,如果没有登录即可拦截跳转到登录页面,否则继续往下执行。
if (chain.actionPath().equals("circlemodule/test")) {
Toast.makeText(actionPost.context, "拦截圈子,跳转到登录", Toast.LENGTH_LONG).show();
// 跳转到登录页面
DRouter.getInstance()
.action("login/action")
.context(actionPost.context)
.invokeAction();
// 这个方法调用后便会拦截整条链
chain.onInterrupt();
}
// 继续向下转发
chain.proceed(actionPost);
}
}
```
6.混淆配置
```
-keep public class com.drouter.assist.**{*;}
```
### **三. 其他**
1. 简书详细介绍地址:https://www.jianshu.com/p/d0e1320704e4
2. 视频详细讲解地址:https://pan.baidu.com/s/1kWoIA95
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.darren.drouter"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
/*compile 'com.alibaba:arouter-api:1.2.1.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.0.3'*/
implementation project(':login-module')
implementation project(':circle-module')
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: app/src/androidTest/java/com/darren/drouter/ExampleInstrumentedTest.kt
================================================
package com.darren.drouter
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("com.darren.drouter", appContext.packageName)
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/darren/drouter/BaseApplication.java
================================================
package com.darren.drouter;
import android.app.Application;
import com.drouter.api.core.DRouter;
/**
* description:
* author: Darren on 2018/1/22 11:27
* email: 240336124@qq.com
* version: 1.0
*/
public class BaseApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
DRouter.openDebug();
DRouter.getInstance().init(this);
}
}
================================================
FILE: app/src/main/java/com/darren/drouter/MainActivity.java
================================================
package com.darren.drouter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.drouter.api.core.DRouter;
import com.drouter.api.result.ActionCallback;
import com.drouter.api.result.RouterResult;
/**
* description:
* author: Darren on 2018/1/22 09:43
* email: 240336124@qq.com
* version: 1.0
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void jumpLogin(View view) {
DRouter.getInstance()
.action("login/action")
.context(this)
.param("key", "value")
.invokeAction();
}
public void jumpCircle(View view) {
DRouter.getInstance()
.action("circlemodule/test")
.context(this)
.param("key", "value")
.invokeAction(new ActionCallback() {
@Override
public void onInterrupt() {
Log.e("TAG", "被拦截了");
}
@Override
public void onResult(RouterResult result) {
Log.e("TAG", "result = " + result.toString());
}
});
}
}
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
================================================
FILE: app/src/main/res/values/strings.xml
================================================
DRouter
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/com/darren/drouter/ExampleUnitTest.kt
================================================
package com.darren.drouter
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
================================================
FILE: base-core/.gitignore
================================================
/build
================================================
FILE: base-core/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:26.1.0'
compile project(':drouter-api')
annotationProcessor project(':drouter-compiler')
}
================================================
FILE: base-core/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: base-core/src/androidTest/java/com/base/core/ExampleInstrumentedTest.java
================================================
package com.base.core;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.base.core.test", appContext.getPackageName());
}
}
================================================
FILE: base-core/src/main/AndroidManifest.xml
================================================
================================================
FILE: base-core/src/main/res/values/strings.xml
================================================
base-core
================================================
FILE: base-core/src/test/java/com/base/core/ExampleUnitTest.java
================================================
package com.base.core;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.1.51'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: circle-module/.gitignore
================================================
/build
================================================
FILE: circle-module/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
repositories {
mavenCentral()
}
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation project(':base-core')
annotationProcessor project(':drouter-compiler')
}
================================================
FILE: circle-module/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: circle-module/src/androidTest/java/com/example/circle_module/ExampleInstrumentedTest.java
================================================
package com.example.circle_module;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.example.circle_module.test", appContext.getPackageName());
}
}
================================================
FILE: circle-module/src/main/AndroidManifest.xml
================================================
================================================
FILE: circle-module/src/main/java/com/example/circle_module/CircleAction.java
================================================
package com.example.circle_module;
import android.content.Context;
import android.content.Intent;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.result.RouterResult;
import com.drouter.base.ThreadMode;
import com.drouter.base.annotation.Action;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/22 10:57
* email: 240336124@qq.com
* version: 1.0
*/
@Action(path = "circlemodule/test", threadMode = ThreadMode.MAIN)
public class CircleAction implements IRouterAction {
@Override
public RouterResult invokeAction(Context context, Map requestData) {
Intent intent = new Intent(context, CircleActivity.class);
intent.putExtra("key", (String) requestData.get("key"));
context.startActivity(intent);
return new RouterResult.Builder().success().build();
}
}
================================================
FILE: circle-module/src/main/java/com/example/circle_module/CircleActivity.java
================================================
package com.example.circle_module;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.drouter.api.core.DRouter;
import com.drouter.api.result.ActionCallback;
import com.drouter.api.result.RouterResult;
/**
* description:
* author: Darren on 2018/1/22 15:08
* email: 240336124@qq.com
* version: 1.0
*/
public class CircleActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_circle);
}
public void click(View view) {
DRouter.getInstance()
.action("login/action")
.context(CircleActivity.this)
.param("key", "value")
.invokeAction(new ActionCallback() {
@Override
public void onInterrupt() {
Log.e("TAG", "被拦截了");
}
@Override
public void onResult(RouterResult result) {
Log.e("TAG", "result = " + result.toString());
}
});
}
}
================================================
FILE: circle-module/src/main/java/com/example/circle_module/CircleInterceptor.java
================================================
package com.example.circle_module;
import android.widget.Toast;
import com.drouter.api.core.DRouter;
import com.drouter.api.interceptor.ActionInterceptor;
import com.drouter.api.thread.ActionPost;
import com.drouter.base.annotation.Interceptor;
/**
* description:
* author: Darren on 2018/1/24 16:14
* email: 240336124@qq.com
* version: 1.0
*/
@Interceptor(priority = 18)
public class CircleInterceptor implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
if (chain.actionPath().equals("circlemodule/test")) {
Toast.makeText(actionPost.context, "拦截圈子,跳转到登录", Toast.LENGTH_LONG).show();
// 拦截
chain.onInterrupt();
// 跳转到登录页面
DRouter.getInstance()
.action("login/action")
.context(actionPost.context)
.invokeAction();
}
// 继续向下转发
chain.proceed(actionPost);
}
}
================================================
FILE: circle-module/src/main/java/com/example/circle_module/CircleInterceptor1.java
================================================
package com.example.circle_module;
import com.drouter.api.interceptor.ActionInterceptor;
import com.drouter.api.thread.ActionPost;
import com.drouter.base.annotation.Interceptor;
/**
* description:
* author: Darren on 2018/1/24 16:14
* email: 240336124@qq.com
* version: 1.0
*/
@Interceptor(priority = 6)
public class CircleInterceptor1 implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
if (chain.actionPath().equals("circlemodule/test")) {
// 拦截
chain.onInterrupt();
}
// 继续向下转发
chain.proceed(actionPost);
}
}
================================================
FILE: circle-module/src/main/java/com/example/circle_module/CircleInterceptor2.java
================================================
package com.example.circle_module;
import com.drouter.api.interceptor.ActionInterceptor;
import com.drouter.api.thread.ActionPost;
import com.drouter.base.annotation.Interceptor;
/**
* description:
* author: Darren on 2018/1/24 16:14
* email: 240336124@qq.com
* version: 1.0
*/
@Interceptor(priority = 10)
public class CircleInterceptor2 implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
if (chain.actionPath().equals("circlemodule/test")) {
// 拦截
chain.onInterrupt();
}
// 继续向下转发
chain.proceed(actionPost);
}
}
================================================
FILE: circle-module/src/main/res/layout/activity_circle.xml
================================================
================================================
FILE: circle-module/src/main/res/values/strings.xml
================================================
circle-module
================================================
FILE: circle-module/src/test/java/com/example/circle_module/ExampleUnitTest.java
================================================
package com.example.circle_module;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: drouter-api/.gitignore
================================================
/build
================================================
FILE: drouter-api/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
compile project(':drouter-base')
}
================================================
FILE: drouter-api/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: drouter-api/src/androidTest/java/com/drouter/api/ExampleInstrumentedTest.java
================================================
package com.drouter.api;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.drouter.api.test", appContext.getPackageName());
}
}
================================================
FILE: drouter-api/src/main/AndroidManifest.xml
================================================
================================================
FILE: drouter-api/src/main/java/com/drouter/api/action/IRouterAction.java
================================================
package com.drouter.api.action;
import android.content.Context;
import com.drouter.api.result.RouterResult;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/22 10:32
* email: 240336124@qq.com
* version: 1.0
*/
public interface IRouterAction {
// 执行 Action 方法
RouterResult invokeAction(Context context, Map requestData);
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/action/IRouterInterceptor.java
================================================
package com.drouter.api.action;
import com.drouter.api.interceptor.ActionInterceptor;
import java.util.List;
/**
* description:
* author: Darren on 2018/1/22 11:08
* email: 240336124@qq.com
* version: 1.0
*/
public interface IRouterInterceptor {
// 通过 Action 的名称找到 Action
List getInterceptors();
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/action/IRouterModule.java
================================================
package com.drouter.api.action;
import com.drouter.api.extra.ActionWrapper;
/**
* description:
* author: Darren on 2018/1/22 11:08
* email: 240336124@qq.com
* version: 1.0
*/
public interface IRouterModule {
// 通过 Action 的名称找到 Action
ActionWrapper findAction(String actionName);
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/core/DRouter.java
================================================
package com.drouter.api.core;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.widget.Toast;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.action.IRouterInterceptor;
import com.drouter.api.action.IRouterModule;
import com.drouter.api.exception.InitException;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.extra.Consts;
import com.drouter.api.extra.DefaultLogger;
import com.drouter.api.extra.ErrorActionWrapper;
import com.drouter.api.extra.ILogger;
import com.drouter.api.interceptor.ActionInterceptor;
import com.drouter.api.interceptor.CallActionInterceptor;
import com.drouter.api.interceptor.ErrorActionInterceptor;
import com.drouter.api.thread.PosterSupport;
import com.drouter.api.utils.ClassUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/22 09:59
* email: 240336124@qq.com
* version: 1.0
*/
public class DRouter {
// 是否被初始化
private volatile static boolean hasInit = false;
// 是否是 debugable 状态
private volatile static boolean debuggable = false;
// 日志打印
public volatile static ILogger logger = new DefaultLogger();
// 缓存的 RouterAction
private volatile static Map cacheRouterActions = new HashMap();
// 缓存的 RouterModule
private volatile static Map cacheRouterModules = new HashMap();
// 所有 moudle
private static List mAllModuleClassName;
private Context mApplicationContext;
private static List interceptors = new ArrayList<>();
public static synchronized void openDebug() {
debuggable = true;
logger.showLog(true);
logger.d(Consts.TAG, "DRouter openDebug");
}
private volatile static DRouter instance = null;
public static boolean debuggable() {
return debuggable;
}
private DRouter() {
}
/**
* Get instance of router. A
* All feature U use, will be starts here.
*/
public static DRouter getInstance() {
if (instance == null) {
synchronized (DRouter.class) {
if (instance == null) {
instance = new DRouter();
}
}
}
return instance;
}
/**
* 初始化数据
*/
public void init(Application context) {
if (hasInit) {
throw new InitException("ARouter already initialized, It can only be initialized once.");
}
hasInit = true;
this.mApplicationContext = context;
// 获取 com.drotuer.assist 包名下的所有类名信息
try {
mAllModuleClassName = ClassUtils.getFileNameByPackageName(context, Consts.ROUTER_MODULE_PACK_NAME);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (String className : mAllModuleClassName) {
logger.d(Consts.TAG, "扫描到: " + className);
}
// 添加并且实例化所有拦截器
scanAddInterceptors(context);
}
// 扫描并且添加拦截器
private void scanAddInterceptors(final Context context) {
PosterSupport.getExecutorService().execute(new Runnable() {
@Override
public void run() {
// 1. 错误拦截器
interceptors.add(new ErrorActionInterceptor());
// 2. module 自定义的拦截器
try {
List interceptorGroups = ClassUtils.getFileNameByPackageName(context, Consts.ROUTER_INTERCEPTOR_PACK_NAME);
// 循环所有的 Group 拦截器
for (String interceptorGroup : interceptorGroups) {
if (interceptorGroup.contains(Consts.ROUTER_INTERCEPTOR_GROUP_PREFIX)) {
IRouterInterceptor routerInterceptor = (IRouterInterceptor) Class.forName(interceptorGroup).newInstance();
List interceptorClasses = routerInterceptor.getInterceptors();
for (int i = interceptorClasses.size() - 1; i >= 0; i--) {
ActionInterceptor interceptor = interceptorClasses.get(i);
// 添加到拦截器链表
interceptors.add(interceptor);
}
}
}
} catch (Exception e) {
e.printStackTrace();
String message = "Instance interceptor error: " + e.getMessage();
logger.e(Consts.TAG, message);
}
// 3. 最后添加 Action 执行调用的拦截器
interceptors.add(new CallActionInterceptor());
}
});
}
public RouterForward action(String actionName) {
// 1. 动态先查找加载 Module
// actionName 的格式必须是 xxx/xxx
if (!actionName.contains("/")) {
String message = "action name format error -> <" + actionName + ">, like: moduleName/actionName";
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
// 2.获取 moduleName,实例化 Module,并缓存
String moduleName = actionName.split("/")[0];
String moduleClassName = searchModuleClassName(moduleName);
if (TextUtils.isEmpty(moduleClassName)) {
String message = String.format("Please check to the action name is correct: according to the <%s> cannot find module %s.", actionName, moduleName);
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
IRouterModule routerModule = cacheRouterModules.get(moduleClassName);
if (routerModule == null) {
try {
Class extends IRouterModule> moduleClass = (Class extends IRouterModule>) Class.forName(moduleClassName);
routerModule = moduleClass.newInstance();
cacheRouterModules.put(moduleClassName, routerModule);
} catch (Exception e) {
e.printStackTrace();
String message = "instance module error: " + e.getMessage();
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
}
// 3. 从 Module 中获取 ActionWrapper 类名,然后创建缓存 ActionWrapper
ActionWrapper actionWrapper = cacheRouterActions.get(actionName);
if (actionWrapper == null) {
actionWrapper = routerModule.findAction(actionName);
} else {
return new RouterForward(actionWrapper, interceptors);
}
if (actionWrapper == null) {
String message = String.format("Please check to the action name is correct: according to the <%s> cannot find action.", actionName);
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
Class extends IRouterAction> actionClass = actionWrapper.getActionClass();
IRouterAction routerAction = actionWrapper.getRouterAction();
if (routerAction == null) {
try {
if (!IRouterAction.class.isAssignableFrom(actionClass)) {
String message = actionClass.getCanonicalName() + " must be implements IRouterAction.";
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
// 创建 RouterAction 实例,并缓存起来
routerAction = actionClass.newInstance();
actionWrapper.setRouterAction(routerAction);
cacheRouterActions.put(actionName, actionWrapper);
} catch (Exception e) {
String message = "instance action error: " + e.getMessage();
debugMessage(message);
return new RouterForward(new ErrorActionWrapper(), interceptors);
}
}
return new RouterForward(actionWrapper, interceptors);
}
/**
* 显示 debug 信息
*
* @param message
*/
private void debugMessage(String message) {
if (debuggable) {
logger.e(Consts.TAG, message);
showToast(message);
}
}
/**
* 打印显示 Toast
*
* @param message
*/
private void showToast(String message) {
Toast.makeText(mApplicationContext, message, Toast.LENGTH_LONG).show();
}
/**
* 根据 moduleName 查询 module 的全类名
*
* @param moduleName
* @return
*/
private String searchModuleClassName(String moduleName) {
for (String moduleClassName : mAllModuleClassName) {
if (moduleClassName.contains(moduleName)) {
return moduleClassName;
}
}
return null;
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/core/RouterForward.java
================================================
package com.drouter.api.core;
import android.content.Context;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.interceptor.ActionInterceptor;
import com.drouter.api.interceptor.ActionInterceptorChain;
import com.drouter.api.result.ActionCallback;
import com.drouter.api.thread.ActionPost;
import com.drouter.base.ThreadMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* description: 路由转发
* author: Darren on 2018/1/22 11:20
* email: 240336124@qq.com
* version: 1.0
*/
public class RouterForward {
private ActionWrapper mActionWrapper;
private Context mContext;
private Map mParams;
private ThreadMode mThreadMode = null;
// 所有拦截器表
private List interceptors;
/**
* 指定 threadMode 这里指定的优先级高于 Action 注解上的 threadMode
*
* @param threadMode
* @return
*/
public RouterForward threadMode(ThreadMode threadMode) {
this.mThreadMode = threadMode;
return this;
}
RouterForward(ActionWrapper actionWrapper, List interceptors) {
this.mActionWrapper = actionWrapper;
mParams = new HashMap<>();
this.interceptors = interceptors;
}
/**
* 执行 Action
*
* @return
*/
public void invokeAction() {
invokeAction(ActionCallback.DEFAULT_ACTION_CALLBACK);
}
/**
* 执行 Action
*
* @return
*/
public void invokeAction(ActionCallback actionCallback) {
// 先封装 actionPost
mActionWrapper.setThreadMode(getThreadMode());
ActionPost actionPost = ActionPost.obtainActionPost(mActionWrapper, mContext, mParams, actionCallback);
// 开始拦截器的流程
ActionInterceptor.ActionChain chain = new ActionInterceptorChain(interceptors, actionPost, 0);
chain.proceed(actionPost);
}
/**
* 路由转发方法传递的 threadMode 优先级高于 Action 注解上的 threadMode
*
* @return
*/
public ThreadMode getThreadMode() {
return mThreadMode == null ? mActionWrapper.getThreadMode() : mThreadMode;
}
public RouterForward context(Context context) {
this.mContext = context;
return this;
}
public RouterForward param(String key, Object value) {
mParams.put(key, value);
return this;
}
public RouterForward param(Map params) {
mParams.putAll(params);
return this;
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/exception/InitException.java
================================================
package com.drouter.api.exception;
/**
* description: 初始化异常类
* author: Darren on 2018/1/22 10:26
* email: 240336124@qq.com
* version: 1.0
*/
public class InitException extends RuntimeException {
public InitException(String message) {
super(message);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/exception/RouterActionException.java
================================================
package com.drouter.api.exception;
/**
* description: Router Action Exception
* author: Darren on 2018/1/22 10:44
* email: 240336124@qq.com
* version: 1.0
*/
public class RouterActionException extends RuntimeException{
public RouterActionException(String message) {
super(message);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/exception/RouterModuleException.java
================================================
package com.drouter.api.exception;
/**
* description: Router Module Exception
* author: Darren on 2018/1/22 10:44
* email: 240336124@qq.com
* version: 1.0
*/
public class RouterModuleException extends RuntimeException{
public RouterModuleException(String message) {
super(message);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/extra/ActionWrapper.java
================================================
package com.drouter.api.extra;
import com.drouter.api.action.IRouterAction;
import com.drouter.base.ThreadMode;
/**
* description:
* author: Darren on 2018/1/23 14:22
* email: 240336124@qq.com
* version: 1.0
*/
public class ActionWrapper {
private Class extends IRouterAction> actionClass;
private String path;
private ThreadMode threadMode;
private boolean extraProcess;
private IRouterAction routerAction;
ActionWrapper() {
}
public void setRouterAction(IRouterAction routerAction) {
this.routerAction = routerAction;
}
public void setThreadMode(ThreadMode threadMode) {
this.threadMode = threadMode;
}
public IRouterAction getRouterAction() {
return routerAction;
}
private ActionWrapper(Class extends IRouterAction> actionClass, String path, boolean extraProcess, ThreadMode threadMode) {
this.actionClass = actionClass;
this.path = path;
this.extraProcess = extraProcess;
this.threadMode = threadMode;
}
public Class extends IRouterAction> getActionClass() {
return actionClass;
}
public ThreadMode getThreadMode() {
return threadMode;
}
public String getPath() {
return path;
}
public boolean isExtraProcess() {
return extraProcess;
}
public static ActionWrapper build(Class extends IRouterAction> actionClass, String path, boolean extraProcess, ThreadMode threadMode) {
return new ActionWrapper(actionClass, path, extraProcess, threadMode);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/extra/Consts.java
================================================
package com.drouter.api.extra;
/**
* description:
* author: Darren on 2018/1/22 10:01
* email: 240336124@qq.com
* version: 1.0
*/
public class Consts {
public static final String SDK_NAME = "DRouter";
public static final String TAG = SDK_NAME;
public static final String SUFFIX_INTERCEPTORS = "Interceptors";
public static final String ROUTER_MODULE_PACK_NAME = "com.drouter.assist.module";
public static final String ROUTER_INTERCEPTOR_PACK_NAME = "com.drouter.assist.interceptor";
public static final String ROUTER_INTERCEPTOR_GROUP_PREFIX = "DRouter$$Interceptor$$";
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/extra/DefaultLogger.java
================================================
package com.drouter.api.extra;
import android.text.TextUtils;
import android.util.Log;
/**
* description:
* author: Darren on 2018/1/22 10:05
* email: 240336124@qq.com
* version: 1.0
*/
public class DefaultLogger implements ILogger {
boolean isShowLog = false;
private String defaultTag = "DRouter";
@Override
public void showLog(boolean isShowLog) {
this.isShowLog = isShowLog;
}
@Override
public void d(String tag, String message) {
if (isShowLog) {
Log.d(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);
}
}
@Override
public void i(String tag, String message) {
if (isShowLog) {
Log.i(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);
}
}
@Override
public void w(String tag, String message) {
if (isShowLog) {
Log.w(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);
}
}
@Override
public void e(String tag, String message) {
if (isShowLog) {
Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);
}
}
public String getDefaultTag() {
return defaultTag;
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/extra/ErrorActionWrapper.java
================================================
package com.drouter.api.extra;
/**
* description:
* author: Darren on 2018/1/23 15:45
* email: 240336124@qq.com
* version: 1.0
*/
public class ErrorActionWrapper extends ActionWrapper {
public ErrorActionWrapper() {
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/extra/ILogger.java
================================================
package com.drouter.api.extra;
/**
* description:
* author: Darren on 2018/1/22 10:02
* email: 240336124@qq.com
* version: 1.0
*/
public interface ILogger {
void showLog(boolean isShowLog);
void d(String tag, String message);
void i(String tag, String message);
void w(String tag, String message);
void e(String tag, String message);
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/interceptor/ActionInterceptor.java
================================================
package com.drouter.api.interceptor;
import com.drouter.api.thread.ActionPost;
/**
* description: 拦截器
* author: Darren on 2018/1/22 11:59
* email: 240336124@qq.com
* version: 1.0
*/
public interface ActionInterceptor {
void intercept(ActionChain chain);
interface ActionChain {
// 打断拦截
void onInterrupt();
// 分发给下一个拦截器
void proceed(ActionPost actionPost);
// 获取 ActionPost
ActionPost action();
String actionPath();
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/interceptor/ActionInterceptorChain.java
================================================
package com.drouter.api.interceptor;
import com.drouter.api.thread.ActionPost;
import java.util.List;
/**
* description:
* author: Darren on 2018/1/24 09:38
* email: 240336124@qq.com
* version: 1.0
*/
public class ActionInterceptorChain implements ActionInterceptor.ActionChain {
// 是否被拦截了
private boolean isInterrupt = false;
private List interceptors;
private ActionPost actionPost;
private int index;
public ActionInterceptorChain(List interceptors, ActionPost actionPost, int index) {
this.interceptors = interceptors;
this.actionPost = actionPost;
this.index = index;
}
@Override
public void onInterrupt() {
isInterrupt = true;
actionPost.actionCallback.onInterrupt();
}
@Override
public void proceed(ActionPost actionPost) { // 0
if (!isInterrupt && index < interceptors.size()) {
// 继续往下分发
ActionInterceptor.ActionChain next = new ActionInterceptorChain(interceptors, actionPost, index + 1);
// 0 拦截器
ActionInterceptor interceptor = interceptors.get(index);
// 执行第一个
interceptor.intercept(next);
}
}
@Override
public ActionPost action() {
return actionPost;
}
@Override
public String actionPath() {
return actionPost.actionWrapper.getPath();
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/interceptor/CallActionInterceptor.java
================================================
package com.drouter.api.interceptor;
import android.os.Looper;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.result.RouterResult;
import com.drouter.api.thread.ActionPost;
import com.drouter.api.thread.PosterSupport;
/**
* description: 执行 Action 方法的最后一个拦截器
* author: Darren on 2018/1/24 09:03
* email: 240336124@qq.com
* version: 1.0
*/
public class CallActionInterceptor implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
// 执行 Action 方法
ActionPost actionPost = chain.action();
invokeAction(actionPost, Looper.myLooper() == Looper.getMainLooper());
}
/**
* 处理线程切换
*
* @param isMainThread
* @return
*/
private void invokeAction(ActionPost actionPost, boolean isMainThread) {
switch (actionPost.actionWrapper.getThreadMode()) {
case POSTING:
invokeAction(actionPost);
case MAIN:
if (isMainThread) {
invokeAction(actionPost);
} else {
PosterSupport.getMainPoster().enqueue(actionPost);
}
break;
case BACKGROUND:
if (isMainThread) {
PosterSupport.getBackgroundPoster().enqueue(actionPost);
} else {
invokeAction(actionPost);
}
break;
case ASYNC:
PosterSupport.getAsyncPoster().enqueue(actionPost);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + actionPost.actionWrapper.getThreadMode());
}
}
/**
* 执行 Action
*
* @param actionPost
*/
private void invokeAction(ActionPost actionPost) {
ActionWrapper actionWrapper = actionPost.actionWrapper;
IRouterAction routerAction = actionWrapper.getRouterAction();
RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);
actionPost.actionCallback.onResult(routerResult);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/interceptor/ErrorActionInterceptor.java
================================================
package com.drouter.api.interceptor;
import com.drouter.api.extra.ErrorActionWrapper;
import com.drouter.api.thread.ActionPost;
/**
* description: 错误的 Action 拦截器,放在拦截链的第一个位置
* author: Darren on 2018/1/24 09:03
* email: 240336124@qq.com
* version: 1.0
*/
public class ErrorActionInterceptor implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
// 拦截错误
if (actionPost.actionWrapper instanceof ErrorActionWrapper) {
chain.onInterrupt();
}
// 继续分发
chain.proceed(actionPost);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/result/ActionCallback.java
================================================
package com.drouter.api.result;
/**
* description:
* author: Darren on 2018/1/24 09:05
* email: 240336124@qq.com
* version: 1.0
*/
public interface ActionCallback {
// 被拦截了
void onInterrupt();
// 没被拦截返回结果
void onResult(RouterResult result);
// 默认的 ActionCallback
ActionCallback DEFAULT_ACTION_CALLBACK = new ActionCallback() {
@Override
public void onInterrupt() {
}
@Override
public void onResult(RouterResult result) {
}
};
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/result/RouterResult.java
================================================
package com.drouter.api.result;
/**
* description: 路由的返回结果
* author: Darren on 2018/1/22 10:35
* email: 240336124@qq.com
* version: 1.0
*/
public class RouterResult {
static final int SUCCEED_CODE = 0x000011;
static final int ERROR_CODE = 0x000022;
private String msg;
private int code;
private Object object;
public int getCode() {
return code;
}
private RouterResult(Builder builder) {
this.code = builder.code;
this.msg = builder.msg;
this.object = builder.object;
}
@Override
public String toString() {
return super.toString() + "{" +
"msg='" + msg + '\'' +
", code=" + code +
", object=" + object +
'}';
}
public String getMsg() {
return msg;
}
public Object getObject() {
return object;
}
/**
* 返回是否成功
*
* @return
*/
public boolean isSucceed() {
return code == SUCCEED_CODE;
}
public static class Builder {
int code = SUCCEED_CODE;
String msg;
Object object;
public Builder error() {
this.code = ERROR_CODE;
return this;
}
public Builder success() {
this.code = SUCCEED_CODE;
return this;
}
public Builder msg(String msg) {
this.msg = msg;
return this;
}
public Builder object(Object object) {
this.object = object;
return this;
}
public RouterResult build() {
return new RouterResult(this);
}
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/ActionPost.java
================================================
package com.drouter.api.thread;
import android.content.Context;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.result.ActionCallback;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/23 16:11
* email: 240336124@qq.com
* version: 1.0
*/
public final class ActionPost {
private final static List pendingPostPool = new ArrayList();
public Context context;
public ActionWrapper actionWrapper;
public Map params;
public ActionCallback actionCallback;
ActionPost next;
private ActionPost(ActionWrapper actionWrapper, Context context, Map params, ActionCallback actionCallback) {
this.context = context;
this.actionWrapper = actionWrapper;
this.params = params;
this.actionCallback = actionCallback;
}
public static ActionPost obtainActionPost(ActionWrapper actionWrapper, Context context, Map params, ActionCallback actionCallback) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
ActionPost actionPost = pendingPostPool.remove(size - 1);
actionPost.context = context;
actionPost.actionWrapper = actionWrapper;
actionPost.params = params;
actionPost.next = null;
actionPost.actionCallback = actionCallback;
return actionPost;
}
}
return new ActionPost(actionWrapper, context, params, actionCallback);
}
public void releasePendingPost() {
this.context = null;
this.actionWrapper = null;
this.next = null;
this.actionCallback = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(this);
}
}
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/ActionPostQueue.java
================================================
package com.drouter.api.thread;
/**
* description:
* author: Darren on 2018/1/23 16:13
* email: 240336124@qq.com
* version: 1.0
*/
public class ActionPostQueue {
private ActionPost head;
private ActionPost tail;
synchronized void enqueue(ActionPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronized ActionPost poll() {
ActionPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
synchronized ActionPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/AsyncPoster.java
================================================
package com.drouter.api.thread;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.result.RouterResult;
/**
* description: 处理异步
* author: Darren on 2018/1/23 16:46
* email: 240336124@qq.com
* version: 1.0
*/
public class AsyncPoster implements Runnable, Poster {
private final ActionPostQueue queue;
AsyncPoster() {
queue = new ActionPostQueue();
}
@Override
public void run() {
ActionPost actionPost = queue.poll();
if (actionPost == null) {
throw new IllegalStateException("No pending post available");
}
ActionWrapper actionWrapper = actionPost.actionWrapper;
IRouterAction routerAction = actionWrapper.getRouterAction();
RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);
actionPost.actionCallback.onResult(routerResult);
actionPost.releasePendingPost();
}
@Override
public void enqueue(ActionPost actionPost) {
queue.enqueue(actionPost);
PosterSupport.getExecutorService().execute(this);
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/BackgroundPoster.java
================================================
package com.drouter.api.thread;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.core.DRouter;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.extra.Consts;
import com.drouter.api.result.RouterResult;
/**
* description:
* author: Darren on 2018/1/23 17:01
* email: 240336124@qq.com
* version: 1.0
*/
final class BackgroundPoster implements Runnable, Poster {
private final ActionPostQueue queue;
private volatile boolean executorRunning;
BackgroundPoster() {
queue = new ActionPostQueue();
}
@Override
public void enqueue(ActionPost actionPost) {
synchronized (this) {
queue.enqueue(actionPost);
if (!executorRunning) {
executorRunning = true;
PosterSupport.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
ActionPost actionPost = queue.poll(1000);
if (actionPost == null) {
synchronized (this) {
// Check again, this time in synchronized
actionPost = queue.poll();
if (actionPost == null) {
executorRunning = false;
return;
}
}
}
ActionWrapper actionWrapper = actionPost.actionWrapper;
IRouterAction routerAction = actionWrapper.getRouterAction();
RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);
actionPost.actionCallback.onResult(routerResult);
actionPost.releasePendingPost();
}
} catch (InterruptedException e) {
DRouter.logger.e(Consts.TAG, Thread.currentThread().getName() + " was interruppted");
}
} finally {
executorRunning = false;
}
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/HandlerPoster.java
================================================
package com.drouter.api.thread;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.extra.ActionWrapper;
import com.drouter.api.result.RouterResult;
/**
* description: 处理主线程切换
* author: Darren on 2018/1/23 16:10
* email: 240336124@qq.com
* version: 1.0
*/
public class HandlerPoster extends Handler implements Poster {
private final ActionPostQueue queue;
private final int maxMillisInsideHandleMessage;
private boolean handlerActive;
protected HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new ActionPostQueue();
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
ActionPost actionPost = queue.poll();
if (actionPost == null) {
synchronized (this) {
// Check again, this time in synchronized
actionPost = queue.poll();
if (actionPost == null) {
handlerActive = false;
return;
}
}
}
ActionWrapper actionWrapper = actionPost.actionWrapper;
IRouterAction routerAction = actionWrapper.getRouterAction();
RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);
actionPost.actionCallback.onResult(routerResult);
actionPost.releasePendingPost();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new RuntimeException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
@Override
public void enqueue(ActionPost actionPost) {
synchronized (this) {
queue.enqueue(actionPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new RuntimeException("Could not send handler message");
}
}
}
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/Poster.java
================================================
package com.drouter.api.thread;
/**
* description:
* author: Darren on 2018/1/23 16:05
* email: 240336124@qq.com
* version: 1.0
*/
public interface Poster {
void enqueue(ActionPost actionPost);
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/thread/PosterSupport.java
================================================
package com.drouter.api.thread;
import android.os.Looper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* description:
* author: Darren on 2018/1/23 16:08
* email: 240336124@qq.com
* version: 1.0
*/
public class PosterSupport {
private static volatile Poster mainPoster, backgroundPoster, asyncPoster;
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
public static Poster getMainPoster() {
if (mainPoster == null) {
synchronized (PosterSupport.class) {
if (mainPoster == null) {
mainPoster = new HandlerPoster(Looper.getMainLooper(), 10);
}
}
}
return mainPoster;
}
public static ExecutorService getExecutorService() {
return DEFAULT_EXECUTOR_SERVICE;
}
public static Poster getBackgroundPoster() {
if (backgroundPoster == null) {
synchronized (PosterSupport.class) {
if (backgroundPoster == null) {
backgroundPoster = new BackgroundPoster();
}
}
}
return backgroundPoster;
}
public static Poster getAsyncPoster() {
if (asyncPoster == null) {
synchronized (PosterSupport.class) {
if (asyncPoster == null) {
asyncPoster = new AsyncPoster();
}
}
}
return asyncPoster;
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/utils/ClassUtils.java
================================================
package com.drouter.api.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import com.drouter.api.core.DRouter;
import com.drouter.api.extra.Consts;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dalvik.system.DexFile;
/**
* description: thanks alibaba ARouter
* author: Darren on 2018/1/23 09:24
* email: 240336124@qq.com
* version: 1.0
*/
public class ClassUtils {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator + "secondary-dexes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? Context.MODE_PRIVATE : Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*/
public static List getFileNameByPackageName(Context context, String packageName) throws PackageManager.NameNotFoundException, IOException {
List classNames = new ArrayList<>();
for (String path : getSourcePaths(context)) {
DexFile dexfile;
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.contains(packageName)) {
classNames.add(className);
}
}
}
DRouter.logger.d(Consts.TAG, "Scan " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List sourcePaths = new ArrayList<>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
if (!isVMMultidexCapable()) {
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
}
}
return sourcePaths;
}
/**
* Identifies if the current VM has a native support for multidex, meaning there is no need for
* additional installation by this library.
*
* @return true if the VM handles multidex
*/
private static boolean isVMMultidexCapable() {
boolean isMultidexCapable = false;
String vmName = null;
try {
if (isYunOS()) {
vmName = "'YunOS'";
isMultidexCapable = Integer.valueOf(System.getProperty("ro.build.version.sdk")) >= 21;
} else {
vmName = "'Android'";
String versionString = System.getProperty("java.vm.version");
if (versionString != null) {
Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
if (matcher.matches()) {
try {
int major = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)
|| ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)
&& (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));
} catch (NumberFormatException ignore) {
// let isMultidexCapable be false
}
}
}
}
} catch (Exception ignore) {
}
return isMultidexCapable;
}
/**
* 判断系统是否为YunOS系统
*/
private static boolean isYunOS() {
try {
String version = System.getProperty("ro.yunos.version");
String vmName = System.getProperty("java.vm.name");
return (vmName != null && vmName.toLowerCase().contains("lemur"))
|| (version != null && version.trim().length() > 0);
} catch (Exception ignore) {
return false;
}
}
}
================================================
FILE: drouter-api/src/main/java/com/drouter/api/utils/MapUtils.java
================================================
package com.drouter.api.utils;
import com.drouter.api.interceptor.ActionInterceptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/24 16:57
* email: 240336124@qq.com
* version: 1.0
*/
public class MapUtils {
public static List getInterceptorClasses(Map map) {
List list = new ArrayList();
for (Object key : map.keySet()) {
list.add(map.get(key));
}
return list;
}
}
================================================
FILE: drouter-api/src/main/res/values/strings.xml
================================================
drouter-api
================================================
FILE: drouter-api/src/test/java/com/drouter/api/ExampleUnitTest.java
================================================
package com.drouter.api;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: drouter-base/.gitignore
================================================
/build
================================================
FILE: drouter-base/build.gradle
================================================
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
================================================
FILE: drouter-base/src/main/java/com/drouter/base/ThreadMode.java
================================================
package com.drouter.base;
/**
* description: thanks EventBus
* author: Darren on 2018/1/23 08:57
* email: 240336124@qq.com
* version: 1.0
*/
public enum ThreadMode {
/**
* Action will be called directly in the same thread, which is posting the event. This is the default. Event delivery
* implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
* simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
* using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
*/
POSTING,
/**
* On Android, action will be called in Android's main thread (UI thread). If the posting thread is
* the main thread, action methods will be called directly, blocking the posting thread. Otherwise the event
* is queued for delivery (non-blocking). Action using this mode must return quickly to avoid blocking the main thread.
* If not on Android, behaves the same as {@link #POSTING}.
*/
MAIN,
/**
* On Android, action will be called in a background thread. If posting thread is not the main thread, action methods
* will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
* background thread, that will deliver all its events sequentially. Action using this mode should try to
* return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
*/
BACKGROUND,
/**
* Action will be called in a separate thread. This is always independent from the posting thread and the
* main thread. Posting events never wait for action methods using this mode. Action methods should
* use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
* of long running asynchronous action methods at the same time to limit the number of concurrent threads. EventBus
* uses a thread pool to efficiently reuse threads from completed asynchronous action notifications.
*/
ASYNC
}
================================================
FILE: drouter-base/src/main/java/com/drouter/base/annotation/Action.java
================================================
package com.drouter.base.annotation;
import com.drouter.base.ThreadMode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* description: 模块注册 apt
* author: Darren on 2018/1/22 12:32
* email: 240336124@qq.com
* version: 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Action {
/**
* thread mode
*/
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* Path of route
*/
String path();
/**
* extra process
*/
boolean extraProcess() default false;
}
================================================
FILE: drouter-base/src/main/java/com/drouter/base/annotation/Interceptor.java
================================================
package com.drouter.base.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* description:
* author: Darren on 2018/1/24 16:10
* email: 240336124@qq.com
* version: 1.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
int priority();
}
================================================
FILE: drouter-compiler/.gitignore
================================================
/build
================================================
FILE: drouter-compiler/build.gradle
================================================
apply plugin: 'java-library'
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':drouter-base')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation 'com.squareup:javapoet:1.7.0'
}
tasks.withType(JavaCompile){
options.encoding='UTF-8'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
================================================
FILE: drouter-compiler/src/main/java/com/drouter/compiler/Consts.java
================================================
package com.drouter.compiler;
/**
* description:
* author: Darren on 2018/1/22 10:01
* email: 240336124@qq.com
* version: 1.0
*/
public class Consts {
public static final String SDK_NAME = "DRouter";
public static final String TAG = SDK_NAME;
public static final String SUFFIX_INTERCEPTORS = "Interceptors";
public static final String ROUTER_MODULE_PACK_NAME = "com.drouter.assist.module";
public static final String ROUTER_INTERCEPTOR_PACK_NAME = "com.drouter.assist.interceptor";
public static final String ACTIONINTERCEPTOR = "com.drouter.api.interceptor.ActionInterceptor";
public static final String ROUTERACTION = "com.drouter.api.action.IRouterAction";
}
================================================
FILE: drouter-compiler/src/main/java/com/drouter/compiler/InterceptorProcessor.java
================================================
package com.drouter.compiler;
import com.drouter.base.annotation.Interceptor;
import com.drouter.compiler.util.TextUtils;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
/**
* description:
* author: Darren on 2018/1/22 12:29
* email: 240336124@qq.com
* version: 1.0
*/
@AutoService(Processor.class)
public class InterceptorProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Filer mFiler;
private final String KEY_MODULE_NAME = "moduleName";
private TypeMirror iInterceptor = null;
private String moduleName = null;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mElementUtils = processingEnvironment.getElementUtils();
iInterceptor = mElementUtils.getTypeElement(Consts.ACTIONINTERCEPTOR).asType();
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 1. 有没配置 modelName 防止 class 类冲突
String moduleName = "";
Map options = processingEnv.getOptions();
if (isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
}
if (!TextUtils.isEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
} else {
String errorMessage = "These no module name, at 'build.gradle', like :\n" +
"apt {\n" +
" arguments {\n" +
" moduleName project.getName();\n" +
" }\n" +
"}\n";
throw new RuntimeException("DRouter::Compiler >>> No module name, for more information, look at gradle log.\n" + errorMessage);
}
// 生成类继承和实现接口
ClassName routerAssistClassName = ClassName.get("com.drouter.api.action", "IRouterInterceptor");
ClassName mapClassName = ClassName.get("java.util", "Map");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("DRouter$$Interceptor$$" + moduleName)
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addSuperinterface(routerAssistClassName)
.addField(mapClassName, "interceptors", Modifier.PRIVATE);
// 构造函数
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);
constructorMethodBuilder.addStatement("interceptors = new $T<>()", ClassName.get("java.util", "TreeMap"));
// 2. 解析到所有的 Action 信息
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Interceptor.class);
Map interceptors = new HashMap<>(elements.size());
for (Element element : elements) {
// 获取注解上面的 priority
Interceptor interceptorAnnotation = element.getAnnotation(Interceptor.class);
int priority = interceptorAnnotation.priority();
// 获取 Interceptor 的 ClassName
Element enclosingElement = element.getEnclosingElement();
String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
String interceptorClassName = packageName + "." + element.getSimpleName();
// 判断 Interceptor 注解类是否实现了 ActionInterceptor
if (!((TypeElement) element).getInterfaces().contains(iInterceptor)) {
error(element, "%s verify failed, @Interceptor must be implements %s", element.getSimpleName().toString(), Consts.ACTIONINTERCEPTOR);
}
if (interceptors.containsKey(priority)) {
// 输出错误,拦截器优先级 冲突重复了
error(element, "More than one interceptors use same priority <%s> , The last interceptor was <%s>", String.valueOf(priority), interceptors.get(priority));
}
// 添加到集合
interceptors.put(priority, interceptorClassName);
constructorMethodBuilder.addStatement("this.interceptors.put(" + priority + ",new $T())", ClassName.bestGuess(interceptorClassName));
}
// 实现方法
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("getInterceptors")
.addAnnotation(Override.class)
.returns(List.class)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
unbindMethodBuilder.addStatement("return $T.getInterceptorClasses(interceptors)", ClassName.get("com.drouter.api.utils", "MapUtils"));
classBuilder.addMethod(constructorMethodBuilder.build());
classBuilder.addMethod(unbindMethodBuilder.build());
// 生成类,看下效果
try {
JavaFile.builder(Consts.ROUTER_INTERCEPTOR_PACK_NAME, classBuilder.build())
.addFileComment("DRouter 自动生成")
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
System.out.println("翻车了!");
}
return false;
}
private boolean isNotEmpty(Map options) {
return options != null && !options.isEmpty();
}
private void error(Element element, String message, String... args) {
printMessage(Diagnostic.Kind.ERROR, element, message, args);
}
private void printMessage(Diagnostic.Kind kind, Element element, String message, Object[] args) {
if (args.length > 0) {
message = String.format(message, args);
}
processingEnv.getMessager().printMessage(kind, message, element);
}
// 1. 指定处理的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 2. 给到需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
private Set> getSupportedAnnotations() {
Set> annotations = new LinkedHashSet<>();
// 需要解析的自定义注解 BindView OnClick
annotations.add(Interceptor.class);
return annotations;
}
}
================================================
FILE: drouter-compiler/src/main/java/com/drouter/compiler/ModuleProcessor.java
================================================
package com.drouter.compiler;
import com.drouter.base.annotation.Action;
import com.drouter.compiler.util.TextUtils;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
/**
* description:
* author: Darren on 2018/1/22 12:29
* email: 240336124@qq.com
* version: 1.0
*/
@AutoService(Processor.class)
public class ModuleProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Filer mFiler;
private final String KEY_MODULE_NAME = "moduleName";
private TypeMirror iRouterAction = null;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
mElementUtils = processingEnvironment.getElementUtils();
iRouterAction = mElementUtils.getTypeElement(Consts.ROUTERACTION).asType();
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 1. 有没配置 modelName 防止 class 类冲突
String moduleName = "";
Map options = processingEnv.getOptions();
if (isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
}
System.out.println("moduleName = " + moduleName);
if (!TextUtils.isEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
} else {
String errorMessage = "These no module name, at 'build.gradle', like :\n" +
"apt {\n" +
" arguments {\n" +
" moduleName project.getName();\n" +
" }\n" +
"}\n";
throw new RuntimeException("DRouter::Compiler >>> No module name, for more information, look at gradle log.\n" + errorMessage);
}
// 3. 生成 Java 类,效果如下
/*public class DRouter$$Assist implements IRouterAssist {
Map modules = new HashMap<>();
public DRouter$$Assist() {
modules.put("login/module", "com.login.module.LoginModule");
}
@Override
public String findModuleClassName(String moduleName) {
return modules.get(moduleName);
}
}*/
// 生成类继承和实现接口
ClassName routerAssistClassName = ClassName.get("com.drouter.api.action", "IRouterModule");
ClassName mapClassName = ClassName.get("java.util", "Map");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("DRouter$$Module$$" + moduleName)
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addSuperinterface(routerAssistClassName)
.addField(mapClassName, "actions", Modifier.PRIVATE);
// 构造函数
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);
constructorMethodBuilder.addStatement("actions = new $T<>()", ClassName.bestGuess("java.util.HashMap"));
// 2. 解析到所有的 Action 信息
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Action.class);
Map modules = new HashMap<>(elements.size());
ClassName actionWrapperClassName = ClassName.get("com.drouter.api.extra", "ActionWrapper");
ClassName threadModeClassName = ClassName.get("com.drouter.base", "ThreadMode");
for (Element element : elements) {
// 获取注解上面的 action
Action actionAnnotation = element.getAnnotation(Action.class);
String actionName = actionAnnotation.path();
// 必须以配置的 gradle 包名开头
if (!actionName.startsWith(moduleName + "/")) {
error(element, "Path name of the action must begin with %s%s", moduleName, "/");
}
// 获取 Action 的 ClassName
Element enclosingElement = element.getEnclosingElement();
String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
String actionClassName = packageName + "." + element.getSimpleName();
// 判断 Interceptor 注解类是否实现了 ActionInterceptor
if (!((TypeElement) element).getInterfaces().contains(iRouterAction)) {
error(element, "%s verify failed, @Action must be implements %s", element.getSimpleName().toString(), Consts.ROUTERACTION);
}
if (modules.containsKey(actionName)) {
// 输出错误,Action 名称冲突重复了
error(element, "%s module name already exists", actionName);
}
// 添加到集合
modules.put(actionName, actionClassName);
constructorMethodBuilder.addStatement("this.actions.put($S,$T.build($T.class, $S, "
+ actionAnnotation.extraProcess() + ", $T." + actionAnnotation.threadMode() + "))",
actionName, actionWrapperClassName, ClassName.bestGuess(actionClassName), actionName, threadModeClassName);
}
// 实现方法
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("findAction")
.addParameter(String.class, "actionName")
.addAnnotation(Override.class)
.returns(actionWrapperClassName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
unbindMethodBuilder.addStatement("return (ActionWrapper)actions.get(actionName)");
classBuilder.addMethod(constructorMethodBuilder.build());
classBuilder.addMethod(unbindMethodBuilder.build());
// 生成类,看下效果
try {
JavaFile.builder(Consts.ROUTER_MODULE_PACK_NAME, classBuilder.build())
.addFileComment("DRouter 自动生成")
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
System.out.println("翻车了!");
}
return false;
}
private boolean isNotEmpty(Map options) {
return options != null && !options.isEmpty();
}
private void error(Element element, String message, String... args) {
printMessage(Diagnostic.Kind.ERROR, element, message, args);
}
private void printMessage(Diagnostic.Kind kind, Element element, String message, Object[] args) {
if (args.length > 0) {
message = String.format(message, args);
}
processingEnv.getMessager().printMessage(kind, message, element);
}
// 1. 指定处理的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 2. 给到需要处理的注解
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
private Set> getSupportedAnnotations() {
Set> annotations = new LinkedHashSet<>();
// 需要解析的自定义注解 BindView OnClick
annotations.add(Action.class);
return annotations;
}
}
================================================
FILE: drouter-compiler/src/main/java/com/drouter/compiler/util/TextUtils.java
================================================
package com.drouter.compiler.util;
/**
* description:
* author: Darren on 2018/1/22 17:45
* email: 240336124@qq.com
* version: 1.0
*/
public class TextUtils {
public static boolean isEmpty(String moduleName) {
return moduleName == null || moduleName.isEmpty();
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Jan 22 09:42:50 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: login-module/.gitignore
================================================
/build
================================================
FILE: login-module/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
repositories {
mavenCentral()
}
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: "login"]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
compile project(':base-core')
annotationProcessor project(':drouter-compiler')
}
================================================
FILE: login-module/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: login-module/src/androidTest/java/com/login/module/ExampleInstrumentedTest.java
================================================
package com.login.module;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.login.module.test", appContext.getPackageName());
}
}
================================================
FILE: login-module/src/main/AndroidManifest.xml
================================================
================================================
FILE: login-module/src/main/java/com/login/module/LoginAction.java
================================================
package com.login.module;
import android.content.Context;
import android.content.Intent;
import com.drouter.api.action.IRouterAction;
import com.drouter.api.result.RouterResult;
import com.drouter.base.ThreadMode;
import com.drouter.base.annotation.Action;
import java.util.Map;
/**
* description:
* author: Darren on 2018/1/22 10:57
* email: 240336124@qq.com
* version: 1.0
*/
@Action(path = "login/action", threadMode = ThreadMode.MAIN)
public class LoginAction implements IRouterAction {
@Override
public RouterResult invokeAction(Context context, Map requestData) {
Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra("key", (String) requestData.get("key"));
context.startActivity(intent);
return new RouterResult.Builder().success().object(100).build();
}
}
================================================
FILE: login-module/src/main/java/com/login/module/LoginActivity.java
================================================
package com.login.module;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.drouter.api.core.DRouter;
import com.drouter.api.result.ActionCallback;
import com.drouter.api.result.RouterResult;
/**
* description:
* author: Darren on 2018/1/22 15:08
* email: 240336124@qq.com
* version: 1.0
*/
public class LoginActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
String key = getIntent().getStringExtra("key");
Toast.makeText(this, key, Toast.LENGTH_LONG).show();
}
public void click(View view){
DRouter.getInstance()
.action("circlemodule/test")
.context(this)
.param("key", "value")
.invokeAction(new ActionCallback() {
@Override
public void onInterrupt() {
Log.e("TAG", "被拦截了");
}
@Override
public void onResult(RouterResult result) {
Log.e("TAG", "result = " + result.toString());
}
});
}
}
================================================
FILE: login-module/src/main/res/layout/activity_login.xml
================================================
================================================
FILE: login-module/src/main/res/values/strings.xml
================================================
login-module
================================================
FILE: login-module/src/test/java/com/login/module/ExampleUnitTest.java
================================================
package com.login.module;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: settings.gradle
================================================
include ':app', ':drouter-api', ':login-module', ':base-core', ':drouter-compiler', ':drouter-base', ':circle-module'