Repository: Xiasm/EasyRouter
Branch: master
Commit: 2b06de13e0a4
Files: 114
Total size: 137.0 KB
Directory structure:
gitextract_n0f08trr/
├── .gitignore
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xsm/
│ │ └── easyrouter/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xsm/
│ │ │ └── easyrouter/
│ │ │ ├── BussinessInterceptor.java
│ │ │ ├── Main2Activity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── ShowActivity.java
│ │ │ └── app/
│ │ │ └── MyApplication.java
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ ├── activity_main.xml
│ │ │ ├── activity_main2.xml
│ │ │ └── activity_show.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xsm/
│ └── easyrouter/
│ └── ExampleUnitTest.java
├── base/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xsm/
│ │ └── base/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xsm/
│ │ │ └── base/
│ │ │ └── providers/
│ │ │ └── module1/
│ │ │ └── Module1Providers.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xsm/
│ └── base/
│ └── ExampleUnitTest.java
├── build.gradle
├── config.gradle
├── easy-annotation/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── xsm/
│ └── easy/
│ └── annotation/
│ ├── Extra.java
│ ├── Interceptor.java
│ ├── Route.java
│ └── modle/
│ └── RouteMeta.java
├── easy-compiler/
│ ├── .gitignore
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── xsm/
│ └── easy/
│ └── compiler/
│ ├── processor/
│ │ ├── ExtraProcessor.java
│ │ ├── InterceptorProcessor.java
│ │ └── RouterProcessor.java
│ └── utils/
│ ├── Constant.java
│ ├── LoadExtraBuilder.java
│ ├── Log.java
│ └── Utils.java
├── easy-core/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xsm/
│ │ └── easy/
│ │ └── core/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xsm/
│ │ │ └── easy/
│ │ │ └── core/
│ │ │ ├── EasyRouter.java
│ │ │ ├── ExtraManager.java
│ │ │ ├── Postcard.java
│ │ │ ├── Warehouse.java
│ │ │ ├── callback/
│ │ │ │ ├── InterceptorCallback.java
│ │ │ │ └── NavigationCallback.java
│ │ │ ├── exception/
│ │ │ │ └── NoRouteFoundException.java
│ │ │ ├── implments/
│ │ │ │ └── InterceptorImpl.java
│ │ │ ├── template/
│ │ │ │ ├── IExtra.java
│ │ │ │ ├── IInterceptor.java
│ │ │ │ ├── IInterceptorGroup.java
│ │ │ │ ├── IRouteGroup.java
│ │ │ │ ├── IRouteRoot.java
│ │ │ │ └── IService.java
│ │ │ ├── thread/
│ │ │ │ └── DefaultPoolExecutor.java
│ │ │ └── utils/
│ │ │ ├── CancelableCountDownLatch.java
│ │ │ ├── ClassUtils.java
│ │ │ ├── UniqueKeyTreeMap.java
│ │ │ └── Utils.java
│ │ └── res/
│ │ └── values/
│ │ └── strings.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xsm/
│ └── easy/
│ └── core/
│ └── ExampleUnitTest.java
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── module1/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xsm/
│ │ └── module1/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xsm/
│ │ │ └── module1/
│ │ │ ├── LoginInterceptor.java
│ │ │ ├── Module1MainActivity.java
│ │ │ └── Module1ProvidersImpl.java
│ │ ├── module/
│ │ │ └── AndroidManifest.xml
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ └── activity_module1_main.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xsm/
│ └── module1/
│ └── ExampleUnitTest.java
├── module2/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xsm/
│ │ └── module2/
│ │ └── ExampleInstrumentedTest.java
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xsm/
│ │ │ └── module2/
│ │ │ └── Module2MainActivity.java
│ │ ├── module/
│ │ │ └── AndroidManifest.xml
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_launcher_background.xml
│ │ ├── drawable-v24/
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── layout/
│ │ │ └── activity_module2_main.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xsm/
│ └── module2/
│ └── ExampleUnitTest.java
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Intellij IDEA
.idea/
*.iml
*.ipr
*.iws
out/
# Gradle build folder
build/
.gradle/
# Android
local.properties
bin/
gen/
# JNI compile files
*.o
*.o.d
# OS autogen folder information
.DS_Store
Thumbs.db
# Temp files
*.bak
*.tmp
*.temp
*.swp
*.*~
~*.*
# Eclipse project files
.classpath
.settings/
.project
#Android 2.2 C/C++ compile
.externalNativeBuild/
================================================
FILE: README.md
================================================
### 简介
路由在项目中用了很长一段时间了,一直用的ARouter,很早的时候,我就把ARouter里里外外研究了一番,可以说里面干货多多,但时间长了就有些记不住了,甚至一些技术点都记的混淆了。
于是便萌生了写一篇技术博客的想法,后来又觉着干巴巴的写一篇文章不足以让我认识的更深刻,就想着可否按照ARouter的思想自己实现一个简单的路由框架呢?
后来,在我不断地学习推动以及参考众多资料的情况下,耗费了几个月的业余时间,终于完成了这个项目。
### 这个项目对你有什么帮助?
通常看来,我要理解ARouter,就要去读ARouter的源码,这当然是不错的方法,但需要花费很多的时间去理解源码,而这个项目能给你的好处是一步步有条理的指导你如何去设计路由框架、一些技术点的作用及为什么要用这些技术等等,并且重点放在讲解,直到最后设计出来一款自己的路由框架,所以说呢,干货多多,简要列举几点:
* 首先,你可以学习到如何搭建组件化架构
* 其次,你可以学习到apt、javapoet等框架开发利器
* 再者,你可以手动实现自己的依赖注入框架
* 最后,像instantrun处理、框架设计等等
### 项目理解推荐
既然你来了,我想我们的目的很明确,就是要从根本上理解路由。所以我推荐大家把demo clone下来在电脑上跑一遍,然后边浏览WIKI的文档边读代码,这样才能快速的理解设计的思想。
[文档指导,点击跳转wiki](https://github.com/Xiasm/EasyRouter/wiki)
##### 目录:
[一、从组件化引入路由设计需要满足的条件](https://github.com/Xiasm/EasyRouter/wiki/%E4%BB%8E%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%95%E5%85%A5%E8%B7%AF%E7%94%B1%E8%AE%BE%E8%AE%A1%E9%9C%80%E8%A6%81%E6%BB%A1%E8%B6%B3%E7%9A%84%E6%9D%A1%E4%BB%B6)
[二、通过Route注解去探究如何实现路由跳转](https://github.com/Xiasm/EasyRouter/wiki/%E9%80%9A%E8%BF%87Route%E6%B3%A8%E8%A7%A3%E5%8E%BB%E6%8E%A2%E7%A9%B6%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%B7%AF%E7%94%B1%E8%B7%B3%E8%BD%AC)
[三、利用apt和javapoet生成路由映射文件](https://github.com/Xiasm/EasyRouter/wiki/%E5%88%A9%E7%94%A8apt%E5%92%8Cjavapoet%E7%94%9F%E6%88%90%E8%B7%AF%E7%94%B1%E6%98%A0%E5%B0%84%E6%96%87%E4%BB%B6)
[四、框架的设计](https://github.com/Xiasm/EasyRouter/wiki/%E6%A1%86%E6%9E%B6%E7%9A%84%E8%AE%BE%E8%AE%A1)
[五、框架的初始化](https://github.com/Xiasm/EasyRouter/wiki/%E6%A1%86%E6%9E%B6%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96)
[六、实现路由跳转](https://github.com/Xiasm/EasyRouter/wiki/%E5%AE%9E%E7%8E%B0%E8%B7%AF%E7%94%B1%E8%B7%B3%E8%BD%AC)
[七、为什么需要依赖注入](https://github.com/Xiasm/EasyRouter/wiki/%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5)
[八、Activity的属性注入](https://github.com/Xiasm/EasyRouter/wiki/Activity%E7%9A%84%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5)
### 联系我
email:xiasem@163.com & devxiasm@gmail.com
微信:xsm0824mn003
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
def cfg = rootProject.ext.android
def appId = rootProject.ext.appId
android {
compileSdkVersion 26
defaultConfig {
applicationId appId["app"]
minSdkVersion cfg.minSdkVersion
targetSdkVersion cfg.targetSdkVersion
versionCode cfg.versionCode
versionName cfg.versionName
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.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'
annotationProcessor project(':easy-compiler')
implementation project(':base')
if (isModule) {
implementation project(':module1')
implementation project(':module2')
}
}
================================================
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/xsm/easyrouter/ExampleInstrumentedTest.java
================================================
package com.xsm.easyrouter;
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.xsm.easyrouter", appContext.getPackageName());
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/xsm/easyrouter/BussinessInterceptor.java
================================================
package com.xsm.easyrouter;
import android.content.Context;
import android.util.Log;
import com.xsm.easy.annotation.Interceptor;
import com.xsm.easy.core.Postcard;
import com.xsm.easy.core.callback.InterceptorCallback;
import com.xsm.easy.core.template.IInterceptor;
/**
* @author: luoxiaohui
* @date: 2019-06-18 18:02
* @desc:
*/
@Interceptor(priority = 2, name = "test")
public class BussinessInterceptor implements IInterceptor {
private static final String TAG = "BussinessInterceptor";
/**
* 拦截器流程
*
* @param postcard
* @param callback
* @author luoxiaohui
* @createTime 2019-05-23 20:53
*/
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
Log.e(TAG, "process()...");
callback.onNext(postcard);
}
/**
* 在调用EasyRouter.init()初始化时,会调用到此方法
*
* @param context
* @author luoxiaohui
* @createTime 2019-06-18 10:39
*/
@Override
public void init(Context context) {
Log.e(TAG, "init()...");
}
}
================================================
FILE: app/src/main/java/com/xsm/easyrouter/Main2Activity.java
================================================
package com.xsm.easyrouter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.xsm.easy.annotation.Route;
@Route(path = "/main/main2")
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
}
================================================
FILE: app/src/main/java/com/xsm/easyrouter/MainActivity.java
================================================
package com.xsm.easyrouter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.xsm.base.providers.module1.Module1Providers;
import com.xsm.easy.annotation.Route;
import com.xsm.easy.core.EasyRouter;
import com.xsm.easy.core.Postcard;
import com.xsm.easy.core.callback.NavigationCallback;
@Route(path = "/main/main")
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Module1Providers module1Providers;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initProviders();
}
private void initProviders() {
module1Providers = (Module1Providers) EasyRouter.getsInstance().build("/module1/providers").navigation();
}
public void startModule1MainActivity(View view) {
// EasyRouter.getsInstance().build("/module1/module1main").navigation();
EasyRouter.getsInstance().build("/module1/module1main")
.withString("msg", "从MainActivity").navigation();
}
public void startModule2MainActivity(View view) {
EasyRouter.getsInstance().build("/module2/module2main").navigation(this, new NavigationCallback() {
@Override
public void onFound(Postcard postcard) {
}
@Override
public void onLost(Postcard postcard) {
}
@Override
public void onArrival(Postcard postcard) {
}
@Override
public void onInterrupt(Throwable throwable) {
Log.e(TAG, throwable.getMessage());
}
});
}
public void add(View view) {
int num = module1Providers.add(5, 6);
Toast.makeText(this, "5+6=" + num, Toast.LENGTH_SHORT).show();
}
}
================================================
FILE: app/src/main/java/com/xsm/easyrouter/ShowActivity.java
================================================
package com.xsm.easyrouter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.xsm.easy.annotation.Route;
@Route(path = "/show/info")
public class ShowActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show);
}
}
================================================
FILE: app/src/main/java/com/xsm/easyrouter/app/MyApplication.java
================================================
package com.xsm.easyrouter.app;
import android.app.Application;
import com.xsm.easy.core.EasyRouter;
/**
* Author: 夏胜明
* Date: 2018/7/30 0030
* Email: xiasem@163.com
* Description:
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
EasyRouter.init(this);
}
}
================================================
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/layout/activity_main2.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_show.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
================================================
EasyRouter
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/com/xsm/easyrouter/ExampleUnitTest.java
================================================
package com.xsm.easyrouter;
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: base/.gitignore
================================================
/build
================================================
FILE: base/build.gradle
================================================
apply plugin: 'com.android.library'
def cfg = rootProject.ext.android
def librarys = rootProject.ext.dependencies
android {
compileSdkVersion cfg.compileSdkVersion
defaultConfig {
minSdkVersion cfg.minSdkVersion
targetSdkVersion cfg.targetSdkVersion
versionCode cfg.versionCode
versionName cfg.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
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'
librarys.each { k, v -> api v }
api project(':easy-core')
}
================================================
FILE: base/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/src/androidTest/java/com/xsm/base/ExampleInstrumentedTest.java
================================================
package com.xsm.base;
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.xsm.base.test", appContext.getPackageName());
}
}
================================================
FILE: base/src/main/AndroidManifest.xml
================================================
================================================
FILE: base/src/main/java/com/xsm/base/providers/module1/Module1Providers.java
================================================
package com.xsm.base.providers.module1;
import com.xsm.easy.core.template.IService;
/**
* Author: 夏胜明
* Date: 2018/8/20 0020
* Email: xiasem@163.com
* Description:
*/
public interface Module1Providers extends IService {
int add(int a, int b);
}
================================================
FILE: base/src/main/res/values/strings.xml
================================================
base
================================================
FILE: base/src/test/java/com/xsm/base/ExampleUnitTest.java
================================================
package com.xsm.base;
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.
//相当于引入头文件 将 config中的内容引入进来
apply from: "config.gradle"
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
// 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: config.gradle
================================================
ext {
//true 集成模式 false 组件模式
isModule = true
android = [
compileSdkVersion:26,
minSdkVersion :14,
targetSdkVersion :26,
versionCode :1,
versionName :"1.0"
]
appId = [
"app":"com.xsm.easyrouter",
"module1":"com.xsm.module1",
"module2":"com.xsm.module2"
]
supportLibrary = "26.1.0"
dependencies = [
"appcompat-v7":"com.android.support:appcompat-v7:${supportLibrary}"
]
}
================================================
FILE: easy-annotation/.gitignore
================================================
/build
================================================
FILE: easy-annotation/build.gradle
================================================
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
================================================
FILE: easy-annotation/src/main/java/com/xsm/easy/annotation/Extra.java
================================================
package com.xsm.easy.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Author: 夏胜明
* Date: 2018/8/20 0020
* Email: xiasem@163.com
* Description:
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Extra {
String name() default "";
}
================================================
FILE: easy-annotation/src/main/java/com/xsm/easy/annotation/Interceptor.java
================================================
package com.xsm.easy.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: luoxiaohui
* @date: 2019-05-23 20:08
* @desc:
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* 拦截器优先级
* @author luoxiaohui
* @createTime 2019-06-04 20:51
*/
int priority();
/**
* 拦截器的名称
* @author luoxiaohui
* @createTime 2019-05-23 20:33
*/
String name() default "";
}
================================================
FILE: easy-annotation/src/main/java/com/xsm/easy/annotation/Route.java
================================================
package com.xsm.easy.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* 路由的路径
* @return
*/
String path();
/**
* 将路由节点进行分组,可以实现动态加载
* @return
*/
String group() default "";
}
================================================
FILE: easy-annotation/src/main/java/com/xsm/easy/annotation/modle/RouteMeta.java
================================================
package com.xsm.easy.annotation.modle;
import com.xsm.easy.annotation.Route;
import javax.lang.model.element.Element;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public class RouteMeta {
public enum Type {
ACTIVITY,ISERVICE
}
private Type type;
/**
* 节点(Activity)
*/
private Element element;
/**
* 注解使用的类对象
*/
private Class> destination;
/**
* 路由地址
*/
private String path;
/**
* 路由组
*/
private String group;
public static RouteMeta build(Type type, Class> destination, String path, String
group) {
return new RouteMeta(type, null, destination, path, group);
}
public RouteMeta() {
}
public RouteMeta(Type type, Route route, Element element) {
this(type, element, null, route.path(), route.group());
}
public RouteMeta(Type type, Element element, Class> destination, String path, String
group) {
this.type = type;
this.destination = destination;
this.element = element;
this.path = path;
this.group = group;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Element getElement() {
return element;
}
public void setElement(Element element) {
this.element = element;
}
public Class> getDestination() {
return destination;
}
public void setDestination(Class> destination) {
this.destination = destination;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}
================================================
FILE: easy-compiler/.gitignore
================================================
/build
================================================
FILE: easy-compiler/build.gradle
================================================
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc2'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
implementation 'com.squareup:javapoet:1.7.0'
implementation project(':easy-annotation')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/processor/ExtraProcessor.java
================================================
package com.xsm.easy.compiler.processor;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.xsm.easy.annotation.Extra;
import com.xsm.easy.compiler.utils.Constant;
import com.xsm.easy.compiler.utils.LoadExtraBuilder;
import com.xsm.easy.compiler.utils.Log;
import com.xsm.easy.compiler.utils.Utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
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.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* Author: 夏胜明
* Date: 2018/8/20 0020
* Email: xiasem@163.com
* Description:
*/
@AutoService(Processor.class)
@SupportedOptions(Constant.ARGUMENTS_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Constant.ANN_TYPE_EXTRA})
public class ExtraProcessor extends AbstractProcessor {
/**
* 节点工具类 (类、函数、属性都是节点)
*/
private Elements elementUtils;
/**
* type(类信息)工具类
*/
private Types typeUtils;
/**
* 类/资源生成器
*/
private Filer filerUtils;
/**
* 记录所有需要注入的属性 key:类节点 value:需要注入的属性节点集合
*/
private Map> parentAndChild = new HashMap<>();
private Log log;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//获得apt的日志输出
log = Log.newLog(processingEnvironment.getMessager());
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
filerUtils = processingEnv.getFiler();
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (!Utils.isEmpty(set)) {
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Extra.class);
if (!Utils.isEmpty(elements)) {
try {
categories(elements);
generateAutoWired();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
return false;
}
private void generateAutoWired() throws IOException {
TypeMirror typeActivity = elementUtils.getTypeElement(Constant.ACTIVITY).asType();
TypeElement iExtra = elementUtils.getTypeElement(Constant.IEXTRA);
if (!Utils.isEmpty(parentAndChild)) {
// 参数 Object target
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
for (Map.Entry> entry : parentAndChild.entrySet()) {
TypeElement rawClassElement = entry.getKey();
if (!typeUtils.isSubtype(rawClassElement.asType(), typeActivity)) {
throw new RuntimeException("just support activity filed: " + rawClassElement);
}
//封装的函数生成类
LoadExtraBuilder loadExtra = new LoadExtraBuilder(objectParamSpec);
loadExtra.setElementUtils(elementUtils);
loadExtra.setTypeUtils(typeUtils);
ClassName className = ClassName.get(rawClassElement);
loadExtra.injectTarget(className);
//遍历属性
for (int i = 0; i < entry.getValue().size(); i++) {
Element element = entry.getValue().get(i);
loadExtra.buildStatement(element);
}
// 生成java类名
String extraClassName = rawClassElement.getSimpleName() + Constant.NAME_OF_EXTRA;
// 生成 XX$$Autowired
JavaFile.builder(className.packageName(), TypeSpec.classBuilder(extraClassName)
.addSuperinterface(ClassName.get(iExtra))
.addModifiers(PUBLIC).addMethod(loadExtra.build()).build())
.build().writeTo(filerUtils);
log.i("Generated Extra: " + className.packageName() + "." + extraClassName);
}
}
}
/**
* 记录需要生成的类与属性
*
* @param elements
* @throws IllegalAccessException
*/
private void categories(Set extends Element> elements) {
for (Element element : elements) {
//获得父节点 (类)
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
if (parentAndChild.containsKey(enclosingElement)) {
parentAndChild.get(enclosingElement).add(element);
} else {
List childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
}
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/processor/InterceptorProcessor.java
================================================
package com.xsm.easy.compiler.processor;
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.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import com.xsm.easy.annotation.Interceptor;
import com.xsm.easy.compiler.utils.Constant;
import com.xsm.easy.compiler.utils.Log;
import com.xsm.easy.compiler.utils.Utils;
import java.io.IOException;
import java.util.HashMap;
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.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
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.lang.model.util.Types;
/**
* @author: luoxiaohui
* @date: 2019-05-23 19:48
* @desc: 拦截器
*/
@AutoService(Processor.class)
@SupportedOptions(Constant.ARGUMENTS_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes(Constant.ANNOTATION_TYPE_INTERCEPTOR)
public class InterceptorProcessor extends AbstractProcessor {
private Map interceptors = new HashMap<>();
/**
* 节点工具类 (类、函数、属性都是节点)
*/
private Elements elementUtils;
/**
* type(类信息)工具类
*/
private Types typeUtils;
/**
* 文件生成器 类/资源
*/
private Filer filerUtils;
private TypeMirror iInterceptor;
private Log log;
private String moduleName = "";
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
log = Log.newLog(processingEnv.getMessager());
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
filerUtils = processingEnv.getFiler();
iInterceptor = elementUtils.getTypeElement(Constant.IINTERCEPTOR).asType();
Map options = processingEnv.getOptions();
if (!Utils.isEmpty(options)) {
moduleName = options.get(Constant.ARGUMENTS_NAME);
}
}
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!Utils.isEmpty(annotations)) {
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
try {
parseInterceptor(elements);
} catch (Exception e) {
log.i(e.getMessage());
}
return true;
}
return false;
}
/**
* 解析拦截器
*
* @author luoxiaohui
* @createTime 2019-05-23 20:12
*/
private void parseInterceptor(Set extends Element> elements) throws IOException {
if (!Utils.isEmpty(elements)) {
for (Element element : elements) {
if (verify(element)) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
interceptors.put(interceptor.priority(), element);
}
}
TypeElement iInterceptor = elementUtils.getTypeElement(Constant.IINTERCEPTOR);
TypeElement iInterceptorGroup = elementUtils.getTypeElement(Constant.IINTERCEPTOR_GROUP);
/**
* Map>>
*/
ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(iInterceptor))
)
);
/**
* 参数+变量名
* Map> interceptors
*/
ParameterSpec parameterSpec = ParameterSpec.builder(parameterizedTypeName, "interceptors").build();
/**
* 构建方法
* public void loadInto(Map> interceptors){}
*/
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterSpec);
if (!interceptors.isEmpty() && interceptors.size() > 0) {
/**
* 构建方法体中的语句
*/
for (Map.Entry entry : interceptors.entrySet()) {
methodBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)",
ClassName.get((TypeElement) entry.getValue()));
}
}
/**
* 将文件写入磁盘中
* 路径是在app/build/source/api/debug/PACKAGE_OF_GENERATE_FILE下面
*/
JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(Constant.NAME_OF_INTERCEPTOR + moduleName)
.addModifiers(Modifier.PUBLIC)
.addMethod(methodBuilder.build())
.addSuperinterface(ClassName.get(iInterceptorGroup))
.build()
).build().writeTo(filerUtils);
}
}
/**
* 验证节点是否含有拦截器注解
*
* @author luoxiaohui
* @createTime 2019-05-23 20:21
*/
private boolean verify(Element element) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
return interceptor != null && ((TypeElement) element).getInterfaces().contains(iInterceptor);
}
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/processor/RouterProcessor.java
================================================
package com.xsm.easy.compiler.processor;
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.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import com.xsm.easy.annotation.Route;
import com.xsm.easy.annotation.modle.RouteMeta;
import com.xsm.easy.compiler.utils.Constant;
import com.xsm.easy.compiler.utils.Log;
import com.xsm.easy.compiler.utils.Utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
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.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
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.lang.model.util.Types;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
@AutoService(Processor.class)
/**
处理器接收的参数 替代 {@link AbstractProcessor#getSupportedOptions()} 函数
*/
@SupportedOptions(Constant.ARGUMENTS_NAME)
/**
* 指定使用的Java版本 替代 {@link AbstractProcessor#getSupportedSourceVersion()} 函数
*/
@SupportedSourceVersion(SourceVersion.RELEASE_7)
/**
* 注册给哪些注解的 替代 {@link AbstractProcessor#getSupportedAnnotationTypes()} 函数
*/
@SupportedAnnotationTypes(Constant.ANNOTATION_TYPE_ROUTE)
public class RouterProcessor extends AbstractProcessor {
/**
* key:组名 value:类名
*/
private Map rootMap = new TreeMap<>();
/**
* 分组 key:组名 value:对应组的路由信息
*/
private Map> groupMap = new HashMap<>();
/**
* 节点工具类 (类、函数、属性都是节点)
*/
private Elements elementUtils;
/**
* type(类信息)工具类
*/
private Types typeUtils;
/**
* 文件生成器 类/资源
*/
private Filer filerUtils;
private String moduleName;
private Log log;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//获得apt的日志输出
log = Log.newLog(processingEnvironment.getMessager());
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
filerUtils = processingEnvironment.getFiler();
//参数是模块名 为了防止多模块/组件化开发的时候 生成相同的 xx$$ROOT$$文件
Map options = processingEnvironment.getOptions();
if (!Utils.isEmpty(options)) {
moduleName = options.get(Constant.ARGUMENTS_NAME);
}
if (Utils.isEmpty(moduleName)) {
throw new RuntimeException("Not set processor moudleName option !");
}
log.i("init RouterProcessor " + moduleName + " success !");
}
/**
*
* @param set 使用了支持处理注解的节点集合
* @param roundEnvironment 表示当前或是之前的运行环境,可以通过该对象查找找到的注解。
* @return true 表示后续处理器不会再处理(已经处理)
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (!Utils.isEmpty(set)) {
//被Route注解的节点集合
Set extends Element> rootElements = roundEnvironment.getElementsAnnotatedWith(Route.class);
if (!Utils.isEmpty(rootElements)) {
processorRoute(rootElements);
}
return true;
}
return false;
}
private void processorRoute(Set extends Element> rootElements) {
//获得Activity这个类的节点信息
TypeElement activity = elementUtils.getTypeElement(Constant.ACTIVITY);
TypeElement service = elementUtils.getTypeElement(Constant.ISERVICE);
for (Element element : rootElements) {
RouteMeta routeMeta;
//类信息
TypeMirror typeMirror = element.asType();
log.i("Route class:" + typeMirror.toString());
Route route = element.getAnnotation(Route.class);
if (typeUtils.isSubtype(typeMirror, activity.asType())) {
routeMeta = new RouteMeta(RouteMeta.Type.ACTIVITY, route, element);
} else if (typeUtils.isSubtype(typeMirror, service.asType())) {
routeMeta = new RouteMeta(RouteMeta.Type.ISERVICE, route, element);
} else {
throw new RuntimeException("Just support Activity or IService Route: " + element);
}
categories(routeMeta);
}
TypeElement iRouteGroup = elementUtils.getTypeElement(Constant.IROUTE_GROUP);
TypeElement iRouteRoot = elementUtils.getTypeElement(Constant.IROUTE_ROOT);
//生成Group记录分组表
generatedGroup(iRouteGroup);
//生成Root类 作用:记录<分组,对应的Group类>
generatedRoot(iRouteRoot, iRouteGroup);
}
/**
* 生成Root类 作用:记录<分组,对应的Group类>
* @param iRouteRoot
* @param iRouteGroup
*/
private void generatedRoot(TypeElement iRouteRoot, TypeElement iRouteGroup) {
//创建参数类型 Map> routes>
//Wildcard 通配符
ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup))
));
//参数 Map> routes> routes
ParameterSpec parameter = ParameterSpec.builder(parameterizedTypeName, "routes").build();
//函数 public void loadInfo(Map> routes> routes)
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(parameter);
//函数体
for (Map.Entry entry : rootMap.entrySet()) {
methodBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(Constant.PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
//生成$Root$类
String className = Constant.NAME_OF_ROOT + moduleName;
TypeSpec typeSpec = TypeSpec.classBuilder(className)
.addSuperinterface(ClassName.get(iRouteRoot))
.addModifiers(Modifier.PUBLIC)
.addMethod(methodBuilder.build())
.build();
try {
JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE, typeSpec).build().writeTo(filerUtils);
log.i("Generated RouteRoot:" + Constant.PACKAGE_OF_GENERATE_FILE + "." + className);
} catch (IOException e) {
e.printStackTrace();
}
}
private void generatedGroup(TypeElement iRouteGroup) {
//创建参数类型 Map
ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class));
ParameterSpec altas = ParameterSpec.builder(parameterizedTypeName, "atlas").build();
for (Map.Entry> entry : groupMap.entrySet()) {
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(altas);
String groupName = entry.getKey();
List groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
//函数体的添加
methodBuilder.addStatement("atlas.put($S,$T.build($T.$L,$T.class,$S,$S))",
routeMeta.getPath(),
ClassName.get(RouteMeta.class),
ClassName.get(RouteMeta.Type.class),
routeMeta.getType(),
ClassName.get(((TypeElement) routeMeta.getElement())),
routeMeta.getPath(),
routeMeta.getGroup());
}
String groupClassName = Constant.NAME_OF_GROUP + groupName;
TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName)
.addSuperinterface(ClassName.get(iRouteGroup))
.addModifiers(Modifier.PUBLIC)
.addMethod(methodBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE, typeSpec).build();
try {
javaFile.writeTo(filerUtils);
} catch (IOException e) {
e.printStackTrace();
}
rootMap.put(groupName, groupClassName);
}
}
/**
* 检查是否配置 group 如果没有配置 则从path截取出组名
* @param routeMeta
*/
private void categories(RouteMeta routeMeta) {
if (routeVerify(routeMeta)) {
log.i("Group : " + routeMeta.getGroup() + " path=" + routeMeta.getPath());
//分组与组中的路由信息
List routeMetas = groupMap.get(routeMeta.getGroup());
if (Utils.isEmpty(routeMetas)) {
routeMetas = new ArrayList<>();
routeMetas.add(routeMeta);
groupMap.put(routeMeta.getGroup(), routeMetas);
} else {
routeMetas.add(routeMeta);
}
} else {
log.i("Group info error:" + routeMeta.getPath());
}
}
/**
* 验证path路由地址的合法性
* @param routeMeta
* @return
*/
private boolean routeVerify(RouteMeta routeMeta) {
String path = routeMeta.getPath();
String group = routeMeta.getGroup();
// 必须以 / 开头来指定路由地址
if (!path.startsWith("/")) {
return false;
}
//如果group没有设置 我们从path中获得group
if (Utils.isEmpty(group)) {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
//截取出的group还是空
if (Utils.isEmpty(defaultGroup)) {
return false;
}
routeMeta.setGroup(defaultGroup);
}
return true;
}
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/utils/Constant.java
================================================
package com.xsm.easy.compiler.utils;
import com.squareup.javapoet.ClassName;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public class Constant {
public static final ClassName ROUTER = ClassName.get("com.xsm.easy.core", "EasyRouter");
public static final String ACTIVITY = "android.app.Activity";
public static final String ISERVICE = "com.xsm.easy.core.template.IService";
public static final String ARGUMENTS_NAME = "moduleName";
public static final String ANNOTATION_TYPE_ROUTE = "com.xsm.easy.annotation.Route";
public static final String ANN_TYPE_EXTRA = "com.xsm.easy.annotation.Extra";
public static final String ANNOTATION_TYPE_INTERCEPTOR = "com.xsm.easy.annotation.Interceptor";
public static final String IROUTE_GROUP = "com.xsm.easy.core.template.IRouteGroup";
public static final String IROUTE_ROOT = "com.xsm.easy.core.template.IRouteRoot";
public static final String IEXTRA = "com.xsm.easy.core.template.IExtra";
public static final String IINTERCEPTOR = "com.xsm.easy.core.template.IInterceptor";
public static final String IINTERCEPTOR_GROUP = "com.xsm.easy.core.template.IInterceptorGroup";
public static final String SEPARATOR = "_";
public static final String PROJECT = "EaseRouter";
public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
public static final String NAME_OF_ROOT = PROJECT + SEPARATOR + "Root" + SEPARATOR;
public static final String PACKAGE_OF_GENERATE_FILE = "com.xsm.easyrouter.routes";
public static final String METHOD_LOAD_INTO = "loadInto";
public static final String METHOD_LOAD_EXTRA = "loadExtra";
public static final String PARCELABLE = "android.os.Parcelable";
private static final String LANG = "java.lang";
public static final String BYTE = LANG + ".Byte";
public static final String SHORT = LANG + ".Short";
public static final String INTEGER = LANG + ".Integer";
public static final String LONG = LANG + ".Long";
public static final String FLOAT = LANG + ".Float";
public static final String DOUBEL = LANG + ".Double";
public static final String BOOLEAN = LANG + ".Boolean";
public static final String STRING = LANG + ".String";
public static final String ARRAY = "ARRAY";
public static final String ARRAYLIST = "java.util.ArrayList";
public static final String LIST = "java.util.List";
public static final String BYTEARRAY = "byte[]";
public static final String SHORTARRAY = "short[]";
public static final String BOOLEANARRAY = "boolean[]";
public static final String CHARARRAY = "char[]";
public static final String DOUBLEARRAY = "double[]";
public static final String FLOATARRAY = "float[]";
public static final String INTARRAY = "int[]";
public static final String LONGARRAY = "long[]";
public static final String STRINGARRAY = "java.lang.String[]";
public static final String NAME_OF_EXTRA = SEPARATOR + "Extra";
public static final String NAME_OF_INTERCEPTOR = PROJECT + SEPARATOR + "Interceptor" + SEPARATOR;
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/utils/LoadExtraBuilder.java
================================================
package com.xsm.easy.compiler.utils;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.xsm.easy.annotation.Extra;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/**
* Author: 夏胜明
* Date: 2018/8/20 0020
* Email: xiasem@163.com
* Description:
*/
public class LoadExtraBuilder {
private static final String INJECT_TARGET = "$T t = ($T)target";
private MethodSpec.Builder builder;
private Elements elementUtils;
private Types typeUtils;
private TypeMirror parcelableType;
private TypeMirror iServiceType;
public LoadExtraBuilder(ParameterSpec parameterSpec) {
// 函数 public void loadExtra(Object target)
builder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_EXTRA)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterSpec);
}
public void setElementUtils(Elements elementUtils) {
this.elementUtils = elementUtils;
parcelableType = elementUtils.getTypeElement(Constant.PARCELABLE).asType();
iServiceType = elementUtils.getTypeElement(Constant.ISERVICE).asType();
}
public void setTypeUtils(Types typeUtils) {
this.typeUtils = typeUtils;
}
public void buildStatement(Element element) {
TypeMirror typeMirror = element.asType();
int type = typeMirror.getKind().ordinal();
//属性名 String text 获得text
String fieldName = element.getSimpleName().toString();
//获得注解 name值
String extraName = element.getAnnotation(Extra.class).name();
extraName = Utils.isEmpty(extraName) ? fieldName : extraName;
String defaultValue = "t." + fieldName;
String statement = defaultValue + " = t.getIntent().";
if (type == TypeKind.BOOLEAN.ordinal()) {
statement += "getBooleanExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.BYTE.ordinal()) {
statement += "getByteExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.SHORT.ordinal()) {
statement += "getShortExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.INT.ordinal()) {
statement += "getIntExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.LONG.ordinal()) {
statement += "getLongExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.CHAR.ordinal()) {
statement += "getCharExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.FLOAT.ordinal()) {
statement += "getFloatExtra($S, " + defaultValue + ")";
} else if (type == TypeKind.DOUBLE.ordinal()) {
statement += "getDoubleExtra($S, " + defaultValue + ")";
} else {
//数组类型
if (type == TypeKind.ARRAY.ordinal()) {
addArrayStatement(statement, fieldName, extraName, typeMirror, element);
} else {
//Object
addObjectStatement(statement, fieldName, extraName, typeMirror, element);
}
return;
}
builder.addStatement(statement, extraName);
}
/**
* 添加对象 String/List/Parcelable
*
* @param statement
* @param extraName
* @param typeMirror
* @param element
*/
private void addObjectStatement(String statement, String fieldName, String extraName,
TypeMirror typeMirror,
Element element) {
//Parcelable
if (typeUtils.isSubtype(typeMirror, parcelableType)) {
statement += "getParcelableExtra($S)";
} else if (typeMirror.toString().equals(Constant.STRING)) {
statement += "getStringExtra($S)";
} else if (typeUtils.isSubtype(typeMirror, iServiceType)) {
statement = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation()";
builder.addStatement(statement, TypeName.get(element.asType()), Constant.ROUTER, extraName);
return;
} else {
//List
TypeName typeName = ClassName.get(typeMirror);
//泛型
if (typeName instanceof ParameterizedTypeName) {
//list 或 arraylist
ClassName rawType = ((ParameterizedTypeName) typeName).rawType;
//泛型类型
List typeArguments = ((ParameterizedTypeName) typeName)
.typeArguments;
if (!rawType.toString().equals(Constant.ARRAYLIST) && !rawType.toString()
.equals(Constant.LIST)) {
throw new RuntimeException("Not Support Inject Type:" + typeMirror + " " +
element);
}
if (typeArguments.isEmpty() || typeArguments.size() != 1) {
throw new RuntimeException("List Must Specify Generic Type:" + typeArguments);
}
TypeName typeArgumentName = typeArguments.get(0);
TypeElement typeElement = elementUtils.getTypeElement(typeArgumentName
.toString());
// Parcelable 类型
if (typeUtils.isSubtype(typeElement.asType(), parcelableType)) {
statement += "getParcelableArrayListExtra($S)";
} else if (typeElement.asType().toString().equals(Constant.STRING)) {
statement += "getStringArrayListExtra($S)";
} else if (typeElement.asType().toString().equals(Constant.INTEGER)) {
statement += "getIntegerArrayListExtra($S)";
} else {
throw new RuntimeException("Not Support Generic Type : " + typeMirror + " " +
element);
}
} else {
throw new RuntimeException("Not Support Extra Type : " + typeMirror + " " +
element);
}
}
builder.addStatement(statement, extraName);
}
/**
* 添加数组
*
* @param statement
* @param fieldName
* @param typeMirror
* @param element
*/
private void addArrayStatement(String statement, String fieldName, String extraName, TypeMirror
typeMirror, Element element) {
//数组
switch (typeMirror.toString()) {
case Constant.BOOLEANARRAY:
statement += "getBooleanArrayExtra($S)";
break;
case Constant.INTARRAY:
statement += "getIntArrayExtra($S)";
break;
case Constant.SHORTARRAY:
statement += "getShortArrayExtra($S)";
break;
case Constant.FLOATARRAY:
statement += "getFloatArrayExtra($S)";
break;
case Constant.DOUBLEARRAY:
statement += "getDoubleArrayExtra($S)";
break;
case Constant.BYTEARRAY:
statement += "getByteArrayExtra($S)";
break;
case Constant.CHARARRAY:
statement += "getCharArrayExtra($S)";
break;
case Constant.LONGARRAY:
statement += "getLongArrayExtra($S)";
break;
case Constant.STRINGARRAY:
statement += "getStringArrayExtra($S)";
break;
default:
//Parcelable 数组
String defaultValue = "t." + fieldName;
//object数组 componentType获得object类型
ArrayTypeName arrayTypeName = (ArrayTypeName) ClassName.get(typeMirror);
TypeElement typeElement = elementUtils.getTypeElement(arrayTypeName
.componentType.toString());
//是否为 Parcelable 类型
if (!typeUtils.isSubtype(typeElement.asType(), parcelableType)) {
throw new RuntimeException("Not Support Extra Type:" + typeMirror + " " +
element);
}
statement = "$T[] " + fieldName + " = t.getIntent()" +
".getParcelableArrayExtra" +
"($S)";
builder.addStatement(statement, parcelableType, extraName);
builder.beginControlFlow("if( null != $L)", fieldName);
statement = defaultValue + " = new $T[" + fieldName + ".length]";
builder.addStatement(statement, arrayTypeName.componentType)
.beginControlFlow("for (int i = 0; i < " + fieldName + "" +
".length; " +
"i++)")
.addStatement(defaultValue + "[i] = ($T)" + fieldName + "[i]",
arrayTypeName.componentType)
.endControlFlow();
builder.endControlFlow();
return;
}
builder.addStatement(statement, extraName);
}
/**
* 加入 $T t = ($T)target
*
* @param className
*/
public void injectTarget(ClassName className) {
builder.addStatement(INJECT_TARGET, className, className);
}
public MethodSpec build() {
return builder.build();
}
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/utils/Log.java
================================================
package com.xsm.easy.compiler.utils;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public class Log {
private Messager messager;
private Log(Messager messager) {
this.messager = messager;
}
public static Log newLog(Messager messager) {
return new Log(messager);
}
public void i(String msg) {
messager.printMessage(Diagnostic.Kind.NOTE, msg);
}
}
================================================
FILE: easy-compiler/src/main/java/com/xsm/easy/compiler/utils/Utils.java
================================================
package com.xsm.easy.compiler.utils;
import java.util.Collection;
import java.util.Map;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public class Utils {
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
public static boolean isEmpty(Collection> coll) {
return coll == null || coll.isEmpty();
}
public static boolean isEmpty(final Map, ?> map) {
return map == null || map.isEmpty();
}
}
================================================
FILE: easy-core/.gitignore
================================================
/build
================================================
FILE: easy-core/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 14
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(dir: 'libs', include: ['*.jar'])
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'
// compile被废弃
// api:同compile
// implementation: 不会进行传递依赖
// 即router-annotation模块只对 本模块(router-core) 开放,
// 无法在 app 模块中使用
api project(':easy-annotation')
}
================================================
FILE: easy-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: easy-core/src/androidTest/java/com/xsm/easy/core/ExampleInstrumentedTest.java
================================================
package com.xsm.easy.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.xsm.easy.core.test", appContext.getPackageName());
}
}
================================================
FILE: easy-core/src/main/AndroidManifest.xml
================================================
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/EasyRouter.java
================================================
package com.xsm.easy.core;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.util.Log;
import com.xsm.easy.annotation.modle.RouteMeta;
import com.xsm.easy.core.callback.InterceptorCallback;
import com.xsm.easy.core.callback.NavigationCallback;
import com.xsm.easy.core.exception.NoRouteFoundException;
import com.xsm.easy.core.implments.InterceptorImpl;
import com.xsm.easy.core.template.IInterceptor;
import com.xsm.easy.core.template.IInterceptorGroup;
import com.xsm.easy.core.template.IRouteGroup;
import com.xsm.easy.core.template.IRouteRoot;
import com.xsm.easy.core.template.IService;
import com.xsm.easy.core.utils.ClassUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
/**
* Author: 夏胜明
* Date: 2018/4/3 0003
* Email: xiasem@163.com
* Description:
*/
public class EasyRouter {
private static final String TAG = "EasyRouter";
private static final String ROUTE_ROOT_PAKCAGE = "com.xsm.easyrouter.routes";
private static final String SDK_NAME = "EaseRouter";
private static final String SEPARATOR = "_";
private static final String SUFFIX_ROOT = "Root";
private static final String SUFFIX_INTERCEPTOR = "Interceptor";
private static EasyRouter sInstance;
private static Application mContext;
private Handler mHandler;
private EasyRouter() {
mHandler = new Handler(Looper.getMainLooper());
}
public static EasyRouter getsInstance() {
if (sInstance == null) {
synchronized (EasyRouter.class) {
if (sInstance == null) {
sInstance = new EasyRouter();
}
}
}
return sInstance;
}
public static void init(Application application) {
mContext = application;
try {
loadInfo();
InterceptorImpl.init(application.getApplicationContext());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "初始化失败!", e);
}
}
/**
* 分组表制作
*/
private static void loadInfo() throws PackageManager.NameNotFoundException, InterruptedException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获得所有 apt生成的路由类的全类名 (路由表)
Set routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + "." + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
//root中注册的是分组信息 将分组信息加入仓库中
((IRouteRoot) Class.forName(className).getConstructor().newInstance()).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + "." + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTOR)) {
((IInterceptorGroup) Class.forName(className).getConstructor().newInstance()).loadInto(Warehouse.interceptorsIndex);
}
}
for (Map.Entry> stringClassEntry : Warehouse.groupsIndex.entrySet()) {
Log.d(TAG, "Root映射表[ " + stringClassEntry.getKey() + " : " + stringClassEntry.getValue() + "]");
}
}
public Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new RuntimeException("路由地址无效!");
} else {
return build(path, extractGroup(path));
}
}
public Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new RuntimeException("路由地址无效!");
} else {
return new Postcard(path, group);
}
}
/**
* 获得组别
*
* @param path
* @return
*/
private String extractGroup(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new RuntimeException(path + " : 不能提取group.");
}
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new RuntimeException(path + " : 不能提取group.");
} else {
return defaultGroup;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
if (callback != null) {
InterceptorImpl.onInterceptions(postcard, new InterceptorCallback() {
@Override
public void onNext(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(String interruptMsg) {
callback.onInterrupt(new Throwable(interruptMsg));
}
});
}else{
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
protected Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
prepareCard(postcard);
} catch (NoRouteFoundException e) {
e.printStackTrace();
//没找到
if (null != callback) {
callback.onLost(postcard);
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
switch (postcard.getType()) {
case ACTIVITY:
final Context currentContext = null == context ? mContext : context;
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mHandler.post(new Runnable() {
@Override
public void run() {
//可能需要返回码
if (requestCode > 0) {
ActivityCompat.startActivityForResult((Activity) currentContext, intent,
requestCode, postcard.getOptionsBundle());
} else {
ActivityCompat.startActivity(currentContext, intent, postcard
.getOptionsBundle());
}
if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) &&
currentContext instanceof Activity) {
//老版本
((Activity) currentContext).overridePendingTransition(postcard
.getEnterAnim()
, postcard.getExitAnim());
}
//跳转完成
if (null != callback) {
callback.onArrival(postcard);
}
}
});
break;
case ISERVICE:
return postcard.getService();
default:
break;
}
return null;
}
/**
* 准备卡片
*
* @param card
*/
private void prepareCard(Postcard card) {
RouteMeta routeMeta = Warehouse.routes.get(card.getPath());
if (null == routeMeta) {
Class extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(card.getGroup());
if (null == groupMeta) {
throw new NoRouteFoundException("没找到对应路由:分组=" + card.getGroup() + " 路径=" + card.getPath());
}
IRouteGroup iGroupInstance;
try {
iGroupInstance = groupMeta.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("路由分组映射表记录失败.", e);
}
iGroupInstance.loadInto(Warehouse.routes);
//已经准备过了就可以移除了 (不会一直存在内存中)
Warehouse.groupsIndex.remove(card.getGroup());
//再次进入 else
prepareCard(card);
} else {
//类 要跳转的activity 或IService实现类
card.setDestination(routeMeta.getDestination());
card.setType(routeMeta.getType());
switch (routeMeta.getType()) {
case ISERVICE:
Class> destination = routeMeta.getDestination();
IService service = Warehouse.services.get(destination);
if (null == service) {
try {
service = (IService) destination.getConstructor().newInstance();
Warehouse.services.put(destination, service);
} catch (Exception e) {
e.printStackTrace();
}
}
card.setService(service);
break;
default:
break;
}
}
}
/**
* 注入
*
* @param instance
*/
public void inject(Activity instance) {
ExtraManager.getInstance().loadExtras(instance);
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/ExtraManager.java
================================================
package com.xsm.easy.core;
import android.app.Activity;
import android.util.LruCache;
import com.xsm.easy.core.template.IExtra;
/**
* Author: 夏胜明
* Date: 2018/4/25 0025
* Email: xiasem@163.com
* Description:
*/
public class ExtraManager {
public static final String SUFFIX_AUTOWIRED = "_Extra";
private static ExtraManager instance;
private LruCache classCache;
public static ExtraManager getInstance() {
if (instance == null) {
synchronized (ExtraManager.class) {
if (instance == null) {
instance = new ExtraManager();
}
}
}
return instance;
}
public ExtraManager() {
classCache = new LruCache<>(66);
}
/**
* 注入
*
* @param instance
*/
public void loadExtras(Activity instance) {
//查找对应activity的缓存
String className = instance.getClass().getName();
IExtra iExtra = classCache.get(className);
try {
if (null == iExtra) {
iExtra = (IExtra) Class.forName(instance.getClass().getName() +
SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
iExtra.loadExtra(instance);
classCache.put(className, iExtra);
} catch (Exception e) {
e.printStackTrace();
}
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/Postcard.java
================================================
package com.xsm.easy.core;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityOptionsCompat;
import com.xsm.easy.annotation.modle.RouteMeta;
import com.xsm.easy.core.callback.NavigationCallback;
import com.xsm.easy.core.template.IService;
import java.util.ArrayList;
/**
* Author: 夏胜明
* Date: 2018/4/24 0024
* Email: xiasem@163.com
* Description:
*/
public class Postcard extends RouteMeta {
private Bundle mBundle;
private int flags = -1;
//新版风格
private Bundle optionsCompat;
//老版
private int enterAnim;
private int exitAnim;
//服务
private IService service;
public Postcard(String path, String group) {
this(path, group, null);
}
public Postcard(String path, String group, Bundle bundle) {
setPath(path);
setGroup(group);
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
public Bundle getExtras() {return mBundle;}
public int getEnterAnim() {return enterAnim;}
public int getExitAnim() {return exitAnim;}
public IService getService() {
return service;
}
public void setService(IService service) {
this.service = service;
}
/**
* Intent.FLAG_ACTIVITY**
* @param flag
* @return
*/
public Postcard withFlags(int flag) {
this.flags = flag;
return this;
}
public int getFlags() {
return flags;
}
/**
* 跳转动画
*
* @param enterAnim
* @param exitAnim
* @return
*/
public Postcard withTransition(int enterAnim, int exitAnim) {
this.enterAnim = enterAnim;
this.exitAnim = exitAnim;
return this;
}
/**
* 转场动画
*
* @param compat
* @return
*/
public Postcard withOptionsCompat(ActivityOptionsCompat compat) {
if (null != compat) {
this.optionsCompat = compat.toBundle();
}
return this;
}
public Postcard withString(@Nullable String key, @Nullable String value) {
mBundle.putString(key, value);
return this;
}
public Postcard withBoolean(@Nullable String key, boolean value) {
mBundle.putBoolean(key, value);
return this;
}
public Postcard withShort(@Nullable String key, short value) {
mBundle.putShort(key, value);
return this;
}
public Postcard withInt(@Nullable String key, int value) {
mBundle.putInt(key, value);
return this;
}
public Postcard withLong(@Nullable String key, long value) {
mBundle.putLong(key, value);
return this;
}
public Postcard withDouble(@Nullable String key, double value) {
mBundle.putDouble(key, value);
return this;
}
public Postcard withByte(@Nullable String key, byte value) {
mBundle.putByte(key, value);
return this;
}
public Postcard withChar(@Nullable String key, char value) {
mBundle.putChar(key, value);
return this;
}
public Postcard withFloat(@Nullable String key, float value) {
mBundle.putFloat(key, value);
return this;
}
public Postcard withParcelable(@Nullable String key, @Nullable Parcelable value) {
mBundle.putParcelable(key, value);
return this;
}
public Postcard withStringArray(@Nullable String key, @Nullable String[] value) {
mBundle.putStringArray(key, value);
return this;
}
public Postcard withBooleanArray(@Nullable String key, boolean[] value) {
mBundle.putBooleanArray(key, value);
return this;
}
public Postcard withShortArray(@Nullable String key, short[] value) {
mBundle.putShortArray(key, value);
return this;
}
public Postcard withIntArray(@Nullable String key, int[] value) {
mBundle.putIntArray(key, value);
return this;
}
public Postcard withLongArray(@Nullable String key, long[] value) {
mBundle.putLongArray(key, value);
return this;
}
public Postcard withDoubleArray(@Nullable String key, double[] value) {
mBundle.putDoubleArray(key, value);
return this;
}
public Postcard withByteArray(@Nullable String key, byte[] value) {
mBundle.putByteArray(key, value);
return this;
}
public Postcard withCharArray(@Nullable String key, char[] value) {
mBundle.putCharArray(key, value);
return this;
}
public Postcard withFloatArray(@Nullable String key, float[] value) {
mBundle.putFloatArray(key, value);
return this;
}
public Postcard withParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {
mBundle.putParcelableArray(key, value);
return this;
}
public Postcard withParcelableArrayList(@Nullable String key, @Nullable ArrayList extends
Parcelable> value) {
mBundle.putParcelableArrayList(key, value);
return this;
}
public Postcard withIntegerArrayList(@Nullable String key, @Nullable ArrayList value) {
mBundle.putIntegerArrayList(key, value);
return this;
}
public Postcard withStringArrayList(@Nullable String key, @Nullable ArrayList value) {
mBundle.putStringArrayList(key, value);
return this;
}
public Bundle getOptionsBundle() {
return optionsCompat;
}
public Object navigation() {
return EasyRouter.getsInstance().navigation(null, this, -1, null);
}
public Object navigation(Context context) {
return EasyRouter.getsInstance().navigation(context, this, -1, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return EasyRouter.getsInstance().navigation(context, this, -1, callback);
}
public Object navigation(Context context, int requestCode) {
return EasyRouter.getsInstance().navigation(context, this, requestCode, null);
}
public Object navigation(Context context, int requestCode, NavigationCallback callback) {
return EasyRouter.getsInstance().navigation(context, this, requestCode, callback);
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/Warehouse.java
================================================
package com.xsm.easy.core;
import com.xsm.easy.annotation.modle.RouteMeta;
import com.xsm.easy.core.template.IInterceptor;
import com.xsm.easy.core.template.IRouteGroup;
import com.xsm.easy.core.template.IService;
import com.xsm.easy.core.utils.UniqueKeyTreeMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Author: 夏胜明
* Date: 2018/4/24 0024
* Email: xiasem@163.com
* Description:
*/
public class Warehouse {
// root 映射表 保存分组信息
static Map> groupsIndex = new HashMap<>();
// group 映射表 保存组中的所有数据
static Map routes = new HashMap<>();
// group 映射表 保存组中的所有数据
static Map services = new HashMap<>();
// TestServiceImpl.class , TestServiceImpl 没有再反射
/**
* 以键值对优先级的方式保存拦截器对象
*/
public static Map> interceptorsIndex = new UniqueKeyTreeMap<>();
/**
* 以集合的方式保存所有拦截器对象
*/
public static List interceptors = new ArrayList<>();
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/callback/InterceptorCallback.java
================================================
package com.xsm.easy.core.callback;
import com.xsm.easy.core.Postcard;
/**
* @author: luoxiaohui
* @date: 2019-05-23 20:41
* @desc: 拦截器回调
*/
public interface InterceptorCallback {
/**
* 未拦截,走正常流程
* @author luoxiaohui
* @createTime 2019-05-23 20:50
*/
void onNext(Postcard postcard);
/**
* 拦截器拦截成功,中断流程
* @author luoxiaohui
* @createTime 2019-05-23 20:42
*/
void onInterrupt(String interruptMsg);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/callback/NavigationCallback.java
================================================
package com.xsm.easy.core.callback;
import com.xsm.easy.core.Postcard;
/**
* Author: 夏胜明
* Date: 2018/4/25 0025
* Email: xiasem@163.com
* Description:
*/
public interface NavigationCallback {
/**
* 找到跳转页面
* @param postcard
*/
void onFound(Postcard postcard);
/**
* 未找到
* @param postcard
*/
void onLost(Postcard postcard);
/**
* 成功跳转
* @param postcard
*/
void onArrival(Postcard postcard);
/**
* 中断了路由跳转
* @author luoxiaohui
* @createTime 2019-06-18 17:00
*/
void onInterrupt(Throwable throwable);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/exception/NoRouteFoundException.java
================================================
package com.xsm.easy.core.exception;
/**
* Author: 夏胜明
* Date: 2018/4/25 0025
* Email: xiasem@163.com
* Description:
*/
public class NoRouteFoundException extends RuntimeException {
public NoRouteFoundException(String detailMessage) {
super(detailMessage);
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/implments/InterceptorImpl.java
================================================
package com.xsm.easy.core.implments;
import android.content.Context;
import com.xsm.easy.core.Postcard;
import com.xsm.easy.core.Warehouse;
import com.xsm.easy.core.callback.InterceptorCallback;
import com.xsm.easy.core.template.IInterceptor;
import com.xsm.easy.core.thread.DefaultPoolExecutor;
import com.xsm.easy.core.utils.CancelableCountDownLatch;
import com.xsm.easy.core.utils.Utils;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author: luoxiaohui
* @date: 2019-06-18 14:08
* @desc: 拦截器实现,在初始化路由,以及调用路由时,都需要调用到此类
*/
public class InterceptorImpl {
/**
* 初始化路由时,需要轮询每个拦截器中的init()方法
*
* @author luoxiaohui
* @createTime 2019-06-18 14:10
*/
public static void init(final Context context) {
DefaultPoolExecutor.executor.execute(new Runnable() {
@Override
public void run() {
if (!Utils.isEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry> entry : Warehouse.interceptorsIndex.entrySet()) {
Class extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
});
}
/**
* 执行拦截逻辑
*
* @author luoxiaohui
* @createTime 2019-06-18 14:56
*/
public static void onInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (Warehouse.interceptors.size() > 0) {
DefaultPoolExecutor.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch countDownLatch = new CancelableCountDownLatch(Warehouse.interceptors.size());
execute(0, countDownLatch, postcard);
try {
countDownLatch.await(300, TimeUnit.SECONDS);
if (countDownLatch.getCount() > 0){
callback.onInterrupt("拦截器处理超时");
}else if(!Utils.isEmpty(countDownLatch.getMsg())){
callback.onInterrupt(countDownLatch.getMsg());
}else {
callback.onNext(postcard);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} else {
callback.onNext(postcard);
}
}
/**
* 以递归的方式走完所有拦截器的process()方法
*
* @author luoxiaohui
* @createTime 2019-06-18 15:22
*/
private static void execute(final int index, final CancelableCountDownLatch countDownLatch, final Postcard postcard) {
if (index < Warehouse.interceptors.size()){
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onNext(Postcard postcard) {
countDownLatch.countDown();
execute(index + 1, countDownLatch, postcard);
}
@Override
public void onInterrupt(String msg) {
countDownLatch.cancel(msg);
}
});
}
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IExtra.java
================================================
package com.xsm.easy.core.template;
/**
* 注入
*/
public interface IExtra {
void loadExtra(Object target);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IInterceptor.java
================================================
package com.xsm.easy.core.template;
import android.content.Context;
import com.xsm.easy.core.Postcard;
import com.xsm.easy.core.callback.InterceptorCallback;
/**
* @author: luoxiaohui
* @date: 2019-05-23 20:52
* @desc:
*/
public interface IInterceptor {
/**
* 拦截器流程
* @author luoxiaohui
* @createTime 2019-05-23 20:53
*/
void process(Postcard postcard, InterceptorCallback callback);
/**
* 在调用EasyRouter.init()初始化时,会调用到此方法
* @author luoxiaohui
* @createTime 2019-06-18 10:39
*/
void init(Context context);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IInterceptorGroup.java
================================================
package com.xsm.easy.core.template;
import java.util.Map;
/**
* @author: luoxiaohui
* @date: 2019-05-23 20:36
* @desc:
*/
public interface IInterceptorGroup {
/**
* key为拦截器的优先级,value为拦截器
* @author luoxiaohui
* @createTime 2019-05-23 20:54
* @param map
*/
void loadInto(Map> map);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IRouteGroup.java
================================================
package com.xsm.easy.core.template;
import com.xsm.easy.annotation.modle.RouteMeta;
import java.util.Map;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public interface IRouteGroup {
void loadInto(Map atlas);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IRouteRoot.java
================================================
package com.xsm.easy.core.template;
import java.util.Map;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description:
*/
public interface IRouteRoot {
void loadInto(Map> routes);
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/template/IService.java
================================================
package com.xsm.easy.core.template;
/**
* Author: 夏胜明
* Date: 2018/3/29 0029
* Email: xiasem@163.com
* Description: 用于组件之间业务通信
*/
public interface IService {
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/thread/DefaultPoolExecutor.java
================================================
package com.xsm.easy.core.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Author: 夏胜明
* Date: 2018/4/3 0003
* Email: xiasem@163.com
* Description:
*/
public class DefaultPoolExecutor {
public static ThreadPoolExecutor executor;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "EasyRouter #" + mCount.getAndIncrement());
}
};
//核心线程和最大线程都是cpu核心数+1
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int MAX_CORE_POOL_SIZE = CPU_COUNT + 1;
//存活30秒 回收线程
private static final long SURPLUS_THREAD_LIFE = 30L;
public static ThreadPoolExecutor newDefaultPoolExecutor(int corePoolSize) {
if (corePoolSize == 0) {
return null;
}
corePoolSize = Math.min(corePoolSize, MAX_CORE_POOL_SIZE);
executor = new ThreadPoolExecutor(corePoolSize,
corePoolSize, SURPLUS_THREAD_LIFE, TimeUnit.SECONDS, new
ArrayBlockingQueue(64), sThreadFactory);
//核心线程也会被销毁
executor.allowCoreThreadTimeOut(true);
return executor;
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/utils/CancelableCountDownLatch.java
================================================
package com.xsm.easy.core.utils;
import java.util.concurrent.CountDownLatch;
/**
* @author: luoxiaohui
* @date: 2019-06-18 14:46
* @desc:
*/
public class CancelableCountDownLatch extends CountDownLatch {
private String msg = "";
public CancelableCountDownLatch(int count) {
super(count);
}
/**
* 当遇到特殊情况时,需要将计步器清0
*
* @author luoxiaohui
* @createTime 2019-06-18 14:47
*/
public void cancel(String msg) {
this.msg = msg;
while (getCount() > 0) {
countDown();
}
}
public String getMsg(){
return msg;
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/utils/ClassUtils.java
================================================
package com.xsm.easy.core.utils;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;
import com.xsm.easy.core.thread.DefaultPoolExecutor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import dalvik.system.DexFile;
/**
* Author: 夏胜明
* Date: 2018/4/3 0003
* Email: xiasem@163.com
* Description:
*/
public class ClassUtils {
/**
* 获得程序所有的apk(instant run会产生很多split apk)
* @param context
* @return
* @throws PackageManager.NameNotFoundException
*/
private static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
List sourcePaths = new ArrayList<>();
sourcePaths.add(applicationInfo.sourceDir);
//instant run
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (null != applicationInfo.splitSourceDirs) {
sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
}
}
return sourcePaths;
}
/**
* 得到路由表的类名
* @param context
* @param packageName
* @return
* @throws PackageManager.NameNotFoundException
* @throws InterruptedException
*/
public static Set getFileNameByPackageName(Application context, final String packageName)
throws PackageManager.NameNotFoundException, InterruptedException {
final Set classNames = new HashSet<>();
List paths = getSourcePaths(context);
//使用同步计数器判断均处理完成
final CountDownLatch countDownLatch = new CountDownLatch(paths.size());
ThreadPoolExecutor threadPoolExecutor = DefaultPoolExecutor.newDefaultPoolExecutor(paths.size());
for (final String path : paths) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
DexFile dexFile = null;
try {
//加载 apk中的dex 并遍历 获得所有包名为 {packageName} 的类
dexFile = new DexFile(path);
Enumeration dexEntries = dexFile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (!TextUtils.isEmpty(className) && className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != dexFile) {
try {
dexFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//释放一个
countDownLatch.countDown();
}
}
});
}
//等待执行完成
countDownLatch.await();
return classNames;
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/utils/UniqueKeyTreeMap.java
================================================
package com.xsm.easy.core.utils;
import java.util.TreeMap;
/**
* @author: luoxiaohui
* @date: 2019-06-17 15:38
* @desc: 主要用于拦截器优先级
*/
public class UniqueKeyTreeMap extends TreeMap {
@Override
public V put(K key, V value) {
if (containsKey(key)){
throw new RuntimeException("优先级为" + key + "的拦截器已经存在,不允许再次添加同级别的拦截器!");
}else{
return super.put(key, value);
}
}
}
================================================
FILE: easy-core/src/main/java/com/xsm/easy/core/utils/Utils.java
================================================
package com.xsm.easy.core.utils;
import java.util.Collection;
import java.util.Map;
/**
* @author: luoxiaohui
* @date: 2019-06-18 14:21
* @desc: 判断集合,Map等是否为空
*/
public class Utils {
public static boolean isEmpty(String str){
return str == null || str.equals("") || str.isEmpty();
}
public static boolean isEmpty(Collection> coll) {
return coll == null || coll.isEmpty();
}
public static boolean isEmpty(final Map, ?> map) {
return map == null || map.isEmpty();
}
}
================================================
FILE: easy-core/src/main/res/values/strings.xml
================================================
easy-core
================================================
FILE: easy-core/src/test/java/com/xsm/easy/core/ExampleUnitTest.java
================================================
package com.xsm.easy.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: gradle/wrapper/gradle-wrapper.properties
================================================
#Tue Mar 27 09:07:20 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.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: module1/.gitignore
================================================
/build
================================================
FILE: module1/build.gradle
================================================
if (isModule) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
def cfg = rootProject.ext.android
def appId = rootProject.ext.appId
android {
compileSdkVersion cfg.compileSdkVersion
defaultConfig {
if (!isModule) {
applicationId appId.module1
}
minSdkVersion cfg.minSdkVersion
targetSdkVersion cfg.targetSdkVersion
versionCode cfg.versionCode
versionName cfg.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
//添加一条 boolean类型的变量
buildConfigField("boolean", "isModule", String.valueOf(isModule))
sourceSets {
main {
if (isModule) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
java.srcDirs 'src/main/module/java', 'src/main/java'
}
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
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'
annotationProcessor project(':easy-compiler')
implementation project(':base')
}
================================================
FILE: module1/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: module1/src/androidTest/java/com/xsm/module1/ExampleInstrumentedTest.java
================================================
package com.xsm.module1;
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.xsm.module1", appContext.getPackageName());
}
}
================================================
FILE: module1/src/main/AndroidManifest.xml
================================================
================================================
FILE: module1/src/main/java/com/xsm/module1/LoginInterceptor.java
================================================
package com.xsm.module1;
import android.content.Context;
import android.util.Log;
import com.xsm.easy.annotation.Interceptor;
import com.xsm.easy.core.Postcard;
import com.xsm.easy.core.callback.InterceptorCallback;
import com.xsm.easy.core.template.IInterceptor;
/**
* @author: luoxiaohui
* @date: 2019-05-30 20:35
* @desc:
*/
@Interceptor(priority = 1, name = "login")
public class LoginInterceptor implements IInterceptor {
private static final String TAG = "LoginInterceptor";
/**
* 拦截器流程
*
* @param postcard
* @param callback
* @author luoxiaohui
* @createTime 2019-05-23 20:53
*/
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
Log.e(TAG, "process()...");
callback.onNext(postcard);
}
/**
* 在调用EasyRouter.init()初始化时,会调用到此方法
*
* @param context
* @author luoxiaohui
* @createTime 2019-06-18 10:39
*/
@Override
public void init(Context context) {
Log.e(TAG, "init()...");
}
}
================================================
FILE: module1/src/main/java/com/xsm/module1/Module1MainActivity.java
================================================
package com.xsm.module1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import com.xsm.easy.annotation.Extra;
import com.xsm.easy.annotation.Route;
import com.xsm.easy.core.EasyRouter;
@Route(path = "/module1/module1main")
public class Module1MainActivity extends AppCompatActivity {
@Extra
String msg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module1_main);
EasyRouter.getsInstance().inject(this);
Toast.makeText(this, "msg=" + msg, Toast.LENGTH_SHORT).show();
}
}
================================================
FILE: module1/src/main/java/com/xsm/module1/Module1ProvidersImpl.java
================================================
package com.xsm.module1;
import com.xsm.base.providers.module1.Module1Providers;
import com.xsm.easy.annotation.Route;
/**
* Author: 夏胜明
* Date: 2018/8/20 0020
* Email: xiasem@163.com
* Description:
*/
@Route(path = "/module1/providers")
public class Module1ProvidersImpl implements Module1Providers {
@Override
public int add(int a, int b) {
return a + b;
}
}
================================================
FILE: module1/src/main/module/AndroidManifest.xml
================================================
================================================
FILE: module1/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: module1/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: module1/src/main/res/layout/activity_module1_main.xml
================================================
================================================
FILE: module1/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: module1/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: module1/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
================================================
FILE: module1/src/main/res/values/strings.xml
================================================
Module1
================================================
FILE: module1/src/main/res/values/styles.xml
================================================
================================================
FILE: module1/src/test/java/com/xsm/module1/ExampleUnitTest.java
================================================
package com.xsm.module1;
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: module2/.gitignore
================================================
/build
================================================
FILE: module2/build.gradle
================================================
if (isModule) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
def cfg = rootProject.ext.android
def appId = rootProject.ext.appId
android {
compileSdkVersion cfg.compileSdkVersion
defaultConfig {
if (!isModule) {
applicationId appId.module2
}
minSdkVersion cfg.minSdkVersion
targetSdkVersion cfg.targetSdkVersion
versionCode cfg.versionCode
versionName cfg.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
//添加一条 boolean类型的变量
buildConfigField("boolean", "isModule", String.valueOf(isModule))
sourceSets {
main {
if (isModule) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
java.srcDirs 'src/main/module/java', 'src/main/java'
}
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
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'
annotationProcessor project(':easy-compiler')
implementation project(':base')
}
================================================
FILE: module2/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: module2/src/androidTest/java/com/xsm/module2/ExampleInstrumentedTest.java
================================================
package com.xsm.module2;
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.xsm.module2", appContext.getPackageName());
}
}
================================================
FILE: module2/src/main/AndroidManifest.xml
================================================
================================================
FILE: module2/src/main/java/com/xsm/module2/Module2MainActivity.java
================================================
package com.xsm.module2;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.xsm.easy.annotation.Route;
@Route(path = "/module2/module2main")
public class Module2MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module2_main);
}
}
================================================
FILE: module2/src/main/module/AndroidManifest.xml
================================================
================================================
FILE: module2/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: module2/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: module2/src/main/res/layout/activity_module2_main.xml
================================================
================================================
FILE: module2/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: module2/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: module2/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
================================================
FILE: module2/src/main/res/values/strings.xml
================================================
Module2
================================================
FILE: module2/src/main/res/values/styles.xml
================================================
================================================
FILE: module2/src/test/java/com/xsm/module2/ExampleUnitTest.java
================================================
package com.xsm.module2;
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', ':easy-compiler', ':easy-annotation', ':easy-core', ':module1', ':module2', ':base'