[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n            <option value=\"$PROJECT_DIR$/base-core\" />\n            <option value=\"$PROJECT_DIR$/circle-module\" />\n            <option value=\"$PROJECT_DIR$/drouter-api\" />\n            <option value=\"$PROJECT_DIR$/drouter-base\" />\n            <option value=\"$PROJECT_DIR$/drouter-compiler\" />\n            <option value=\"$PROJECT_DIR$/login-module\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"MarkdownProjectSettings\">\n    <PreviewSettings splitEditorLayout=\"SPLIT\" splitEditorPreview=\"PREVIEW\" useGrayscaleRendering=\"false\" zoomFactor=\"1.0\" maxImageWidth=\"0\" showGitHubPageIfSynced=\"false\" allowBrowsingInPreview=\"false\" synchronizePreviewPosition=\"true\" highlightPreviewType=\"NONE\" highlightFadeOut=\"5\" highlightOnTyping=\"true\" synchronizeSourcePosition=\"true\" verticallyAlignSourceAndPreviewSyncPosition=\"true\" showSearchHighlightsInPreview=\"false\" showSelectionInPreview=\"true\" openRemoteLinks=\"true\">\n      <PanelProvider>\n        <provider providerId=\"com.vladsch.idea.multimarkdown.editor.swing.html.panel\" providerName=\"Default - Swing\" />\n      </PanelProvider>\n    </PreviewSettings>\n    <ParserSettings gitHubSyntaxChange=\"false\">\n      <PegdownExtensions>\n        <option name=\"ABBREVIATIONS\" value=\"false\" />\n        <option name=\"ANCHORLINKS\" value=\"true\" />\n        <option name=\"ASIDE\" value=\"false\" />\n        <option name=\"ATXHEADERSPACE\" value=\"true\" />\n        <option name=\"AUTOLINKS\" value=\"true\" />\n        <option name=\"DEFINITIONS\" value=\"false\" />\n        <option name=\"DEFINITION_BREAK_DOUBLE_BLANK_LINE\" value=\"false\" />\n        <option name=\"FENCED_CODE_BLOCKS\" value=\"true\" />\n        <option name=\"FOOTNOTES\" value=\"false\" />\n        <option name=\"HARDWRAPS\" value=\"false\" />\n        <option name=\"HTML_DEEP_PARSER\" value=\"false\" />\n        <option name=\"INSERTED\" value=\"false\" />\n        <option name=\"QUOTES\" value=\"false\" />\n        <option name=\"RELAXEDHRULES\" value=\"true\" />\n        <option name=\"SMARTS\" value=\"false\" />\n        <option name=\"STRIKETHROUGH\" value=\"true\" />\n        <option name=\"SUBSCRIPT\" value=\"false\" />\n        <option name=\"SUPERSCRIPT\" value=\"false\" />\n        <option name=\"SUPPRESS_HTML_BLOCKS\" value=\"false\" />\n        <option name=\"SUPPRESS_INLINE_HTML\" value=\"false\" />\n        <option name=\"TABLES\" value=\"true\" />\n        <option name=\"TASKLISTITEMS\" value=\"true\" />\n        <option name=\"TOC\" value=\"false\" />\n        <option name=\"WIKILINKS\" value=\"true\" />\n      </PegdownExtensions>\n      <ParserOptions>\n        <option name=\"COMMONMARK_LISTS\" value=\"true\" />\n        <option name=\"DUMMY\" value=\"false\" />\n        <option name=\"EMOJI_SHORTCUTS\" value=\"true\" />\n        <option name=\"FLEXMARK_FRONT_MATTER\" value=\"false\" />\n        <option name=\"GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA\" value=\"false\" />\n        <option name=\"GFM_TABLE_RENDERING\" value=\"true\" />\n        <option name=\"GITBOOK_URL_ENCODING\" value=\"false\" />\n        <option name=\"GITHUB_EMOJI_URL\" value=\"false\" />\n        <option name=\"GITHUB_LISTS\" value=\"false\" />\n        <option name=\"GITHUB_WIKI_LINKS\" value=\"true\" />\n        <option name=\"JEKYLL_FRONT_MATTER\" value=\"false\" />\n        <option name=\"SIM_TOC_BLANK_LINE_SPACER\" value=\"true\" />\n      </ParserOptions>\n    </ParserSettings>\n    <HtmlSettings headerTopEnabled=\"false\" headerBottomEnabled=\"false\" bodyTopEnabled=\"false\" bodyBottomEnabled=\"false\" embedUrlContent=\"false\" addPageHeader=\"true\" embedImages=\"false\" embedHttpImages=\"false\">\n      <GeneratorProvider>\n        <provider providerId=\"com.vladsch.idea.multimarkdown.editor.swing.html.generator\" providerName=\"Default Swing HTML Generator\" />\n      </GeneratorProvider>\n      <headerTop />\n      <headerBottom />\n      <bodyTop />\n      <bodyBottom />\n    </HtmlSettings>\n    <CssSettings previewScheme=\"UI_SCHEME\" cssUri=\"\" isCssUriEnabled=\"false\" isCssTextEnabled=\"false\" isDynamicPageWidth=\"true\">\n      <StylesheetProvider>\n        <provider providerId=\"com.vladsch.idea.multimarkdown.editor.swing.html.css\" providerName=\"Default Swing Stylesheet\" />\n      </StylesheetProvider>\n      <ScriptProviders />\n      <cssText />\n    </CssSettings>\n    <HtmlExportSettings updateOnSave=\"false\" parentDir=\"$ProjectFileDir$\" targetDir=\"$ProjectFileDir$\" cssDir=\"\" scriptDir=\"\" plainHtml=\"false\" imageDir=\"\" copyLinkedImages=\"false\" imageUniquifyType=\"0\" targetExt=\"\" useTargetExt=\"false\" noCssNoScripts=\"false\" linkToExportedHtml=\"true\" exportOnSettingsChange=\"true\" regenerateOnProjectOpen=\"false\" linkFormatType=\"HTTP_ABSOLUTE\" />\n    <LinkMapSettings>\n      <textMaps />\n    </LinkMapSettings>\n  </component>\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" default=\"true\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n  <component name=\"masterDetails\">\n    <states>\n      <state key=\"ProjectJDKs.UI\">\n        <settings>\n          <last-edited>1.8</last-edited>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n    </states>\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/DRouter.iml\" filepath=\"$PROJECT_DIR$/DRouter.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/base-core/base-core.iml\" filepath=\"$PROJECT_DIR$/base-core/base-core.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/circle-module/circle-module.iml\" filepath=\"$PROJECT_DIR$/circle-module/circle-module.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/drouter-api/drouter-api.iml\" filepath=\"$PROJECT_DIR$/drouter-api/drouter-api.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/drouter-base/drouter-base.iml\" filepath=\"$PROJECT_DIR$/drouter-base/drouter-base.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/drouter-compiler/drouter-compiler.iml\" filepath=\"$PROJECT_DIR$/drouter-compiler/drouter-compiler.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/login-module/login-module.iml\" filepath=\"$PROJECT_DIR$/login-module/login-module.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "# Android 平台多模块多组件开发的路由库\n\n\n### **一. DRouter 基本介绍**\n\n**1.** 该库所涉及到的类大概在 30 个左右，源码并不多相信我们都能读懂里面的内容，这里罗列一下源码中所涉及到的一些知识点：\n\n&emsp;&emsp;(1) 编译时注解自动生成 Module、Action 和 Intercepter\n\n&emsp;&emsp;(2) 线程、线程池、线程同步异步和 Handler\n\n&emsp;&emsp;(3) 责任链模式、享元模式、策略模式、模板模式 ...\n\n**2.** 作为一个多模块的路由通信库，相信它已支持了所有跨模块通信的使用场景，功能介绍如下：\n\n&emsp;&emsp;(1) 支持依赖注入，可单独作为依赖注入框架使用\n\n&emsp;&emsp;(2) 支持线程切换和调度（原始线程，主线程，同步，异步）\n\n&emsp;&emsp;(3) 支持多模块工程下的所有跨模块通信使用场景\n\n&emsp;&emsp;(4) 支持添加多个拦截器，可根据优先级自定义拦截顺序\n\n&emsp;&emsp;(5) 支持权限和网络检测、登录拦截跳转和数据埋点等功能\n\n**3.** 笔者阅读了大量的开源库源码，本库的所有代码思想都来自其中，很感激这些大牛的开源和分享精神：\n\n&emsp;&emsp;[(1) ARouter](https://github.com/alibaba/ARouter)\n\n&emsp;&emsp;[(2) butterknife](https://github.com/JakeWharton/butterknife)\n\n&emsp;&emsp;[(3) okhttp](https://github.com/square/okhttp)\n\n&emsp;&emsp;[(4) EventBus](https://github.com/greenrobot/EventBus)\n\n&emsp;&emsp;[(5) RxJava](https://github.com/ReactiveX/RxJava)\n\n&emsp;&emsp;[(6) retrofit](https://github.com/square/retrofit)\n\n### **二. DRouter 基本使用**\n1. 在需要跨模块通信的Module中添加依赖和配置\n```\n    defaultConfig {\n        ......\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [moduleName: project.getName()]\n            }\n        }\n    }\n\n    dependencies {\n        .......\n        annotationProcessor project(':drouter-compiler')\n    }\n```\n2. 初始化 SDK\n```\npublic class BaseApplication extends Application{\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        // 开启 debug\n        DRouter.openDebug();\n        // 初始化且只能初始化一次，参数必须是 Application\n        DRouter.getInstance().init(this);\n    }\n}\n```\n3. 在 Module 中创建需要执行的 Action\n```\n// path 必须是以在 gradle 中配置的 moduleName + \"/\" 开头，否则编译通不过。\n// threadMode 支持 POSTING 、MAIN、BACKGROUND、ASYNC 默认情况下是 POSTING（原始线程）\n@Action(path = \"login/action\", threadMode = ThreadMode.MAIN)\npublic class LoginAction implements IRouterAction {\n\n    @Override\n    public RouterResult invokeAction(Context context, Map<String, Object> requestData) {\n        // 通信执行方法支持所有场景，启动 Activity，Service，Provider，弹框，缓存数据，获取 Fragment 等等等等\n        Intent intent = new Intent(context, LoginActivity.class);\n        intent.putExtra(\"key\", (String) requestData.get(\"key\"));\n        context.startActivity(intent);\n        return new RouterResult.Builder().success().object(100).build();\n    }\n}\n```\n4. 可在任意 Module 中执行跳转\n```\n// 根据 action 查询只执行对应方法，不处理返回回调，参数携带随意\nDRouter.getInstance()\n                .action(\"login/action\")\n                .context(this)\n                .param(\"key\", \"value\")\n                .invokeAction();\n\n// 根据 action 查询执行对应方法，并处理返回回调\nDRouter.getInstance()\n                .action(\"circlemodule/test\")\n                .context(this)\n                .invokeAction(new ActionCallback() {\n                    @Override\n                    public void onInterrupt() {\n                        Log.e(\"TAG\", \"被拦截了\");\n                    }\n\n                    @Override\n                    public void onResult(RouterResult result) {\n                        // 注意该方法的执行线程是由 Action 的 threadMode 决定的，也就是说和 Action 在同一个线程\n                        Log.e(\"TAG\", \"result = \" + result.toString());\n                    }\n                });\n```\n5. 在任意模块下都可添加拦截\n```\n// priority 优先级越高，拦截器执行越优先\n@Interceptor(priority = 18)\npublic class CircleInterceptor implements ActionInterceptor {\n\n    @Override\n    public void intercept(ActionChain chain) {\n        ActionPost actionPost = chain.action();\n        // 圈子详情页必须是要登录，如果没有登录即可拦截跳转到登录页面，否则继续往下执行。\n        if (chain.actionPath().equals(\"circlemodule/test\")) {\n            Toast.makeText(actionPost.context, \"拦截圈子，跳转到登录\", Toast.LENGTH_LONG).show();\n            // 跳转到登录页面\n            DRouter.getInstance()\n                    .action(\"login/action\")\n                    .context(actionPost.context)\n                    .invokeAction();\n\n            // 这个方法调用后便会拦截整条链\n            chain.onInterrupt();\n        }\n        // 继续向下转发\n        chain.proceed(actionPost);\n    }\n}\n```\n6.混淆配置\n```\n-keep public class com.drouter.assist.**{*;}\n```\n\n### **三. 其他**\n1. 简书详细介绍地址：https://www.jianshu.com/p/d0e1320704e4\n2. 视频详细讲解地址：https://pan.baidu.com/s/1kWoIA95\n\n\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-android-extensions'\n\nandroid {\n    compileSdkVersion 26\n    defaultConfig {\n        applicationId \"com.darren.drouter\"\n        minSdkVersion 15\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [ moduleName : project.getName() ]\n            }\n        }\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version\"\n    implementation 'com.android.support:appcompat-v7:26.1.0'\n    implementation 'com.android.support.constraint:constraint-layout:1.0.2'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'com.android.support.test:runner:1.0.1'\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'\n    /*compile 'com.alibaba:arouter-api:1.2.1.1'\n            annotationProcessor 'com.alibaba:arouter-compiler:1.0.3'*/\n    implementation project(':login-module')\n    implementation project(':circle-module')\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/com/darren/drouter/ExampleInstrumentedTest.kt",
    "content": "package com.darren.drouter\n\nimport android.support.test.InstrumentationRegistry\nimport android.support.test.runner.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getTargetContext()\n        assertEquals(\"com.darren.drouter\", appContext.packageName)\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.darren.drouter\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:name=\".BaseApplication\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/darren/drouter/BaseApplication.java",
    "content": "package com.darren.drouter;\n\nimport android.app.Application;\nimport com.drouter.api.core.DRouter;\n\n/**\n * description:\n * author: Darren on 2018/1/22 11:27\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class BaseApplication extends Application{\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        DRouter.openDebug();\n        DRouter.getInstance().init(this);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/darren/drouter/MainActivity.java",
    "content": "package com.darren.drouter;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.result.ActionCallback;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description:\n * author: Darren on 2018/1/22 09:43\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n    }\n\n    public void jumpLogin(View view) {\n        DRouter.getInstance()\n                .action(\"login/action\")\n                .context(this)\n                .param(\"key\", \"value\")\n                .invokeAction();\n    }\n\n    public void jumpCircle(View view) {\n        DRouter.getInstance()\n                .action(\"circlemodule/test\")\n                .context(this)\n                .param(\"key\", \"value\")\n                .invokeAction(new ActionCallback() {\n                    @Override\n                    public void onInterrupt() {\n                        Log.e(\"TAG\", \"被拦截了\");\n                    }\n\n                    @Override\n                    public void onResult(RouterResult result) {\n                        Log.e(\"TAG\", \"result = \" + result.toString());\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.darren.drouter.MainActivity\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:padding=\"10dp\"\n        android:text=\"主 App 模块\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"jumpLogin\"\n        android:text=\"跳转到登录模块\" />\n\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"jumpCircle\"\n        android:text=\"跳转到圈子模块\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">DRouter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/darren/drouter/ExampleUnitTest.kt",
    "content": "package com.darren.drouter\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "base-core/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "base-core/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n\n\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    compile 'com.android.support:appcompat-v7:26.1.0'\n    compile project(':drouter-api')\n    annotationProcessor project(':drouter-compiler')\n}\n"
  },
  {
    "path": "base-core/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "base-core/src/androidTest/java/com/base/core/ExampleInstrumentedTest.java",
    "content": "package com.base.core;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.base.core.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "base-core/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.base.core\" />\n"
  },
  {
    "path": "base-core/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">base-core</string>\n</resources>\n"
  },
  {
    "path": "base-core/src/test/java/com/base/core/ExampleUnitTest.java",
    "content": "package com.base.core;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.1.51'\n    repositories {\n        google()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.0.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "circle-module/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "circle-module/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n\n    repositories {\n        mavenCentral()\n    }\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [moduleName: project.getName()]\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.android.support:appcompat-v7:26.1.0'\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation 'com.android.support.test:runner:1.0.1'\n    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'\n    implementation project(':base-core')\n    annotationProcessor project(':drouter-compiler')\n}\n"
  },
  {
    "path": "circle-module/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "circle-module/src/androidTest/java/com/example/circle_module/ExampleInstrumentedTest.java",
    "content": "package com.example.circle_module;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.example.circle_module.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.example.circle_module\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\">\n        <activity android:name=\".CircleActivity\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "circle-module/src/main/java/com/example/circle_module/CircleAction.java",
    "content": "package com.example.circle_module;\n\nimport android.content.Context;\nimport android.content.Intent;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.result.RouterResult;\nimport com.drouter.base.ThreadMode;\nimport com.drouter.base.annotation.Action;\n\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:57\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Action(path = \"circlemodule/test\", threadMode = ThreadMode.MAIN)\npublic class CircleAction implements IRouterAction {\n    @Override\n    public RouterResult invokeAction(Context context, Map<String, Object> requestData) {\n        Intent intent = new Intent(context, CircleActivity.class);\n        intent.putExtra(\"key\", (String) requestData.get(\"key\"));\n        context.startActivity(intent);\n        return new RouterResult.Builder().success().build();\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/java/com/example/circle_module/CircleActivity.java",
    "content": "package com.example.circle_module;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.result.ActionCallback;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description:\n * author: Darren on 2018/1/22 15:08\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class CircleActivity extends AppCompatActivity {\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_circle);\n    }\n\n    public void click(View view) {\n        DRouter.getInstance()\n                .action(\"login/action\")\n                .context(CircleActivity.this)\n                .param(\"key\", \"value\")\n                .invokeAction(new ActionCallback() {\n                    @Override\n                    public void onInterrupt() {\n                        Log.e(\"TAG\", \"被拦截了\");\n                    }\n\n                    @Override\n                    public void onResult(RouterResult result) {\n                        Log.e(\"TAG\", \"result = \" + result.toString());\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/java/com/example/circle_module/CircleInterceptor.java",
    "content": "package com.example.circle_module;\n\nimport android.widget.Toast;\n\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.interceptor.ActionInterceptor;\nimport com.drouter.api.thread.ActionPost;\nimport com.drouter.base.annotation.Interceptor;\n\n/**\n * description:\n * author: Darren on 2018/1/24 16:14\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Interceptor(priority = 18)\npublic class CircleInterceptor implements ActionInterceptor {\n\n    @Override\n    public void intercept(ActionChain chain) {\n        ActionPost actionPost = chain.action();\n        if (chain.actionPath().equals(\"circlemodule/test\")) {\n            Toast.makeText(actionPost.context, \"拦截圈子，跳转到登录\", Toast.LENGTH_LONG).show();\n            // 拦截\n            chain.onInterrupt();\n            // 跳转到登录页面\n            DRouter.getInstance()\n                    .action(\"login/action\")\n                    .context(actionPost.context)\n                    .invokeAction();\n        }\n        // 继续向下转发\n        chain.proceed(actionPost);\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/java/com/example/circle_module/CircleInterceptor1.java",
    "content": "package com.example.circle_module;\n\nimport com.drouter.api.interceptor.ActionInterceptor;\nimport com.drouter.api.thread.ActionPost;\nimport com.drouter.base.annotation.Interceptor;\n\n/**\n * description:\n * author: Darren on 2018/1/24 16:14\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Interceptor(priority = 6)\npublic class CircleInterceptor1 implements ActionInterceptor {\n\n    @Override\n    public void intercept(ActionChain chain) {\n        ActionPost actionPost = chain.action();\n\n        if (chain.actionPath().equals(\"circlemodule/test\")) {\n\n            // 拦截\n            chain.onInterrupt();\n        }\n\n        // 继续向下转发\n        chain.proceed(actionPost);\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/java/com/example/circle_module/CircleInterceptor2.java",
    "content": "package com.example.circle_module;\n\nimport com.drouter.api.interceptor.ActionInterceptor;\nimport com.drouter.api.thread.ActionPost;\nimport com.drouter.base.annotation.Interceptor;\n\n/**\n * description:\n * author: Darren on 2018/1/24 16:14\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Interceptor(priority = 10)\npublic class CircleInterceptor2 implements ActionInterceptor {\n\n    @Override\n    public void intercept(ActionChain chain) {\n        ActionPost actionPost = chain.action();\n        if (chain.actionPath().equals(\"circlemodule/test\")) {\n            // 拦截\n            chain.onInterrupt();\n        }\n\n        // 继续向下转发\n        chain.proceed(actionPost);\n    }\n}\n"
  },
  {
    "path": "circle-module/src/main/res/layout/activity_circle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/test\"\n        android:padding=\"10dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:text=\"圈子模块\" />\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:onClick=\"click\"\n        android:text=\"点击跳转到登录模块\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "circle-module/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">circle-module</string>\n</resources>\n"
  },
  {
    "path": "circle-module/src/test/java/com/example/circle_module/ExampleUnitTest.java",
    "content": "package com.example.circle_module;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "drouter-api/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "drouter-api/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n\n\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.android.support:appcompat-v7:26.1.0'\n    compile project(':drouter-base')\n}\n"
  },
  {
    "path": "drouter-api/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "drouter-api/src/androidTest/java/com/drouter/api/ExampleInstrumentedTest.java",
    "content": "package com.drouter.api;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.drouter.api.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.drouter.api\" />\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/action/IRouterAction.java",
    "content": "package com.drouter.api.action;\n\nimport android.content.Context;\n\nimport com.drouter.api.result.RouterResult;\n\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:32\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface IRouterAction {\n    // 执行 Action 方法\n    RouterResult invokeAction(Context context, Map<String, Object> requestData);\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/action/IRouterInterceptor.java",
    "content": "package com.drouter.api.action;\n\nimport com.drouter.api.interceptor.ActionInterceptor;\n\nimport java.util.List;\n\n/**\n * description:\n * author: Darren on 2018/1/22 11:08\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface IRouterInterceptor {\n    // 通过 Action 的名称找到 Action\n    List<ActionInterceptor> getInterceptors();\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/action/IRouterModule.java",
    "content": "package com.drouter.api.action;\n\nimport com.drouter.api.extra.ActionWrapper;\n\n/**\n * description:\n * author: Darren on 2018/1/22 11:08\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface IRouterModule {\n    // 通过 Action 的名称找到 Action\n    ActionWrapper findAction(String actionName);\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/core/DRouter.java",
    "content": "package com.drouter.api.core;\n\nimport android.app.Application;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.text.TextUtils;\nimport android.widget.Toast;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.action.IRouterInterceptor;\nimport com.drouter.api.action.IRouterModule;\nimport com.drouter.api.exception.InitException;\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.extra.Consts;\nimport com.drouter.api.extra.DefaultLogger;\nimport com.drouter.api.extra.ErrorActionWrapper;\nimport com.drouter.api.extra.ILogger;\nimport com.drouter.api.interceptor.ActionInterceptor;\nimport com.drouter.api.interceptor.CallActionInterceptor;\nimport com.drouter.api.interceptor.ErrorActionInterceptor;\nimport com.drouter.api.thread.PosterSupport;\nimport com.drouter.api.utils.ClassUtils;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/22 09:59\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class DRouter {\n    // 是否被初始化\n    private volatile static boolean hasInit = false;\n    // 是否是 debugable 状态\n    private volatile static boolean debuggable = false;\n    // 日志打印\n    public volatile static ILogger logger = new DefaultLogger();\n    // 缓存的 RouterAction\n    private volatile static Map<String, ActionWrapper> cacheRouterActions = new HashMap();\n    // 缓存的 RouterModule\n    private volatile static Map<String, IRouterModule> cacheRouterModules = new HashMap();\n    // 所有 moudle\n    private static List<String> mAllModuleClassName;\n    private Context mApplicationContext;\n\n    private static List<ActionInterceptor> interceptors = new ArrayList<>();\n\n    public static synchronized void openDebug() {\n        debuggable = true;\n        logger.showLog(true);\n\n        logger.d(Consts.TAG, \"DRouter openDebug\");\n    }\n\n    private volatile static DRouter instance = null;\n\n    public static boolean debuggable() {\n        return debuggable;\n    }\n\n    private DRouter() {\n\n    }\n\n    /**\n     * Get instance of router. A\n     * All feature U use, will be starts here.\n     */\n    public static DRouter getInstance() {\n        if (instance == null) {\n            synchronized (DRouter.class) {\n                if (instance == null) {\n                    instance = new DRouter();\n                }\n            }\n        }\n        return instance;\n    }\n\n    /**\n     * 初始化数据\n     */\n    public void init(Application context) {\n        if (hasInit) {\n            throw new InitException(\"ARouter already initialized, It can only be initialized once.\");\n        }\n\n        hasInit = true;\n\n        this.mApplicationContext = context;\n        // 获取 com.drotuer.assist 包名下的所有类名信息\n        try {\n            mAllModuleClassName = ClassUtils.getFileNameByPackageName(context, Consts.ROUTER_MODULE_PACK_NAME);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        for (String className : mAllModuleClassName) {\n            logger.d(Consts.TAG, \"扫描到: \" + className);\n        }\n        // 添加并且实例化所有拦截器\n        scanAddInterceptors(context);\n    }\n\n    // 扫描并且添加拦截器\n    private void scanAddInterceptors(final Context context) {\n        PosterSupport.getExecutorService().execute(new Runnable() {\n            @Override\n            public void run() {\n                // 1. 错误拦截器\n                interceptors.add(new ErrorActionInterceptor());\n                // 2. module 自定义的拦截器\n                try {\n                    List<String> interceptorGroups = ClassUtils.getFileNameByPackageName(context, Consts.ROUTER_INTERCEPTOR_PACK_NAME);\n                    // 循环所有的 Group 拦截器\n                    for (String interceptorGroup : interceptorGroups) {\n                        if (interceptorGroup.contains(Consts.ROUTER_INTERCEPTOR_GROUP_PREFIX)) {\n                            IRouterInterceptor routerInterceptor = (IRouterInterceptor) Class.forName(interceptorGroup).newInstance();\n                            List<ActionInterceptor> interceptorClasses = routerInterceptor.getInterceptors();\n                            for (int i = interceptorClasses.size() - 1; i >= 0; i--) {\n                                ActionInterceptor interceptor = interceptorClasses.get(i);\n                                // 添加到拦截器链表\n                                interceptors.add(interceptor);\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                    String message = \"Instance interceptor error: \" + e.getMessage();\n                    logger.e(Consts.TAG, message);\n                }\n\n                // 3. 最后添加 Action 执行调用的拦截器\n                interceptors.add(new CallActionInterceptor());\n            }\n        });\n    }\n\n\n    public RouterForward action(String actionName) {\n        // 1. 动态先查找加载 Module\n        // actionName 的格式必须是 xxx/xxx\n        if (!actionName.contains(\"/\")) {\n            String message = \"action name  format error -> <\" + actionName + \">, like: moduleName/actionName\";\n            debugMessage(message);\n            return new RouterForward(new ErrorActionWrapper(), interceptors);\n        }\n\n        // 2.获取 moduleName，实例化 Module，并缓存\n        String moduleName = actionName.split(\"/\")[0];\n        String moduleClassName = searchModuleClassName(moduleName);\n        if (TextUtils.isEmpty(moduleClassName)) {\n            String message = String.format(\"Please check to the action name is correct: according to the <%s> cannot find module %s.\", actionName, moduleName);\n            debugMessage(message);\n            return new RouterForward(new ErrorActionWrapper(), interceptors);\n        }\n        IRouterModule routerModule = cacheRouterModules.get(moduleClassName);\n        if (routerModule == null) {\n            try {\n                Class<? extends IRouterModule> moduleClass = (Class<? extends IRouterModule>) Class.forName(moduleClassName);\n                routerModule = moduleClass.newInstance();\n                cacheRouterModules.put(moduleClassName, routerModule);\n            } catch (Exception e) {\n                e.printStackTrace();\n                String message = \"instance module error: \" + e.getMessage();\n                debugMessage(message);\n                return new RouterForward(new ErrorActionWrapper(), interceptors);\n            }\n        }\n\n        // 3. 从 Module 中获取 ActionWrapper 类名，然后创建缓存 ActionWrapper\n        ActionWrapper actionWrapper = cacheRouterActions.get(actionName);\n        if (actionWrapper == null) {\n            actionWrapper = routerModule.findAction(actionName);\n        } else {\n            return new RouterForward(actionWrapper, interceptors);\n        }\n\n        if (actionWrapper == null) {\n            String message = String.format(\"Please check to the action name is correct: according to the <%s> cannot find action.\", actionName);\n            debugMessage(message);\n            return new RouterForward(new ErrorActionWrapper(), interceptors);\n        }\n\n        Class<? extends IRouterAction> actionClass = actionWrapper.getActionClass();\n        IRouterAction routerAction = actionWrapper.getRouterAction();\n        if (routerAction == null) {\n            try {\n                if (!IRouterAction.class.isAssignableFrom(actionClass)) {\n                    String message = actionClass.getCanonicalName() + \" must be implements IRouterAction.\";\n                    debugMessage(message);\n                    return new RouterForward(new ErrorActionWrapper(), interceptors);\n                }\n                // 创建 RouterAction 实例，并缓存起来\n                routerAction = actionClass.newInstance();\n                actionWrapper.setRouterAction(routerAction);\n                cacheRouterActions.put(actionName, actionWrapper);\n            } catch (Exception e) {\n                String message = \"instance action error: \" + e.getMessage();\n                debugMessage(message);\n                return new RouterForward(new ErrorActionWrapper(), interceptors);\n            }\n        }\n\n        return new RouterForward(actionWrapper, interceptors);\n    }\n\n    /**\n     * 显示 debug 信息\n     *\n     * @param message\n     */\n    private void debugMessage(String message) {\n        if (debuggable) {\n            logger.e(Consts.TAG, message);\n            showToast(message);\n        }\n    }\n\n    /**\n     * 打印显示 Toast\n     *\n     * @param message\n     */\n    private void showToast(String message) {\n        Toast.makeText(mApplicationContext, message, Toast.LENGTH_LONG).show();\n    }\n\n    /**\n     * 根据 moduleName 查询 module 的全类名\n     *\n     * @param moduleName\n     * @return\n     */\n    private String searchModuleClassName(String moduleName) {\n        for (String moduleClassName : mAllModuleClassName) {\n            if (moduleClassName.contains(moduleName)) {\n                return moduleClassName;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/core/RouterForward.java",
    "content": "package com.drouter.api.core;\n\nimport android.content.Context;\n\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.interceptor.ActionInterceptor;\nimport com.drouter.api.interceptor.ActionInterceptorChain;\nimport com.drouter.api.result.ActionCallback;\nimport com.drouter.api.thread.ActionPost;\nimport com.drouter.base.ThreadMode;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * description: 路由转发\n * author: Darren on 2018/1/22 11:20\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class RouterForward {\n    private ActionWrapper mActionWrapper;\n    private Context mContext;\n    private Map<String, Object> mParams;\n    private ThreadMode mThreadMode = null;\n    // 所有拦截器表\n    private List<ActionInterceptor> interceptors;\n\n    /**\n     * 指定 threadMode 这里指定的优先级高于 Action 注解上的 threadMode\n     *\n     * @param threadMode\n     * @return\n     */\n    public RouterForward threadMode(ThreadMode threadMode) {\n        this.mThreadMode = threadMode;\n        return this;\n    }\n\n    RouterForward(ActionWrapper actionWrapper, List<ActionInterceptor> interceptors) {\n        this.mActionWrapper = actionWrapper;\n        mParams = new HashMap<>();\n        this.interceptors = interceptors;\n    }\n\n    /**\n     * 执行 Action\n     *\n     * @return\n     */\n    public void invokeAction() {\n        invokeAction(ActionCallback.DEFAULT_ACTION_CALLBACK);\n    }\n\n    /**\n     * 执行 Action\n     *\n     * @return\n     */\n    public void invokeAction(ActionCallback actionCallback) {\n        // 先封装 actionPost\n        mActionWrapper.setThreadMode(getThreadMode());\n        ActionPost actionPost = ActionPost.obtainActionPost(mActionWrapper, mContext, mParams, actionCallback);\n        // 开始拦截器的流程\n        ActionInterceptor.ActionChain chain = new ActionInterceptorChain(interceptors, actionPost, 0);\n        chain.proceed(actionPost);\n    }\n\n    /**\n     * 路由转发方法传递的 threadMode 优先级高于 Action 注解上的 threadMode\n     *\n     * @return\n     */\n    public ThreadMode getThreadMode() {\n        return mThreadMode == null ? mActionWrapper.getThreadMode() : mThreadMode;\n    }\n\n    public RouterForward context(Context context) {\n        this.mContext = context;\n        return this;\n    }\n\n    public RouterForward param(String key, Object value) {\n        mParams.put(key, value);\n        return this;\n    }\n\n    public RouterForward param(Map<String, Object> params) {\n        mParams.putAll(params);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/exception/InitException.java",
    "content": "package com.drouter.api.exception;\n\n/**\n * description: 初始化异常类\n * author: Darren on 2018/1/22 10:26\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class InitException extends RuntimeException {\n    public InitException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/exception/RouterActionException.java",
    "content": "package com.drouter.api.exception;\n\n/**\n * description: Router Action Exception\n * author: Darren on 2018/1/22 10:44\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class RouterActionException extends RuntimeException{\n    public RouterActionException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/exception/RouterModuleException.java",
    "content": "package com.drouter.api.exception;\n\n/**\n * description: Router Module Exception\n * author: Darren on 2018/1/22 10:44\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class RouterModuleException extends RuntimeException{\n    public RouterModuleException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/extra/ActionWrapper.java",
    "content": "package com.drouter.api.extra;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.base.ThreadMode;\n\n/**\n * description:\n * author: Darren on 2018/1/23 14:22\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ActionWrapper {\n    private Class<? extends IRouterAction> actionClass;\n    private String path;\n    private ThreadMode threadMode;\n    private boolean extraProcess;\n    private IRouterAction routerAction;\n\n    ActionWrapper() {\n\n    }\n\n    public void setRouterAction(IRouterAction routerAction) {\n        this.routerAction = routerAction;\n    }\n\n    public void setThreadMode(ThreadMode threadMode) {\n        this.threadMode = threadMode;\n    }\n\n    public IRouterAction getRouterAction() {\n        return routerAction;\n    }\n\n    private ActionWrapper(Class<? extends IRouterAction> actionClass, String path, boolean extraProcess, ThreadMode threadMode) {\n        this.actionClass = actionClass;\n        this.path = path;\n        this.extraProcess = extraProcess;\n        this.threadMode = threadMode;\n    }\n\n    public Class<? extends IRouterAction> getActionClass() {\n        return actionClass;\n    }\n\n    public ThreadMode getThreadMode() {\n        return threadMode;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public boolean isExtraProcess() {\n        return extraProcess;\n    }\n\n    public static ActionWrapper build(Class<? extends IRouterAction> actionClass, String path, boolean extraProcess, ThreadMode threadMode) {\n        return new ActionWrapper(actionClass, path, extraProcess, threadMode);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/extra/Consts.java",
    "content": "package com.drouter.api.extra;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:01\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class Consts {\n    public static final String SDK_NAME = \"DRouter\";\n    public static final String TAG = SDK_NAME;\n    public static final String SUFFIX_INTERCEPTORS = \"Interceptors\";\n    public static final String ROUTER_MODULE_PACK_NAME = \"com.drouter.assist.module\";\n    public static final String ROUTER_INTERCEPTOR_PACK_NAME = \"com.drouter.assist.interceptor\";\n    public static final String ROUTER_INTERCEPTOR_GROUP_PREFIX = \"DRouter$$Interceptor$$\";\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/extra/DefaultLogger.java",
    "content": "package com.drouter.api.extra;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:05\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class DefaultLogger implements ILogger {\n    boolean isShowLog = false;\n    private String defaultTag = \"DRouter\";\n\n    @Override\n    public void showLog(boolean isShowLog) {\n        this.isShowLog = isShowLog;\n    }\n\n    @Override\n    public void d(String tag, String message) {\n        if (isShowLog) {\n            Log.d(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);\n        }\n    }\n\n    @Override\n    public void i(String tag, String message) {\n        if (isShowLog) {\n            Log.i(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);\n        }\n    }\n\n    @Override\n    public void w(String tag, String message) {\n        if (isShowLog) {\n            Log.w(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);\n        }\n    }\n\n    @Override\n    public void e(String tag, String message) {\n        if (isShowLog) {\n            Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message);\n        }\n    }\n\n\n    public String getDefaultTag() {\n        return defaultTag;\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/extra/ErrorActionWrapper.java",
    "content": "package com.drouter.api.extra;\n\n/**\n * description:\n * author: Darren on 2018/1/23 15:45\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ErrorActionWrapper extends ActionWrapper {\n    public ErrorActionWrapper() {\n        \n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/extra/ILogger.java",
    "content": "package com.drouter.api.extra;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:02\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface ILogger {\n    void showLog(boolean isShowLog);\n\n    void d(String tag, String message);\n\n    void i(String tag, String message);\n\n    void w(String tag, String message);\n\n    void e(String tag, String message);\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/interceptor/ActionInterceptor.java",
    "content": "package com.drouter.api.interceptor;\n\nimport com.drouter.api.thread.ActionPost;\n\n/**\n * description: 拦截器\n * author: Darren on 2018/1/22 11:59\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface ActionInterceptor {\n    void intercept(ActionChain chain);\n\n    interface ActionChain {\n        // 打断拦截\n        void onInterrupt();\n\n        // 分发给下一个拦截器\n        void proceed(ActionPost actionPost);\n\n        // 获取 ActionPost\n        ActionPost action();\n\n        String actionPath();\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/interceptor/ActionInterceptorChain.java",
    "content": "package com.drouter.api.interceptor;\n\nimport com.drouter.api.thread.ActionPost;\n\nimport java.util.List;\n\n/**\n * description:\n * author: Darren on 2018/1/24 09:38\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ActionInterceptorChain implements ActionInterceptor.ActionChain {\n    // 是否被拦截了\n    private boolean isInterrupt = false;\n    private List<ActionInterceptor> interceptors;\n    private ActionPost actionPost;\n    private int index;\n\n    public ActionInterceptorChain(List<ActionInterceptor> interceptors, ActionPost actionPost, int index) {\n        this.interceptors = interceptors;\n        this.actionPost = actionPost;\n        this.index = index;\n    }\n\n    @Override\n    public void onInterrupt() {\n        isInterrupt = true;\n        actionPost.actionCallback.onInterrupt();\n    }\n\n    @Override\n    public void proceed(ActionPost actionPost) { // 0\n        if (!isInterrupt && index < interceptors.size()) {\n            // 继续往下分发\n            ActionInterceptor.ActionChain next = new ActionInterceptorChain(interceptors, actionPost, index + 1);\n            // 0 拦截器\n            ActionInterceptor interceptor = interceptors.get(index);\n            // 执行第一个\n            interceptor.intercept(next);\n        }\n    }\n\n    @Override\n    public ActionPost action() {\n        return actionPost;\n    }\n\n    @Override\n    public String actionPath() {\n        return actionPost.actionWrapper.getPath();\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/interceptor/CallActionInterceptor.java",
    "content": "package com.drouter.api.interceptor;\n\n\nimport android.os.Looper;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.result.RouterResult;\nimport com.drouter.api.thread.ActionPost;\nimport com.drouter.api.thread.PosterSupport;\n\n/**\n * description: 执行 Action 方法的最后一个拦截器\n * author: Darren on 2018/1/24 09:03\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class CallActionInterceptor implements ActionInterceptor {\n    @Override\n    public void intercept(ActionChain chain) {\n        // 执行 Action 方法\n        ActionPost actionPost = chain.action();\n        invokeAction(actionPost, Looper.myLooper() == Looper.getMainLooper());\n    }\n\n    /**\n     * 处理线程切换\n     *\n     * @param isMainThread\n     * @return\n     */\n    private void invokeAction(ActionPost actionPost, boolean isMainThread) {\n        switch (actionPost.actionWrapper.getThreadMode()) {\n            case POSTING:\n                invokeAction(actionPost);\n            case MAIN:\n                if (isMainThread) {\n                    invokeAction(actionPost);\n                } else {\n                    PosterSupport.getMainPoster().enqueue(actionPost);\n                }\n                break;\n            case BACKGROUND:\n                if (isMainThread) {\n                    PosterSupport.getBackgroundPoster().enqueue(actionPost);\n                } else {\n                    invokeAction(actionPost);\n                }\n                break;\n            case ASYNC:\n                PosterSupport.getAsyncPoster().enqueue(actionPost);\n                break;\n            default:\n                throw new IllegalStateException(\"Unknown thread mode: \" + actionPost.actionWrapper.getThreadMode());\n        }\n    }\n\n    /**\n     * 执行 Action\n     *\n     * @param actionPost\n     */\n    private void invokeAction(ActionPost actionPost) {\n        ActionWrapper actionWrapper = actionPost.actionWrapper;\n        IRouterAction routerAction = actionWrapper.getRouterAction();\n        RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);\n        actionPost.actionCallback.onResult(routerResult);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/interceptor/ErrorActionInterceptor.java",
    "content": "package com.drouter.api.interceptor;\n\n\nimport com.drouter.api.extra.ErrorActionWrapper;\nimport com.drouter.api.thread.ActionPost;\n\n/**\n * description: 错误的 Action 拦截器，放在拦截链的第一个位置\n * author: Darren on 2018/1/24 09:03\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ErrorActionInterceptor implements ActionInterceptor {\n    @Override\n    public void intercept(ActionChain chain) {\n        ActionPost actionPost = chain.action();\n        // 拦截错误\n        if (actionPost.actionWrapper instanceof ErrorActionWrapper) {\n            chain.onInterrupt();\n        }\n\n        // 继续分发\n        chain.proceed(actionPost);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/result/ActionCallback.java",
    "content": "package com.drouter.api.result;\n\n/**\n * description:\n * author: Darren on 2018/1/24 09:05\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface ActionCallback {\n    // 被拦截了\n    void onInterrupt();\n\n    // 没被拦截返回结果\n    void onResult(RouterResult result);\n\n    // 默认的 ActionCallback\n    ActionCallback DEFAULT_ACTION_CALLBACK = new ActionCallback() {\n\n        @Override\n        public void onInterrupt() {\n\n        }\n\n        @Override\n        public void onResult(RouterResult result) {\n\n        }\n    };\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/result/RouterResult.java",
    "content": "package com.drouter.api.result;\n\n/**\n * description: 路由的返回结果\n * author: Darren on 2018/1/22 10:35\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class RouterResult {\n    static final int SUCCEED_CODE = 0x000011;\n    static final int ERROR_CODE = 0x000022;\n    private String msg;\n    private int code;\n    private Object object;\n\n    public int getCode() {\n        return code;\n    }\n\n    private RouterResult(Builder builder) {\n        this.code = builder.code;\n        this.msg = builder.msg;\n        this.object = builder.object;\n    }\n\n    @Override\n    public String toString() {\n        return super.toString() + \"{\" +\n                \"msg='\" + msg + '\\'' +\n                \", code=\" + code +\n                \", object=\" + object +\n                '}';\n    }\n\n    public String getMsg() {\n        return msg;\n    }\n\n    public Object getObject() {\n        return object;\n    }\n\n    /**\n     * 返回是否成功\n     *\n     * @return\n     */\n    public boolean isSucceed() {\n        return code == SUCCEED_CODE;\n    }\n\n\n    public static class Builder {\n        int code = SUCCEED_CODE;\n        String msg;\n        Object object;\n\n        public Builder error() {\n            this.code = ERROR_CODE;\n            return this;\n        }\n\n        public Builder success() {\n            this.code = SUCCEED_CODE;\n            return this;\n        }\n\n        public Builder msg(String msg) {\n            this.msg = msg;\n            return this;\n        }\n\n        public Builder object(Object object) {\n            this.object = object;\n            return this;\n        }\n\n        public RouterResult build() {\n            return new RouterResult(this);\n        }\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/ActionPost.java",
    "content": "package com.drouter.api.thread;\n\nimport android.content.Context;\n\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.result.ActionCallback;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/23 16:11\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic final class ActionPost {\n    private final static List<ActionPost> pendingPostPool = new ArrayList<ActionPost>();\n\n    public Context context;\n    public ActionWrapper actionWrapper;\n    public Map<String, Object> params;\n    public ActionCallback actionCallback;\n    ActionPost next;\n\n    private ActionPost(ActionWrapper actionWrapper, Context context, Map<String, Object> params, ActionCallback actionCallback) {\n        this.context = context;\n        this.actionWrapper = actionWrapper;\n        this.params = params;\n        this.actionCallback = actionCallback;\n    }\n\n    public static ActionPost obtainActionPost(ActionWrapper actionWrapper, Context context, Map<String, Object> params, ActionCallback actionCallback) {\n        synchronized (pendingPostPool) {\n            int size = pendingPostPool.size();\n            if (size > 0) {\n                ActionPost actionPost = pendingPostPool.remove(size - 1);\n                actionPost.context = context;\n                actionPost.actionWrapper = actionWrapper;\n                actionPost.params = params;\n                actionPost.next = null;\n                actionPost.actionCallback = actionCallback;\n                return actionPost;\n            }\n        }\n        return new ActionPost(actionWrapper, context, params, actionCallback);\n    }\n\n    public void releasePendingPost() {\n        this.context = null;\n        this.actionWrapper = null;\n        this.next = null;\n        this.actionCallback = null;\n        synchronized (pendingPostPool) {\n            // Don't let the pool grow indefinitely\n            if (pendingPostPool.size() < 10000) {\n                pendingPostPool.add(this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/ActionPostQueue.java",
    "content": "package com.drouter.api.thread;\n\n/**\n * description:\n * author: Darren on 2018/1/23 16:13\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ActionPostQueue {\n    private ActionPost head;\n    private ActionPost tail;\n\n    synchronized void enqueue(ActionPost pendingPost) {\n        if (pendingPost == null) {\n            throw new NullPointerException(\"null cannot be enqueued\");\n        }\n        if (tail != null) {\n            tail.next = pendingPost;\n            tail = pendingPost;\n        } else if (head == null) {\n            head = tail = pendingPost;\n        } else {\n            throw new IllegalStateException(\"Head present, but no tail\");\n        }\n        notifyAll();\n    }\n\n    synchronized ActionPost poll() {\n        ActionPost pendingPost = head;\n        if (head != null) {\n            head = head.next;\n            if (head == null) {\n                tail = null;\n            }\n        }\n        return pendingPost;\n    }\n\n    synchronized ActionPost poll(int maxMillisToWait) throws InterruptedException {\n        if (head == null) {\n            wait(maxMillisToWait);\n        }\n        return poll();\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/AsyncPoster.java",
    "content": "package com.drouter.api.thread;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description: 处理异步\n * author: Darren on 2018/1/23 16:46\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class AsyncPoster implements Runnable, Poster {\n\n    private final ActionPostQueue queue;\n\n    AsyncPoster() {\n        queue = new ActionPostQueue();\n    }\n\n    @Override\n    public void run() {\n        ActionPost actionPost = queue.poll();\n        if (actionPost == null) {\n            throw new IllegalStateException(\"No pending post available\");\n        }\n\n        ActionWrapper actionWrapper = actionPost.actionWrapper;\n        IRouterAction routerAction = actionWrapper.getRouterAction();\n        RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);\n        actionPost.actionCallback.onResult(routerResult);\n\n        actionPost.releasePendingPost();\n    }\n\n    @Override\n    public void enqueue(ActionPost actionPost) {\n        queue.enqueue(actionPost);\n        PosterSupport.getExecutorService().execute(this);\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/BackgroundPoster.java",
    "content": "package com.drouter.api.thread;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.extra.Consts;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description:\n * author: Darren on 2018/1/23 17:01\n * email: 240336124@qq.com\n * version: 1.0\n */\nfinal class BackgroundPoster implements Runnable, Poster {\n\n    private final ActionPostQueue queue;\n\n    private volatile boolean executorRunning;\n\n    BackgroundPoster() {\n        queue = new ActionPostQueue();\n    }\n\n    @Override\n    public void enqueue(ActionPost actionPost) {\n        synchronized (this) {\n            queue.enqueue(actionPost);\n            if (!executorRunning) {\n                executorRunning = true;\n                PosterSupport.getExecutorService().execute(this);\n            }\n        }\n    }\n\n    @Override\n    public void run() {\n        try {\n            try {\n                while (true) {\n                    ActionPost actionPost = queue.poll(1000);\n                    if (actionPost == null) {\n                        synchronized (this) {\n                            // Check again, this time in synchronized\n                            actionPost = queue.poll();\n                            if (actionPost == null) {\n                                executorRunning = false;\n                                return;\n                            }\n                        }\n                    }\n\n                    ActionWrapper actionWrapper = actionPost.actionWrapper;\n                    IRouterAction routerAction = actionWrapper.getRouterAction();\n                    RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);\n                    actionPost.actionCallback.onResult(routerResult);\n                    \n                    actionPost.releasePendingPost();\n                }\n            } catch (InterruptedException e) {\n                DRouter.logger.e(Consts.TAG, Thread.currentThread().getName() + \" was interruppted\");\n            }\n        } finally {\n            executorRunning = false;\n        }\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/HandlerPoster.java",
    "content": "package com.drouter.api.thread;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.SystemClock;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.extra.ActionWrapper;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description: 处理主线程切换\n * author: Darren on 2018/1/23 16:10\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class HandlerPoster extends Handler implements Poster {\n    private final ActionPostQueue queue;\n    private final int maxMillisInsideHandleMessage;\n    private boolean handlerActive;\n\n    protected HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) {\n        super(looper);\n        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;\n        queue = new ActionPostQueue();\n    }\n\n    @Override\n    public void handleMessage(Message msg) {\n        boolean rescheduled = false;\n        try {\n            long started = SystemClock.uptimeMillis();\n            while (true) {\n                ActionPost actionPost = queue.poll();\n                if (actionPost == null) {\n                    synchronized (this) {\n                        // Check again, this time in synchronized\n                        actionPost = queue.poll();\n                        if (actionPost == null) {\n                            handlerActive = false;\n                            return;\n                        }\n                    }\n                }\n\n                ActionWrapper actionWrapper = actionPost.actionWrapper;\n                IRouterAction routerAction = actionWrapper.getRouterAction();\n                RouterResult routerResult = routerAction.invokeAction(actionPost.context, actionPost.params);\n                actionPost.actionCallback.onResult(routerResult);\n\n                actionPost.releasePendingPost();\n\n                long timeInMethod = SystemClock.uptimeMillis() - started;\n                if (timeInMethod >= maxMillisInsideHandleMessage) {\n                    if (!sendMessage(obtainMessage())) {\n                        throw new RuntimeException(\"Could not send handler message\");\n                    }\n                    rescheduled = true;\n                    return;\n                }\n            }\n        } finally {\n            handlerActive = rescheduled;\n        }\n    }\n\n    @Override\n    public void enqueue(ActionPost actionPost) {\n        synchronized (this) {\n            queue.enqueue(actionPost);\n            if (!handlerActive) {\n                handlerActive = true;\n                if (!sendMessage(obtainMessage())) {\n                    throw new RuntimeException(\"Could not send handler message\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/Poster.java",
    "content": "package com.drouter.api.thread;\n\n/**\n * description:\n * author: Darren on 2018/1/23 16:05\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic interface Poster {\n    void enqueue(ActionPost actionPost);\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/thread/PosterSupport.java",
    "content": "package com.drouter.api.thread;\n\nimport android.os.Looper;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * description:\n * author: Darren on 2018/1/23 16:08\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class PosterSupport {\n    private static volatile Poster mainPoster, backgroundPoster, asyncPoster;\n    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();\n\n\n    public static Poster getMainPoster() {\n        if (mainPoster == null) {\n            synchronized (PosterSupport.class) {\n                if (mainPoster == null) {\n                    mainPoster = new HandlerPoster(Looper.getMainLooper(), 10);\n                }\n            }\n        }\n        return mainPoster;\n    }\n\n    public static ExecutorService getExecutorService() {\n        return DEFAULT_EXECUTOR_SERVICE;\n    }\n\n    public static Poster getBackgroundPoster() {\n        if (backgroundPoster == null) {\n            synchronized (PosterSupport.class) {\n                if (backgroundPoster == null) {\n                    backgroundPoster = new BackgroundPoster();\n                }\n            }\n        }\n        return backgroundPoster;\n    }\n\n    public static Poster getAsyncPoster() {\n        if (asyncPoster == null) {\n            synchronized (PosterSupport.class) {\n                if (asyncPoster == null) {\n                    asyncPoster = new AsyncPoster();\n                }\n            }\n        }\n        return asyncPoster;\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/utils/ClassUtils.java",
    "content": "package com.drouter.api.utils;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\n\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.extra.Consts;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport dalvik.system.DexFile;\n\n/**\n * description: thanks alibaba ARouter\n * author: Darren on 2018/1/23 09:24\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class ClassUtils {\n    private static final String EXTRACTED_NAME_EXT = \".classes\";\n    private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;\n    private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;\n    private static final String PREFS_FILE = \"multidex.version\";\n    private static final String KEY_DEX_NUMBER = \"dex.number\";\n    private static final String SECONDARY_FOLDER_NAME = \"code_cache\" + File.separator + \"secondary-dexes\";\n    private static final String EXTRACTED_SUFFIX = \".zip\";\n\n    private static SharedPreferences getMultiDexPreferences(Context context) {\n        return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? Context.MODE_PRIVATE : Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);\n    }\n\n    /**\n     * 通过指定包名，扫描包下面包含的所有的ClassName\n     */\n    public static List<String> getFileNameByPackageName(Context context, String packageName) throws PackageManager.NameNotFoundException, IOException {\n        List<String> classNames = new ArrayList<>();\n        for (String path : getSourcePaths(context)) {\n            DexFile dexfile;\n            if (path.endsWith(EXTRACTED_SUFFIX)) {\n                //NOT use new DexFile(path), because it will throw \"permission error in /data/dalvik-cache\"\n                dexfile = DexFile.loadDex(path, path + \".tmp\", 0);\n            } else {\n                dexfile = new DexFile(path);\n            }\n            Enumeration<String> dexEntries = dexfile.entries();\n            while (dexEntries.hasMoreElements()) {\n                String className = dexEntries.nextElement();\n                if (className.contains(packageName)) {\n                    classNames.add(className);\n                }\n            }\n        }\n\n        DRouter.logger.d(Consts.TAG, \"Scan \" + classNames.size() + \" classes by packageName <\" + packageName + \">\");\n        return classNames;\n    }\n\n    /**\n     * get all the dex path\n     *\n     * @param context the application context\n     * @return all the dex path\n     * @throws PackageManager.NameNotFoundException\n     * @throws IOException\n     */\n    public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {\n        ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);\n        File sourceApk = new File(applicationInfo.sourceDir);\n\n        List<String> sourcePaths = new ArrayList<>();\n        sourcePaths.add(applicationInfo.sourceDir); //add the default apk path\n\n        //the prefix of extracted file, ie: test.classes\n        String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;\n\n        if (!isVMMultidexCapable()) {\n            //the total dex numbers\n            int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);\n            File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);\n\n            for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {\n                //for each dex file, ie: test.classes2.zip, test.classes3.zip...\n                String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;\n                File extractedFile = new File(dexDir, fileName);\n                if (extractedFile.isFile()) {\n                    sourcePaths.add(extractedFile.getAbsolutePath());\n                    //we ignore the verify zip part\n                } else {\n                    throw new IOException(\"Missing extracted secondary dex file '\" + extractedFile.getPath() + \"'\");\n                }\n            }\n        }\n\n        return sourcePaths;\n    }\n\n    /**\n     * Identifies if the current VM has a native support for multidex, meaning there is no need for\n     * additional installation by this library.\n     *\n     * @return true if the VM handles multidex\n     */\n    private static boolean isVMMultidexCapable() {\n        boolean isMultidexCapable = false;\n        String vmName = null;\n\n        try {\n            if (isYunOS()) {\n                vmName = \"'YunOS'\";\n                isMultidexCapable = Integer.valueOf(System.getProperty(\"ro.build.version.sdk\")) >= 21;\n            } else {\n                vmName = \"'Android'\";\n                String versionString = System.getProperty(\"java.vm.version\");\n                if (versionString != null) {\n                    Matcher matcher = Pattern.compile(\"(\\\\d+)\\\\.(\\\\d+)(\\\\.\\\\d+)?\").matcher(versionString);\n                    if (matcher.matches()) {\n                        try {\n                            int major = Integer.parseInt(matcher.group(1));\n                            int minor = Integer.parseInt(matcher.group(2));\n                            isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)\n                                    || ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)\n                                    && (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));\n                        } catch (NumberFormatException ignore) {\n                            // let isMultidexCapable be false\n                        }\n                    }\n                }\n            }\n        } catch (Exception ignore) {\n\n        }\n        return isMultidexCapable;\n    }\n\n    /**\n     * 判断系统是否为YunOS系统\n     */\n    private static boolean isYunOS() {\n        try {\n            String version = System.getProperty(\"ro.yunos.version\");\n            String vmName = System.getProperty(\"java.vm.name\");\n            return (vmName != null && vmName.toLowerCase().contains(\"lemur\"))\n                    || (version != null && version.trim().length() > 0);\n        } catch (Exception ignore) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/java/com/drouter/api/utils/MapUtils.java",
    "content": "package com.drouter.api.utils;\n\nimport com.drouter.api.interceptor.ActionInterceptor;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/24 16:57\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class MapUtils {\n\n    public static List<ActionInterceptor> getInterceptorClasses(Map<Integer, ActionInterceptor> map) {\n        List<ActionInterceptor> list = new ArrayList();\n\n        for (Object key : map.keySet()) {\n            list.add(map.get(key));\n        }\n\n        return list;\n    }\n}\n"
  },
  {
    "path": "drouter-api/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">drouter-api</string>\n</resources>\n"
  },
  {
    "path": "drouter-api/src/test/java/com/drouter/api/ExampleUnitTest.java",
    "content": "package com.drouter.api;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "drouter-base/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "drouter-base/build.gradle",
    "content": "apply plugin: 'java-library'\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n}\n\nsourceCompatibility = \"1.7\"\ntargetCompatibility = \"1.7\"\n"
  },
  {
    "path": "drouter-base/src/main/java/com/drouter/base/ThreadMode.java",
    "content": "package com.drouter.base;\n\n/**\n * description: thanks EventBus\n * author: Darren on 2018/1/23 08:57\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic enum ThreadMode {\n    /**\n     * Action will be called directly in the same thread, which is posting the event. This is the default. Event delivery\n     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for\n     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers\n     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.\n     */\n    POSTING,\n\n    /**\n     * On Android, action will be called in Android's main thread (UI thread). If the posting thread is\n     * the main thread, action methods will be called directly, blocking the posting thread. Otherwise the event\n     * is queued for delivery (non-blocking). Action using this mode must return quickly to avoid blocking the main thread.\n     * If not on Android, behaves the same as {@link #POSTING}.\n     */\n    MAIN,\n\n    /**\n     * On Android, action will be called in a background thread. If posting thread is not the main thread, action methods\n     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single\n     * background thread, that will deliver all its events sequentially. Action using this mode should try to\n     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.\n     */\n    BACKGROUND,\n\n    /**\n     * Action will be called in a separate thread. This is always independent from the posting thread and the\n     * main thread. Posting events never wait for action methods using this mode. Action methods should\n     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number\n     * of long running asynchronous action methods at the same time to limit the number of concurrent threads. EventBus\n     * uses a thread pool to efficiently reuse threads from completed asynchronous action notifications.\n     */\n    ASYNC\n}\n"
  },
  {
    "path": "drouter-base/src/main/java/com/drouter/base/annotation/Action.java",
    "content": "package com.drouter.base.annotation;\n\nimport com.drouter.base.ThreadMode;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * description: 模块注册 apt\n * author: Darren on 2018/1/22 12:32\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.CLASS)\npublic @interface Action {\n    /**\n     * thread mode\n     */\n    ThreadMode threadMode() default ThreadMode.POSTING;\n\n    /**\n     * Path of route\n     */\n    String path();\n\n    /**\n     * extra process\n     */\n    boolean extraProcess() default false;\n}\n"
  },
  {
    "path": "drouter-base/src/main/java/com/drouter/base/annotation/Interceptor.java",
    "content": "package com.drouter.base.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * description:\n * author: Darren on 2018/1/24 16:10\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.CLASS)\npublic @interface Interceptor {\n    int priority();\n}\n"
  },
  {
    "path": "drouter-compiler/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "drouter-compiler/build.gradle",
    "content": "apply plugin: 'java-library'\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation project(':drouter-base')\n    implementation 'com.google.auto.service:auto-service:1.0-rc2'\n    implementation 'com.squareup:javapoet:1.7.0'\n}\n\ntasks.withType(JavaCompile){\n    options.encoding='UTF-8'\n}\n\nsourceCompatibility = \"1.7\"\ntargetCompatibility = \"1.7\""
  },
  {
    "path": "drouter-compiler/src/main/java/com/drouter/compiler/Consts.java",
    "content": "package com.drouter.compiler;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:01\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class Consts {\n    public static final String SDK_NAME = \"DRouter\";\n    public static final String TAG = SDK_NAME;\n    public static final String SUFFIX_INTERCEPTORS = \"Interceptors\";\n    public static final String ROUTER_MODULE_PACK_NAME = \"com.drouter.assist.module\";\n    public static final String ROUTER_INTERCEPTOR_PACK_NAME = \"com.drouter.assist.interceptor\";\n    public static final String ACTIONINTERCEPTOR = \"com.drouter.api.interceptor.ActionInterceptor\";\n    public static final String ROUTERACTION = \"com.drouter.api.action.IRouterAction\";\n}\n"
  },
  {
    "path": "drouter-compiler/src/main/java/com/drouter/compiler/InterceptorProcessor.java",
    "content": "package com.drouter.compiler;\n\nimport com.drouter.base.annotation.Interceptor;\nimport com.drouter.compiler.util.TextUtils;\nimport com.google.auto.service.AutoService;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.TypeSpec;\n\nimport java.io.IOException;\nimport java.lang.annotation.Annotation;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.annotation.processing.AbstractProcessor;\nimport javax.annotation.processing.Filer;\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.TypeMirror;\nimport javax.lang.model.util.Elements;\nimport javax.tools.Diagnostic;\n\n/**\n * description:\n * author: Darren on 2018/1/22 12:29\n * email: 240336124@qq.com\n * version: 1.0\n */\n@AutoService(Processor.class)\npublic class InterceptorProcessor extends AbstractProcessor {\n    private Elements mElementUtils;\n    private Filer mFiler;\n    private final String KEY_MODULE_NAME = \"moduleName\";\n    private TypeMirror iInterceptor = null;\n    private String moduleName = null;\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnvironment) {\n        super.init(processingEnvironment);\n        mFiler = processingEnvironment.getFiler();\n        mElementUtils = processingEnvironment.getElementUtils();\n        iInterceptor = mElementUtils.getTypeElement(Consts.ACTIONINTERCEPTOR).asType();\n    }\n\n    @Override\n    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {\n\n        // 1. 有没配置 modelName 防止 class 类冲突\n        String moduleName = \"\";\n\n        Map<String, String> options = processingEnv.getOptions();\n        if (isNotEmpty(options)) {\n            moduleName = options.get(KEY_MODULE_NAME);\n        }\n\n        if (!TextUtils.isEmpty(moduleName)) {\n            moduleName = moduleName.replaceAll(\"[^0-9a-zA-Z_]+\", \"\");\n        } else {\n            String errorMessage = \"These no module name, at 'build.gradle', like :\\n\" +\n                    \"apt {\\n\" +\n                    \"    arguments {\\n\" +\n                    \"        moduleName project.getName();\\n\" +\n                    \"    }\\n\" +\n                    \"}\\n\";\n            throw new RuntimeException(\"DRouter::Compiler >>> No module name, for more information, look at gradle log.\\n\" + errorMessage);\n        }\n\n        // 生成类继承和实现接口\n        ClassName routerAssistClassName = ClassName.get(\"com.drouter.api.action\", \"IRouterInterceptor\");\n        ClassName mapClassName = ClassName.get(\"java.util\", \"Map\");\n        TypeSpec.Builder classBuilder = TypeSpec.classBuilder(\"DRouter$$Interceptor$$\" + moduleName)\n                .addModifiers(Modifier.FINAL, Modifier.PUBLIC)\n                .addSuperinterface(routerAssistClassName)\n                .addField(mapClassName, \"interceptors\", Modifier.PRIVATE);\n\n        // 构造函数\n        MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);\n        constructorMethodBuilder.addStatement(\"interceptors = new $T<>()\", ClassName.get(\"java.util\", \"TreeMap\"));\n\n        // 2. 解析到所有的 Action 信息\n        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Interceptor.class);\n        Map<Integer, String> interceptors = new HashMap<>(elements.size());\n\n        for (Element element : elements) {\n\n            // 获取注解上面的 priority\n            Interceptor interceptorAnnotation = element.getAnnotation(Interceptor.class);\n            int priority = interceptorAnnotation.priority();\n\n            // 获取 Interceptor 的 ClassName\n            Element enclosingElement = element.getEnclosingElement();\n            String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();\n            String interceptorClassName = packageName + \".\" + element.getSimpleName();\n\n            // 判断 Interceptor 注解类是否实现了 ActionInterceptor\n            if (!((TypeElement) element).getInterfaces().contains(iInterceptor)) {\n                error(element, \"%s verify failed, @Interceptor must be implements %s\", element.getSimpleName().toString(), Consts.ACTIONINTERCEPTOR);\n            }\n\n            if (interceptors.containsKey(priority)) {\n                // 输出错误，拦截器优先级 冲突重复了\n                error(element, \"More than one interceptors use same priority <%s> , The last interceptor was <%s>\", String.valueOf(priority), interceptors.get(priority));\n            }\n            // 添加到集合\n            interceptors.put(priority, interceptorClassName);\n\n            constructorMethodBuilder.addStatement(\"this.interceptors.put(\" + priority + \",new $T())\", ClassName.bestGuess(interceptorClassName));\n        }\n\n        // 实现方法\n        MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder(\"getInterceptors\")\n                .addAnnotation(Override.class)\n                .returns(List.class)\n                .addModifiers(Modifier.PUBLIC, Modifier.FINAL);\n        unbindMethodBuilder.addStatement(\"return $T.getInterceptorClasses(interceptors)\", ClassName.get(\"com.drouter.api.utils\", \"MapUtils\"));\n\n        classBuilder.addMethod(constructorMethodBuilder.build());\n        classBuilder.addMethod(unbindMethodBuilder.build());\n\n        // 生成类，看下效果\n        try {\n            JavaFile.builder(Consts.ROUTER_INTERCEPTOR_PACK_NAME, classBuilder.build())\n                    .addFileComment(\"DRouter 自动生成\")\n                    .build().writeTo(mFiler);\n        } catch (IOException e) {\n            e.printStackTrace();\n            System.out.println(\"翻车了！\");\n        }\n\n        return false;\n    }\n\n\n    private boolean isNotEmpty(Map<String, String> options) {\n        return options != null && !options.isEmpty();\n    }\n\n    private void error(Element element, String message, String... args) {\n        printMessage(Diagnostic.Kind.ERROR, element, message, args);\n    }\n\n    private void printMessage(Diagnostic.Kind kind, Element element, String message, Object[] args) {\n        if (args.length > 0) {\n            message = String.format(message, args);\n        }\n\n        processingEnv.getMessager().printMessage(kind, message, element);\n    }\n\n\n    // 1. 指定处理的版本\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n    // 2. 给到需要处理的注解\n    @Override\n    public Set<String> getSupportedAnnotationTypes() {\n        Set<String> types = new LinkedHashSet<>();\n        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {\n            types.add(annotation.getCanonicalName());\n        }\n        return types;\n    }\n\n    private Set<Class<? extends Annotation>> getSupportedAnnotations() {\n        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();\n        // 需要解析的自定义注解 BindView  OnClick\n        annotations.add(Interceptor.class);\n        return annotations;\n    }\n}\n"
  },
  {
    "path": "drouter-compiler/src/main/java/com/drouter/compiler/ModuleProcessor.java",
    "content": "package com.drouter.compiler;\n\nimport com.drouter.base.annotation.Action;\nimport com.drouter.compiler.util.TextUtils;\nimport com.google.auto.service.AutoService;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.TypeSpec;\n\nimport java.io.IOException;\nimport java.lang.annotation.Annotation;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.annotation.processing.AbstractProcessor;\nimport javax.annotation.processing.Filer;\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.TypeMirror;\nimport javax.lang.model.util.Elements;\nimport javax.tools.Diagnostic;\n\n/**\n * description:\n * author: Darren on 2018/1/22 12:29\n * email: 240336124@qq.com\n * version: 1.0\n */\n@AutoService(Processor.class)\npublic class ModuleProcessor extends AbstractProcessor {\n    private Elements mElementUtils;\n    private Filer mFiler;\n    private final String KEY_MODULE_NAME = \"moduleName\";\n    private TypeMirror iRouterAction = null;\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnvironment) {\n        super.init(processingEnvironment);\n        mFiler = processingEnvironment.getFiler();\n        mElementUtils = processingEnvironment.getElementUtils();\n        iRouterAction = mElementUtils.getTypeElement(Consts.ROUTERACTION).asType();\n    }\n\n    @Override\n    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {\n\n        // 1. 有没配置 modelName 防止 class 类冲突\n        String moduleName = \"\";\n\n        Map<String, String> options = processingEnv.getOptions();\n        if (isNotEmpty(options)) {\n            moduleName = options.get(KEY_MODULE_NAME);\n        }\n\n        System.out.println(\"moduleName = \" + moduleName);\n\n        if (!TextUtils.isEmpty(moduleName)) {\n            moduleName = moduleName.replaceAll(\"[^0-9a-zA-Z_]+\", \"\");\n        } else {\n            String errorMessage = \"These no module name, at 'build.gradle', like :\\n\" +\n                    \"apt {\\n\" +\n                    \"    arguments {\\n\" +\n                    \"        moduleName project.getName();\\n\" +\n                    \"    }\\n\" +\n                    \"}\\n\";\n            throw new RuntimeException(\"DRouter::Compiler >>> No module name, for more information, look at gradle log.\\n\" + errorMessage);\n        }\n\n        // 3. 生成 Java 类，效果如下\n        /*public class DRouter$$Assist implements IRouterAssist {\n\n            Map<String, String> modules = new HashMap<>();\n\n            public DRouter$$Assist() {\n                modules.put(\"login/module\", \"com.login.module.LoginModule\");\n            }\n\n            @Override\n            public String findModuleClassName(String moduleName) {\n                return modules.get(moduleName);\n            }\n        }*/\n        // 生成类继承和实现接口\n        ClassName routerAssistClassName = ClassName.get(\"com.drouter.api.action\", \"IRouterModule\");\n        ClassName mapClassName = ClassName.get(\"java.util\", \"Map\");\n        TypeSpec.Builder classBuilder = TypeSpec.classBuilder(\"DRouter$$Module$$\" + moduleName)\n                .addModifiers(Modifier.FINAL, Modifier.PUBLIC)\n                .addSuperinterface(routerAssistClassName)\n                .addField(mapClassName, \"actions\", Modifier.PRIVATE);\n\n        // 构造函数\n        MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC);\n        constructorMethodBuilder.addStatement(\"actions = new $T<>()\", ClassName.bestGuess(\"java.util.HashMap\"));\n\n        // 2. 解析到所有的 Action 信息\n        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Action.class);\n        Map<String, String> modules = new HashMap<>(elements.size());\n\n        ClassName actionWrapperClassName = ClassName.get(\"com.drouter.api.extra\", \"ActionWrapper\");\n        ClassName threadModeClassName = ClassName.get(\"com.drouter.base\", \"ThreadMode\");\n        for (Element element : elements) {\n\n            // 获取注解上面的 action\n            Action actionAnnotation = element.getAnnotation(Action.class);\n            String actionName = actionAnnotation.path();\n\n            // 必须以配置的 gradle 包名开头\n            if (!actionName.startsWith(moduleName + \"/\")) {\n                error(element, \"Path name of the action must begin with %s%s\", moduleName, \"/\");\n            }\n            // 获取 Action 的 ClassName\n            Element enclosingElement = element.getEnclosingElement();\n            String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();\n            String actionClassName = packageName + \".\" + element.getSimpleName();\n\n            // 判断 Interceptor 注解类是否实现了 ActionInterceptor\n            if (!((TypeElement) element).getInterfaces().contains(iRouterAction)) {\n                error(element, \"%s verify failed, @Action must be implements %s\", element.getSimpleName().toString(), Consts.ROUTERACTION);\n            }\n\n            if (modules.containsKey(actionName)) {\n                // 输出错误，Action 名称冲突重复了\n                error(element, \"%s module name already exists\", actionName);\n            }\n            // 添加到集合\n            modules.put(actionName, actionClassName);\n\n            constructorMethodBuilder.addStatement(\"this.actions.put($S,$T.build($T.class, $S, \"\n                            + actionAnnotation.extraProcess() + \", $T.\" + actionAnnotation.threadMode() + \"))\",\n                    actionName, actionWrapperClassName, ClassName.bestGuess(actionClassName), actionName, threadModeClassName);\n        }\n\n        // 实现方法\n\n        MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder(\"findAction\")\n                .addParameter(String.class, \"actionName\")\n                .addAnnotation(Override.class)\n                .returns(actionWrapperClassName)\n                .addModifiers(Modifier.PUBLIC, Modifier.FINAL);\n        unbindMethodBuilder.addStatement(\"return (ActionWrapper)actions.get(actionName)\");\n\n        classBuilder.addMethod(constructorMethodBuilder.build());\n        classBuilder.addMethod(unbindMethodBuilder.build());\n\n        // 生成类，看下效果\n        try {\n            JavaFile.builder(Consts.ROUTER_MODULE_PACK_NAME, classBuilder.build())\n                    .addFileComment(\"DRouter 自动生成\")\n                    .build().writeTo(mFiler);\n        } catch (IOException e) {\n            e.printStackTrace();\n            System.out.println(\"翻车了！\");\n        }\n\n        return false;\n    }\n\n\n    private boolean isNotEmpty(Map<String, String> options) {\n        return options != null && !options.isEmpty();\n    }\n\n    private void error(Element element, String message, String... args) {\n        printMessage(Diagnostic.Kind.ERROR, element, message, args);\n    }\n\n    private void printMessage(Diagnostic.Kind kind, Element element, String message, Object[] args) {\n        if (args.length > 0) {\n            message = String.format(message, args);\n        }\n\n        processingEnv.getMessager().printMessage(kind, message, element);\n    }\n\n\n    // 1. 指定处理的版本\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n    // 2. 给到需要处理的注解\n    @Override\n    public Set<String> getSupportedAnnotationTypes() {\n        Set<String> types = new LinkedHashSet<>();\n        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {\n            types.add(annotation.getCanonicalName());\n        }\n        return types;\n    }\n\n    private Set<Class<? extends Annotation>> getSupportedAnnotations() {\n        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();\n        // 需要解析的自定义注解 BindView  OnClick\n        annotations.add(Action.class);\n        return annotations;\n    }\n}\n"
  },
  {
    "path": "drouter-compiler/src/main/java/com/drouter/compiler/util/TextUtils.java",
    "content": "package com.drouter.compiler.util;\n\n/**\n * description:\n * author: Darren on 2018/1/22 17:45\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class TextUtils {\n    public static boolean isEmpty(String moduleName) {\n        return moduleName == null || moduleName.isEmpty();\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Jan 22 09:42:50 CST 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "login-module/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "login-module/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n\n    repositories {\n        mavenCentral()\n    }\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [moduleName: \"login\"]\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    implementation 'com.android.support:appcompat-v7:26.1.0'\n    testImplementation 'junit:junit:4.12'\n    compile project(':base-core')\n    annotationProcessor project(':drouter-compiler')\n}\n"
  },
  {
    "path": "login-module/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "login-module/src/androidTest/java/com/login/module/ExampleInstrumentedTest.java",
    "content": "package com.login.module;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.login.module.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "login-module/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.login.module\" >\n    <application\n        android:allowBackup=\"true\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\">\n        <activity android:name=\".LoginActivity\"/>\n    </application>\n</manifest>\n"
  },
  {
    "path": "login-module/src/main/java/com/login/module/LoginAction.java",
    "content": "package com.login.module;\n\nimport android.content.Context;\nimport android.content.Intent;\n\nimport com.drouter.api.action.IRouterAction;\nimport com.drouter.api.result.RouterResult;\nimport com.drouter.base.ThreadMode;\nimport com.drouter.base.annotation.Action;\n\nimport java.util.Map;\n\n/**\n * description:\n * author: Darren on 2018/1/22 10:57\n * email: 240336124@qq.com\n * version: 1.0\n */\n@Action(path = \"login/action\", threadMode = ThreadMode.MAIN)\npublic class LoginAction implements IRouterAction {\n\n    @Override\n    public RouterResult invokeAction(Context context, Map<String, Object> requestData) {\n        Intent intent = new Intent(context, LoginActivity.class);\n        intent.putExtra(\"key\", (String) requestData.get(\"key\"));\n        context.startActivity(intent);\n        return new RouterResult.Builder().success().object(100).build();\n    }\n}\n"
  },
  {
    "path": "login-module/src/main/java/com/login/module/LoginActivity.java",
    "content": "package com.login.module;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.drouter.api.core.DRouter;\nimport com.drouter.api.result.ActionCallback;\nimport com.drouter.api.result.RouterResult;\n\n/**\n * description:\n * author: Darren on 2018/1/22 15:08\n * email: 240336124@qq.com\n * version: 1.0\n */\npublic class LoginActivity extends AppCompatActivity {\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_login);\n\n        String key = getIntent().getStringExtra(\"key\");\n        Toast.makeText(this, key, Toast.LENGTH_LONG).show();\n    }\n\n    public void click(View view){\n        DRouter.getInstance()\n                .action(\"circlemodule/test\")\n                .context(this)\n                .param(\"key\", \"value\")\n                .invokeAction(new ActionCallback() {\n                    @Override\n                    public void onInterrupt() {\n                        Log.e(\"TAG\", \"被拦截了\");\n                    }\n\n                    @Override\n                    public void onResult(RouterResult result) {\n                        Log.e(\"TAG\", \"result = \" + result.toString());\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "login-module/src/main/res/layout/activity_login.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/test\"\n        android:padding=\"10dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:text=\"登录模块\" />\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:onClick=\"click\"\n        android:text=\"点击跳转到圈子模块\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "login-module/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">login-module</string>\n</resources>\n"
  },
  {
    "path": "login-module/src/test/java/com/login/module/ExampleUnitTest.java",
    "content": "package com.login.module;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':drouter-api', ':login-module', ':base-core', ':drouter-compiler', ':drouter-base', ':circle-module'\n"
  }
]