[
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "## Look at here\n\n- → Did you read the doc carefully\n- → Did you add annotation above target activity\n- → Did you add annotation processor dependence\n- → **Receive only bugs and suggestions**\n\n## 提 issue 前请看\n\n- → 你是否已经熟读 [README](https://github.com/alibaba/ARouter/blob/master/README.md) ？\n- → 你是否在每一个包含页面 or 服务的模块中依赖了 compiler sdk\n- → **这里不是答疑的地方，仅接受 bug 和建议，答疑请去答疑群**"
  },
  {
    "path": ".github/workflows/gradle-wrapper-validation.yml",
    "content": "name: \"Validate Gradle Wrapper\"\non: [push, pull_request]\n\njobs:\n  validation:\n    name: \"Validation\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: gradle/wrapper-validation-action@v1\n"
  },
  {
    "path": ".gitignore",
    "content": "# Built application files\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\nbuild/\n/*/build/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# IDEA Files\n.idea\n*.iml\n/*.ipr\n/*.iws\n.project\n.classpath\n\n# Eclipse Files\n/target\n*.classpath\n*.project\n*.settings\n\napp/src/main/res/values/config.xml\napp/src/main/res/drawable/yw_1222.jpg\n\n# OS X Files\n.DS_Store\n\nmap.txt\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "```\n    A framework for assisting in the renovation of Android app componentization\n```\n\n[中文文档](https://github.com/alibaba/ARouter/blob/master/README_CN.md)\n\n##### [![Join the chat at https://gitter.im/alibaba/ARouter](https://badges.gitter.im/alibaba/ARouter.svg)](https://gitter.im/alibaba/ARouter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\n---\n\n#### Lastest version\n\nmodule|arouter-api|arouter-compiler|arouter-register|arouter-idea-plugin\n---|---|---|---|---\nversion|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-api)|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-compiler/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-compiler)|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-register/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-register)|[![as plugin](https://img.shields.io/jetbrains/plugin/d/11428-arouter-helper.svg)](https://plugins.jetbrains.com/plugin/11428-arouter-helper)\n\n#### Demo\n\n##### [Demo apk](https://github.com/alibaba/ARouter/blob/develop/demo/arouter-demo-1.5.2.apk)、[Demo Gif](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/arouter-demo.gif)\n\n#### I. Feature\n1. **Supports direct parsing of standard URLs for jumps and automatic injection of parameters into target pages**\n2. **Support for multi-module**\n3. **Support for interceptor**\n4. **Support for dependency injection**\n5. **InstantRun support**\n6. **MultiDex support**\n7. Mappings are grouped by group, multi-level management, on-demand initialization\n8. Supports users to specify global demotion and local demotion strategies\n9. Activity, interceptor and service can be automatically registered to the framework\n10. Support multiple ways to configure transition animation\n11. Support for fragment\n12. Full kotlin support (Look at Other#2)\n13. **Generate route doc support**\n14. **Provide IDE plugin for quick navigation to target class**\n15. Support Incremental annotation processing\n16. Support register route meta dynamic.\n\n#### II. Classic Case\n1. Forward from external URLs to internal pages, and parsing parameters\n2. Jump and decoupling between multi-module\n3. Intercept jump process, handle login, statistics and other logic\n4. Cross-module communication, decouple components by IoC\n\n#### III. Configuration\n1. Adding dependencies and configurations\n    ``` gradle\n    android {\n        defaultConfig {\n            ...\n            javaCompileOptions {\n                annotationProcessorOptions {\n                    arguments = [AROUTER_MODULE_NAME: project.getName()]\n                }\n            }\n        }\n    }\n\n    dependencies {\n        // Replace with the latest version\n        compile 'com.alibaba:arouter-api:?'\n        annotationProcessor 'com.alibaba:arouter-compiler:?'\n        ...\n    }\n    // Old version of gradle plugin (< 2.2), You can use apt plugin, look at 'Other#1'\n    // Kotlin configuration reference 'Other#2'\n    ```\n\n2. Add annotations\n    ``` java\n    // Add annotations on pages that support routing (required)\n    // The path here needs to pay attention to need at least two levels : /xx/xx\n    @Route(path = \"/test/activity\")\n    public class YourActivity extend Activity {\n        ...\n    }\n    ```\n\n3. Initialize the SDK\n    ``` java\n    if (isDebug()) {           // These two lines must be written before init, otherwise these configurations will be invalid in the init process\n        ARouter.openLog();     // Print log\n        ARouter.openDebug();   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)\n    }\n    ARouter.init(mApplication); // As early as possible, it is recommended to initialize in the Application\n    ```\n\n4. Initiate the routing\n    ``` java\n    // 1. Simple jump within application (Jump via URL in 'Advanced usage')\n    ARouter.getInstance().build(\"/test/activity\").navigation();\n\n    // 2. Jump with parameters\n    ARouter.getInstance().build(\"/test/1\")\n                .withLong(\"key1\", 666L)\n                .withString(\"key3\", \"888\")\n                .withObject(\"key4\", new Test(\"Jack\", \"Rose\"))\n                .navigation();\n    ```\n\n5. Add confusing rules (If Proguard is turn on)\n    ``` \n    -keep public class com.alibaba.android.arouter.routes.**{*;}\n    -keep public class com.alibaba.android.arouter.facade.**{*;}\n    -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}\n\n    # If you use the byType method to obtain Service, add the following rules to protect the interface:\n    -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider\n\n    # If single-type injection is used, that is, no interface is defined to implement IProvider, the following rules need to be added to protect the implementation\n    # -keep class * implements com.alibaba.android.arouter.facade.template.IProvider\n    ```\n\n6. Using the custom gradle plugin to autoload the routing table\n    ```gradle\n    apply plugin: 'com.alibaba.arouter'\n\n    buildscript {\n        repositories {\n            mavenCentral()\n        }\n\n        dependencies {\n            // Replace with the latest version\n            classpath \"com.alibaba:arouter-register:?\"\n        }\n    }\n    ```\n\n    Optional, use the registration plugin provided by the ARouter to automatically load the routing table(power by [AutoRegister](https://github.com/luckybilly/AutoRegister)). By default, the ARouter will scanned the dex files .\n    Performing an auto-registration via the gradle plugin can shorten the initialization time , it should be noted that the plugin must be used with api above 1.3.0!\n\n7. use ide plugin for quick navigation to target class (Optional)\n\n    Search for `ARouter Helper` in the Android Studio plugin market, or directly download the `arouter-idea-plugin` zip installation package listed in the `Latest version` above the documentation, after installation\n    plugin without any settings, U can find an icon at the beginning of the jump code. (![navigation](https://raw.githubusercontent.com/alibaba/ARouter/develop/arouter-idea-plugin/src/main/resources/icon/outline_my_location_black_18dp.png)) click the icon to jump to the target class that identifies the path in the code.\n\n#### IV. Advanced usage\n1. Jump via URL\n    ``` java\n    // Create a new Activity for monitoring Scheme events, and then directly pass url to ARouter\n    public class SchemeFilterActivity extends Activity {\n        @Override\n        protected void onCreate(Bundle savedInstanceState) {\n            super.onCreate(savedInstanceState);\n\n            Uri uri = getIntent().getData();\n            ARouter.getInstance().build(uri).navigation();\n            finish();\n        }\n    }\n    ```\n\n    AndroidManifest.xml\n    ``` xml\n    <activity android:name=\".activity.SchemeFilterActivity\">\n        <!-- Scheme -->\n        <intent-filter>\n            <data\n                android:host=\"m.aliyun.com\"\n                android:scheme=\"arouter\"/>\n\n            <action android:name=\"android.intent.action.VIEW\"/>\n\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <category android:name=\"android.intent.category.BROWSABLE\"/>\n        </intent-filter>\n    </activity>\n    ```\n\n2. Parse the parameters in the URL\n    ``` java\n    // Declare a field for each parameter and annotate it with @Autowired\n    @Route(path = \"/test/activity\")\n    public class Test1Activity extends Activity {\n        @Autowired\n        public String name;\n        @Autowired\n        int age;\n        @Autowired(name = \"girl\") // Map different parameters in the URL by name\n        boolean boy;\n        @Autowired\n        TestObj obj;    // Support for parsing custom objects, using json pass in URL\n\n        @Override\n        protected void onCreate(Bundle savedInstanceState) {\n            super.onCreate(savedInstanceState);\n            ARouter.getInstance().inject(this);\n\n            // ARouter will automatically set value of fields\n            Log.d(\"param\", name + age + boy);\n        }\n    }\n\n    // If you need to pass a custom object, Create a new class(Not the custom object class),implement the SerializationService, And use the @Route annotation annotation, E.g:\n    @Route(path = \"/yourservicegroupname/json\")\n    public class JsonServiceImpl implements SerializationService {\n        @Override\n        public void init(Context context) {\n\n        }\n\n        @Override\n        public <T> T json2Object(String text, Class<T> clazz) {\n            return JSON.parseObject(text, clazz);\n        }\n\n        @Override\n        public String object2Json(Object instance) {\n            return JSON.toJSONString(instance);\n        }\n    }\n    ```\n\n3. Declaration Interceptor (Intercept jump process, AOP)\n    ``` java\n    // A more classic application is to handle login events during a jump so that there is no need to repeat the login check on the target page.\n    // Interceptors will be executed between jumps, multiple interceptors will be executed in order of priority\n    @Interceptor(priority = 8, name = \"test interceptor\")\n    public class TestInterceptor implements IInterceptor {\n        @Override\n        public void process(Postcard postcard, InterceptorCallback callback) {\n            ...\n            // No problem! hand over control to the framework\n            callback.onContinue(postcard);  \n            \n            // Interrupt routing process\n            // callback.onInterrupt(new RuntimeException(\"Something exception\"));      \n\n            // The above two types need to call at least one of them, otherwise it will not continue routing\n        }\n\n        @Override\n        public void init(Context context) {\n            // Interceptor initialization, this method will be called when sdk is initialized, it will only be called once\n        }\n    }\n    ```\n\n4. Processing jump results\n    ``` java\n    // U can get the result of a single jump\n    ARouter.getInstance().build(\"/test/1\").navigation(this, new NavigationCallback() {\n        @Override\n        public void onFound(Postcard postcard) {\n        ...\n        }\n\n        @Override\n        public void onLost(Postcard postcard) {\n        ...\n        }\n    });\n    ```\n\n5. Custom global demotion strategy\n    ``` java\n    // Implement the DegradeService interface\n    @Route(path = \"/xxx/xxx\")\n    public class DegradeServiceImpl implements DegradeService {\n        @Override\n        public void onLost(Context context, Postcard postcard) {\n            // do something.\n        }\n\n        @Override\n        public void init(Context context) {\n\n        }\n    }\n    ```\n\n6. Decoupled by dependency injection : Service management -- Exposure services\n    ``` java\n    // Declaration interface, other components get the service instance through the interface\n    public interface HelloService extends IProvider {\n        String sayHello(String name);\n    }\n\n    @Route(path = \"/yourservicegroupname/hello\", name = \"test service\")\n    public class HelloServiceImpl implements HelloService {\n\n        @Override\n        public String sayHello(String name) {\n            return \"hello, \" + name;\n        }\n\n        @Override\n        public void init(Context context) {\n\n        }\n    }\n    ```\n\n7. Decoupled by dependency injection : Service management -- Discovery service\n    ``` java\n    public class Test {\n        @Autowired\n        HelloService helloService;\n\n        @Autowired(name = \"/yourservicegroupname/hello\")\n        HelloService helloService2;\n\n        HelloService helloService3;\n\n        HelloService helloService4;\n\n        public Test() {\n            ARouter.getInstance().inject(this);\n        }\n\n        public void testService() {\n            // 1. Use Dependency Injection to discover services, annotate fields with annotations\n            helloService.sayHello(\"Vergil\");\n            helloService2.sayHello(\"Vergil\");\n\n            // 2. Discovering services using dependency lookup, the following two methods are byName and byType\n            helloService3 = ARouter.getInstance().navigation(HelloService.class);\n            helloService4 = (HelloService) ARouter.getInstance().build(\"/yourservicegroupname/hello\").navigation();\n            helloService3.sayHello(\"Vergil\");\n            helloService4.sayHello(\"Vergil\");\n        }\n    }\n    ```\n  \n8. Pretreatment Service\n    ``` java\n    @Route(path = \"/xxx/xxx\")\n    public class PretreatmentServiceImpl implements PretreatmentService {\n        @Override\n        public boolean onPretreatment(Context context, Postcard postcard) {\n            // Do something before the navigation, if you need to handle the navigation yourself, the method returns false\n        }\n\n        @Override\n        public void init(Context context) {\n    \n        }\n    }\n    ```\n\n9. Dynamic register route meta\nApplicable to apps with plug-in architectures or some scenarios where routing information\nneeds to be dynamically registered，Dynamic registration can be achieved through the\ninterface provided by ARouter, The target page and service need not be marked with @Route\nannotation，**Only the routing information of the same group can be registered in the same batch**\n    ``` java\n        ARouter.getInstance().addRouteGroup(new IRouteGroup() {\n            @Override\n            public void loadInto(Map<String, RouteMeta> atlas) {\n                atlas.put(\"/dynamic/activity\",      // path\n                    RouteMeta.build(\n                        RouteType.ACTIVITY,         // Route type\n                        TestDynamicActivity.class,  // Target class\n                        \"/dynamic/activity\",        // Path\n                        \"dynamic\",                  // Group\n                        0,                          // not need\n                        0                           // Extra tag, Used to mark page feature\n                    )\n                );\n            }\n        });\n    ```\n\n#### V. More features\n\n1. Other settings in initialization\n    ``` java\n    ARouter.openLog(); // Open log\n    ARouter.openDebug(); // When using InstantRun, you need to open this switch and turn it off after going online. Otherwise, there is a security risk.\n    ARouter.printStackTrace(); // Print thread stack when printing logs\n    ```\n\n2. API description\n    ``` java\n    // Build a standard route request\n    ARouter.getInstance().build(\"/home/main\").navigation();\n\n    // Build a standard route request, via URI\n    Uri uri;\n    ARouter.getInstance().build(uri).navigation();\n\n    // Build a standard route request, startActivityForResult\n    // The first parameter must be Activity and the second parameter is RequestCode\n    ARouter.getInstance().build(\"/home/main\", \"ap\").navigation(this, 5);\n\n    // Pass Bundle directly\n    Bundle params = new Bundle();\n    ARouter.getInstance()\n        .build(\"/home/main\")\n        .with(params)\n        .navigation();\n\n    // Set Flag\n    ARouter.getInstance()\n        .build(\"/home/main\")\n        .withFlags();\n        .navigation();\n\n    // For fragment\n    Fragment fragment = (Fragment) ARouter.getInstance().build(\"/test/fragment\").navigation();\n                        \n    // transfer the object \n    ARouter.getInstance()\n        .withObject(\"key\", new TestObj(\"Jack\", \"Rose\"))\n        .navigation();\n\n    // Think the interface is not enough, you can directly set parameter into Bundle\n    ARouter.getInstance()\n            .build(\"/home/main\")\n            .getExtra();\n\n    // Transition animation (regular mode)\n    ARouter.getInstance()\n        .build(\"/test/activity2\")\n        .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)\n        .navigation(this);\n\n    // Transition animation (API16+)\n    ActivityOptionsCompat compat = ActivityOptionsCompat.\n        makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);\n\n    // ps. makeSceneTransitionAnimation, When using shared elements, you need to pass in the current Activity in the navigation method\n\n    ARouter.getInstance()\n        .build(\"/test/activity2\")\n        .withOptionsCompat(compat)\n        .navigation();\n            \n    // Use green channel (skip all interceptors)\n    ARouter.getInstance().build(\"/home/main\").greenChannel().navigation();\n\n    // Use your own log tool to print logs\n    ARouter.setLogger();\n\n    // Use your custom thread pool\n    ARouter.setExecutor();\n    ```\n\n3. Get the original URI\n    ``` java\n    String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);\n    ```\n\n4. Rewrite URL\n    ``` java\n    // Implement the PathReplaceService interface\n    @Route(path = \"/xxx/xxx\")\n    public class PathReplaceServiceImpl implements PathReplaceService {\n        /**\n        * For normal path.\n        *\n        * @param path raw path\n        */\n        String forString(String path) {\n            // Custom logic\n            return path;\n        }\n\n    /**\n        * For uri type.\n        *\n        * @param uri raw uri\n        */\n        Uri forUri(Uri uri) {\n            // Custom logic\n            return url;\n        }\n    }\n    ```\n\n5. Generate router doc\n    ``` gradle\n    // Edit build.gradle, add option 'AROUTER_GENERATE_DOC = enable'\n    // Doc file : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json\n    android {\n        defaultConfig {\n            ...\n            javaCompileOptions {\n                annotationProcessorOptions {\n                    arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: \"enable\"]\n                }\n            }\n        }\n    }\n    ```\n\n#### VI. Other\n\n1. Old version of gradle plugin configuration\n    ``` gradle\n    apply plugin: 'com.neenbedankt.android-apt'\n\n    buildscript {\n        repositories {\n            mavenCentral()\n        }\n\n        dependencies {\n            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'\n        }\n    }\n\n    apt {\n        arguments {\n            AROUTER_MODULE_NAME project.getName();\n        }\n    }\n\n    dependencies {\n        compile 'com.alibaba:arouter-api:x.x.x'\n        apt 'com.alibaba:arouter-compiler:x.x.x'\n        ...\n    }\n    ```\n\n2. Kotlin project configuration\n    ```\n    // You can refer to the wording in the \"module-kotlin\" module\n    apply plugin: 'kotlin-kapt'\n\n    kapt {\n        arguments {\n            arg(\"AROUTER_MODULE_NAME\", project.getName())\n        }\n    }\n\n    dependencies {\n        compile 'com.alibaba:arouter-api:x.x.x'\n        kapt 'com.alibaba:arouter-compiler:x.x.x'\n        ...\n    }\n    ```\n\n#### VII. Communication\n\n1. Communication\n\n    1. DingDing group1\n    \n        ![dingding](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/dingding-group-1.png)\n\n    2. QQ group1\n    \n        ![qq](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/qq-group-1.png)\n\n    3. QQ group2\n        \n        ![qq](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/qq-group-2.png)\n"
  },
  {
    "path": "README_CN.md",
    "content": "```\n    一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦\n```\n\n[English](https://github.com/alibaba/ARouter/blob/master/README.md)\n\n##### [![Join the chat at https://gitter.im/alibaba/ARouter](https://badges.gitter.im/alibaba/ARouter.svg)](https://gitter.im/alibaba/ARouter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Hex.pm](https://img.shields.io/hexpm/l/plug.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n\n---\n\n#### 最新版本\n\n模块|arouter-api|arouter-compiler|arouter-register|arouter-idea-plugin\n---|---|---|---|---\n最新版本|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-api)|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-compiler/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-compiler)|[![Download](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-register/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/arouter-register)|[![as plugin](https://img.shields.io/jetbrains/plugin/d/11428-arouter-helper.svg)](https://plugins.jetbrains.com/plugin/11428-arouter-helper)\n\n#### Demo展示\n\n##### [Demo apk下载](https://github.com/alibaba/ARouter/blob/develop/demo/arouter-demo-1.5.2.apk)、[Demo Gif](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/arouter-demo.gif)\n\n#### 一、功能介绍\n1. **支持直接解析标准URL进行跳转，并自动注入参数到目标页面中**\n2. **支持多模块工程使用**\n3. **支持添加多个拦截器，自定义拦截顺序**\n4. **支持依赖注入，可单独作为依赖注入框架使用**\n5. **支持InstantRun**\n6. **支持MultiDex**(Google方案)\n7. 映射关系按组分类、多级管理，按需初始化\n8. 支持用户指定全局降级与局部降级策略\n9. 页面、拦截器、服务等组件均自动注册到框架\n10. 支持多种方式配置转场动画\n11. 支持获取Fragment\n12. 完全支持Kotlin以及混编(配置见文末 其他#5)\n13. **支持第三方 App 加固**(使用 arouter-register 实现自动注册)\n14. **支持生成路由文档**\n15. **提供 IDE 插件便捷的关联路径和目标类**\n16. 支持增量编译(开启文档生成后无法增量编译)\n17. 支持动态注册路由信息\n\n#### 二、典型应用\n1. 从外部URL映射到内部页面，以及参数传递与解析\n2. 跨模块页面跳转，模块间解耦\n3. 拦截跳转过程，处理登陆、埋点等逻辑\n4. 跨模块API调用，通过控制反转来做组件解耦\n\n#### 三、基础功能\n1. 添加依赖和配置\n    ``` gradle\n    android {\n        defaultConfig {\n            ...\n            javaCompileOptions {\n                annotationProcessorOptions {\n                    arguments = [AROUTER_MODULE_NAME: project.getName()]\n                }\n            }\n        }\n    }\n\n    dependencies {\n        // 替换成最新版本, 需要注意的是api\n        // 要与compiler匹配使用，均使用最新版可以保证兼容\n        compile 'com.alibaba:arouter-api:x.x.x'\n        annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'\n        ...\n    }\n    // 旧版本gradle插件(< 2.2)，可以使用apt插件，配置方法见文末'其他#4'\n    // Kotlin配置参考文末'其他#5'\n    ```\n\n2. 添加注解\n    ``` java\n    // 在支持路由的页面上添加注解(必选)\n    // 这里的路径需要注意的是至少需要有两级，/xx/xx\n    @Route(path = \"/test/activity\")\n    public class YourActivity extend Activity {\n        ...\n    }\n    ```\n\n3. 初始化SDK\n    ``` java\n    if (isDebug()) {           // 这两行必须写在init之前，否则这些配置在init过程中将无效\n        ARouter.openLog();     // 打印日志\n        ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行，必须开启调试模式！线上版本需要关闭,否则有安全风险)\n    }\n    ARouter.init(mApplication); // 尽可能早，推荐在Application中初始化\n    ```\n\n4. 发起路由操作\n    ``` java\n    // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)\n    ARouter.getInstance().build(\"/test/activity\").navigation();\n\n    // 2. 跳转并携带参数\n    ARouter.getInstance().build(\"/test/1\")\n                .withLong(\"key1\", 666L)\n                .withString(\"key3\", \"888\")\n                .withObject(\"key4\", new Test(\"Jack\", \"Rose\"))\n                .navigation();\n    ```\n\n5. 添加混淆规则(如果使用了Proguard)\n    ``` \n    -keep public class com.alibaba.android.arouter.routes.**{*;}\n    -keep public class com.alibaba.android.arouter.facade.**{*;}\n    -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}\n\n    # 如果使用了 byType 的方式获取 Service，需添加下面规则，保护接口\n    -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider\n\n    # 如果使用了 单类注入，即不定义接口实现 IProvider，需添加下面规则，保护实现\n    # -keep class * implements com.alibaba.android.arouter.facade.template.IProvider\n    ```\n\n6. 使用 Gradle 插件实现路由表的自动加载 (可选)\n    ```gradle\n    apply plugin: 'com.alibaba.arouter'\n\n    buildscript {\n        repositories {\n            mavenCentral()\n        }\n\n        dependencies {\n            classpath \"com.alibaba:arouter-register:?\"\n        }\n    }\n    ```\n\n    可选使用，通过 ARouter 提供的注册插件进行路由表的自动加载(power by [AutoRegister](https://github.com/luckybilly/AutoRegister))， 默认通过扫描 dex 的方式\n    进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问\n    dex 文件，初始化失败的问题，需要注意的是，该插件必须搭配 api 1.3.0 以上版本使用！\n\n7. 使用 IDE 插件导航到目标类 (可选)\n\n    在 Android Studio 插件市场中搜索 `ARouter Helper`, 或者直接下载文档上方 `最新版本` 中列出的 `arouter-idea-plugin` zip 安装包手动安装，安装后\n    插件无任何设置，可以在跳转代码的行首找到一个图标 (![navigation](https://raw.githubusercontent.com/alibaba/ARouter/develop/arouter-idea-plugin/src/main/resources/icon/outline_my_location_black_18dp.png))\n    点击该图标，即可跳转到标识了代码中路径的目标类\n\n#### 四、进阶用法\n1. 通过URL跳转\n    ``` java\n    // 新建一个Activity用于监听Scheme事件,之后直接把url传递给ARouter即可\n    public class SchemeFilterActivity extends Activity {\n        @Override\n        protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        Uri uri = getIntent().getData();\n        ARouter.getInstance().build(uri).navigation();\n        finish();\n        }\n    }\n    ```\n\n    AndroidManifest.xml\n    ``` xml\n    <activity android:name=\".activity.SchemeFilterActivity\">\n        <!-- Scheme -->\n        <intent-filter>\n            <data\n            android:host=\"m.aliyun.com\"\n            android:scheme=\"arouter\"/>\n\n            <action android:name=\"android.intent.action.VIEW\"/>\n\n            <category android:name=\"android.intent.category.DEFAULT\"/>\n            <category android:name=\"android.intent.category.BROWSABLE\"/>\n        </intent-filter>\n    </activity>\n    ```\n\n2. 解析参数\n    ``` java\n    // 为每一个参数声明一个字段，并使用 @Autowired 标注\n    // URL中不能传递Parcelable类型数据，通过ARouter api可以传递Parcelable对象\n    @Route(path = \"/test/activity\")\n    public class Test1Activity extends Activity {\n        @Autowired\n        public String name;\n        @Autowired\n        int age;\n        \n        // 通过name来映射URL中的不同参数\n        @Autowired(name = \"girl\") \n        boolean boy;\n        \n        // 支持解析自定义对象，URL中使用json传递\n        @Autowired\n        TestObj obj;      \n        \n        // 使用 withObject 传递 List 和 Map 的实现了\n        // Serializable 接口的实现类(ArrayList/HashMap)\n        // 的时候，接收该对象的地方不能标注具体的实现类类型\n        // 应仅标注为 List 或 Map，否则会影响序列化中类型\n        // 的判断, 其他类似情况需要同样处理        \n        @Autowired\n        List<TestObj> list;\n        @Autowired\n        Map<String, List<TestObj>> map;\n        \n        @Override\n        protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        ARouter.getInstance().inject(this);\n\n        // ARouter会自动对字段进行赋值，无需主动获取\n        Log.d(\"param\", name + age + boy);\n        }\n    }\n\n\n    // 如果需要传递自定义对象，新建一个类（并非自定义对象类），然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式)，例如：\n    @Route(path = \"/yourservicegroupname/json\")\n    public class JsonServiceImpl implements SerializationService {\n        @Override\n        public void init(Context context) {\n\n        }\n\n        @Override\n        public <T> T json2Object(String text, Class<T> clazz) {\n            return JSON.parseObject(text, clazz);\n        }\n\n        @Override\n        public String object2Json(Object instance) {\n            return JSON.toJSONString(instance);\n        }\n    }\n    ```\n\n3. 声明拦截器(拦截跳转过程，面向切面编程)\n    ``` java\n    // 比较经典的应用就是在跳转过程中处理登陆事件，这样就不需要在目标页重复做登陆检查\n    // 拦截器会在跳转之间执行，多个拦截器会按优先级顺序依次执行\n    @Interceptor(priority = 8, name = \"测试用拦截器\")\n    public class TestInterceptor implements IInterceptor {\n        @Override\n        public void process(Postcard postcard, InterceptorCallback callback) {\n        ...\n        callback.onContinue(postcard);  // 处理完成，交还控制权\n        // callback.onInterrupt(new RuntimeException(\"我觉得有点异常\"));      // 觉得有问题，中断路由流程\n\n        // 以上两种至少需要调用其中一种，否则不会继续路由\n        }\n\n        @Override\n        public void init(Context context) {\n        // 拦截器的初始化，会在sdk初始化的时候调用该方法，仅会调用一次\n        }\n    }\n    ```\n\n4. 处理跳转结果\n    ``` java\n    // 使用两个参数的navigation方法，可以获取单次跳转的结果\n    ARouter.getInstance().build(\"/test/1\").navigation(this, new NavigationCallback() {\n        @Override\n        public void onFound(Postcard postcard) {\n        ...\n        }\n\n        @Override\n        public void onLost(Postcard postcard) {\n        ...\n        }\n    });\n    ```\n\n5. 自定义全局降级策略\n    ``` java\n    // 实现DegradeService接口，并加上一个Path内容任意的注解即可\n    @Route(path = \"/xxx/xxx\")\n    public class DegradeServiceImpl implements DegradeService {\n    @Override\n    public void onLost(Context context, Postcard postcard) {\n        // do something.\n    }\n\n    @Override\n    public void init(Context context) {\n\n    }\n    }\n    ```\n\n6. 为目标页面声明更多信息\n    ``` java\n    // 我们经常需要在目标页面中配置一些属性，比方说\"是否需要登陆\"之类的\n    // 可以通过 Route 注解中的 extras 属性进行扩展，这个属性是一个 int值，换句话说，单个int有4字节，也就是32位，可以配置32个开关\n    // 剩下的可以自行发挥，通过字节操作可以标识32个开关，通过开关标记目标页面的一些属性，在拦截器中可以拿到这个标记进行业务逻辑判断\n    @Route(path = \"/test/activity\", extras = Consts.XXXX)\n    ```\n\n7. 通过依赖注入解耦:服务管理(一) 暴露服务\n    ``` java\n    // 声明接口,其他组件通过接口来调用服务\n    public interface HelloService extends IProvider {\n        String sayHello(String name);\n    }\n\n    // 实现接口\n    @Route(path = \"/yourservicegroupname/hello\", name = \"测试服务\")\n    public class HelloServiceImpl implements HelloService {\n\n        @Override\n        public String sayHello(String name) {\n        return \"hello, \" + name;\n        }\n\n        @Override\n        public void init(Context context) {\n\n        }\n    }\n    ```\n\n8. 通过依赖注入解耦:服务管理(二) 发现服务\n    ``` java\n    public class Test {\n        @Autowired\n        HelloService helloService;\n\n        @Autowired(name = \"/yourservicegroupname/hello\")\n        HelloService helloService2;\n\n        HelloService helloService3;\n\n        HelloService helloService4;\n\n        public Test() {\n        ARouter.getInstance().inject(this);\n        }\n\n        public void testService() {\n        // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用，无需主动获取\n        // Autowired注解中标注name之后，将会使用byName的方式注入对应的字段，不设置name属性，会默认使用byType的方式发现服务(当同一接口有多个实现的时候，必须使用byName的方式发现服务)\n        helloService.sayHello(\"Vergil\");\n        helloService2.sayHello(\"Vergil\");\n\n        // 2. 使用依赖查找的方式发现服务，主动去发现服务并使用，下面两种方式分别是byName和byType\n        helloService3 = ARouter.getInstance().navigation(HelloService.class);\n        helloService4 = (HelloService) ARouter.getInstance().build(\"/yourservicegroupname/hello\").navigation();\n        helloService3.sayHello(\"Vergil\");\n        helloService4.sayHello(\"Vergil\");\n        }\n    }\n    ```\n\n9. 预处理服务\n    ``` java\n    // 实现 PretreatmentService 接口，并加上一个Path内容任意的注解即可\n    @Route(path = \"/xxx/xxx\")\n    public class PretreatmentServiceImpl implements PretreatmentService {\n        @Override\n        public boolean onPretreatment(Context context, Postcard postcard) {\n            // 跳转前预处理，如果需要自行处理跳转，该方法返回 false 即可\n        }\n\n        @Override\n        public void init(Context context) {\n    \n        }\n    }\n    ```\n\n10. 动态注册路由信息\n适用于部分插件化架构的App以及需要动态注册路由信息的场景，可以通过 ARouter 提供的接口实现动态注册\n路由信息，目标页面和服务可以不标注 @Route 注解，**注意：同一批次仅允许相同 group 的路由信息注册**\n    ``` java\n        ARouter.getInstance().addRouteGroup(new IRouteGroup() {\n            @Override\n            public void loadInto(Map<String, RouteMeta> atlas) {\n                atlas.put(\"/dynamic/activity\",      // path\n                    RouteMeta.build(\n                        RouteType.ACTIVITY,         // 路由信息\n                        TestDynamicActivity.class,  // 目标的 Class\n                        \"/dynamic/activity\",        // Path\n                        \"dynamic\",                  // Group, 尽量保持和 path 的第一段相同\n                        0,                          // 优先级，暂未使用\n                        0                           // Extra，用于给页面打标\n                    )\n                );\n            }\n        });\n    ```\n \n#### 五、更多功能\n\n1. 初始化中的其他设置\n    ``` java\n    ARouter.openLog(); // 开启日志\n    ARouter.openDebug(); // 使用InstantRun的时候，需要打开该开关，上线之后关闭，否则有安全风险\n    ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈\n    ```\n\n2. 详细的API说明\n    ``` java\n    // 构建标准的路由请求\n    ARouter.getInstance().build(\"/home/main\").navigation();\n\n    // 构建标准的路由请求，并指定分组\n    ARouter.getInstance().build(\"/home/main\", \"ap\").navigation();\n\n    // 构建标准的路由请求，通过Uri直接解析\n    Uri uri;\n    ARouter.getInstance().build(uri).navigation();\n\n    // 构建标准的路由请求，startActivityForResult\n    // navigation的第一个参数必须是Activity，第二个参数则是RequestCode\n    ARouter.getInstance().build(\"/home/main\", \"ap\").navigation(this, 5);\n\n    // 直接传递Bundle\n    Bundle params = new Bundle();\n    ARouter.getInstance()\n        .build(\"/home/main\")\n        .with(params)\n        .navigation();\n\n    // 指定Flag\n    ARouter.getInstance()\n        .build(\"/home/main\")\n        .withFlags();\n        .navigation();\n\n    // 获取Fragment\n    Fragment fragment = (Fragment) ARouter.getInstance().build(\"/test/fragment\").navigation();\n                        \n    // 对象传递\n    ARouter.getInstance()\n        .withObject(\"key\", new TestObj(\"Jack\", \"Rose\"))\n        .navigation();\n\n    // 觉得接口不够多，可以直接拿出Bundle赋值\n    ARouter.getInstance()\n            .build(\"/home/main\")\n            .getExtra();\n\n    // 转场动画(常规方式)\n    ARouter.getInstance()\n        .build(\"/test/activity2\")\n        .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)\n        .navigation(this);\n\n    // 转场动画(API16+)\n    ActivityOptionsCompat compat = ActivityOptionsCompat.\n        makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);\n\n    // ps. makeSceneTransitionAnimation 使用共享元素的时候，需要在navigation方法中传入当前Activity\n\n    ARouter.getInstance()\n        .build(\"/test/activity2\")\n        .withOptionsCompat(compat)\n        .navigation();\n            \n    // 使用绿色通道(跳过所有的拦截器)\n    ARouter.getInstance().build(\"/home/main\").greenChannel().navigation();\n\n    // 使用自己的日志工具打印日志\n    ARouter.setLogger();\n\n    // 使用自己提供的线程池\n    ARouter.setExecutor();\n    ```\n\n3. 获取原始的URI\n    ``` java\n    String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);\n    ```\n\n4. 重写跳转URL\n    ``` java\n    // 实现PathReplaceService接口，并加上一个Path内容任意的注解即可\n    @Route(path = \"/xxx/xxx\") // 必须标明注解\n    public class PathReplaceServiceImpl implements PathReplaceService {\n        /**\n        * For normal path.\n        *\n        * @param path raw path\n        */\n        String forString(String path) {\n        return path;    // 按照一定的规则处理之后返回处理后的结果\n        }\n\n    /**\n        * For uri type.\n        *\n        * @param uri raw uri\n        */\n    Uri forUri(Uri uri) {\n        return url;    // 按照一定的规则处理之后返回处理后的结果\n    }\n    }\n    ```\n\n5. 生成路由文档\n    ``` gradle\n    // 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable\n    // 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json\n    android {\n        defaultConfig {\n            ...\n            javaCompileOptions {\n                annotationProcessorOptions {\n                    arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: \"enable\"]\n                }\n            }\n        }\n    }\n    ```\n\n#### 六、其他\n\n1. 路由中的分组概念\n\n\t- SDK中针对所有的路径(/test/1 /test/2)进行分组，分组只有在分组中的某一个路径第一次被访问的时候，该分组才会被初始化\n\t- 可以通过 @Route 注解主动指定分组，否则使用路径中第一段字符串(/*/)作为分组\n\t- 注意：一旦主动指定分组之后，应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转，手动指定分组，否则无法找到\n    ``` java\n    @Route(path = \"/test/1\", group = \"app\")\n    ```\n\n2. 拦截器和服务的异同\n\n\t- 拦截器和服务所需要实现的接口不同，但是结构类似，都存在 init(Context context) 方法，但是两者的调用时机不同\n\t- 拦截器因为其特殊性，会被任何一次路由所触发，拦截器会在ARouter初始化的时候异步初始化，如果第一次路由的时候拦截器还没有初始化结束，路由会等待，直到初始化完成。\n\t- 服务没有该限制，某一服务可能在App整个生命周期中都不会用到，所以服务只有被调用的时候才会触发初始化操作\n\n3. 旧版本gradle插件的配置方式\n    ``` gradle\n    apply plugin: 'com.neenbedankt.android-apt'\n\n    buildscript {\n        repositories {\n            mavenCentral()\n        }\n\n        dependencies {\n            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'\n        }\n    }\n\n    apt {\n        arguments {\n            AROUTER_MODULE_NAME project.getName();\n        }\n    }\n\n    dependencies {\n        compile 'com.alibaba:arouter-api:x.x.x'\n        apt 'com.alibaba:arouter-compiler:x.x.x'\n        ...\n    }\n    ```\n\n4. Kotlin项目中的配置方式\n    ```\n    // 可以参考 module-kotlin 模块中的写法\n    apply plugin: 'kotlin-kapt'\n\n    kapt {\n        arguments {\n            arg(\"AROUTER_MODULE_NAME\", project.getName())\n        }\n    }\n\n    dependencies {\n        compile 'com.alibaba:arouter-api:x.x.x'\n        kapt 'com.alibaba:arouter-compiler:x.x.x'\n        ...\n    }\n    ```\n\n#### 七、Q&A\n\n1. \"W/ARouter::: ARouter::No postcard![ ]\"\n\n    这个Log正常的情况下也会打印出来，如果您的代码中没有实现DegradeService和PathReplaceService的话，因为ARouter本身的一些功能也依赖\n    自己提供的Service管理功能，ARouter在跳转的时候会尝试寻找用户实现的PathReplaceService，用于对路径进行重写(可选功能)，所以如果您没有\n    实现这个服务的话，也会抛出这个日志\n\n    推荐在app中实现DegradeService、PathReplaceService\n\n2. \"W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ]\"\n\n    - 通常来说这种情况是没有找到目标页面，目标不存在\n    - 如果这个页面是存在的，那么您可以按照下面的步骤进行排查\n        1. 检查目标页面的注解是否配置正确，正确的注解形式应该是 (@Route(path=\"/test/test\"), 如没有特殊需求，请勿指定group字段，废弃功能)\n        2. 检查目标页面所在的模块的gradle脚本中是否依赖了 arouter-compiler sdk (需要注意的是，要使用apt依赖，而不是compile关键字依赖)\n        3. 检查编译打包日志，是否出现了形如 ARouter::\u0005Compiler >>> xxxxx 的日志，日志中会打印出发现的路由目标\n        4. 启动App的时候，开启debug、log(openDebug/openLog), 查看映射表是否已经被扫描出来，形如 D/ARouter::: LogisticsCenter has already been loaded, GroupIndex[4]，GroupIndex > 0\n\n3. 开启InstantRun之后无法跳转(高版本Gradle插件下无法跳转)？\n        \n     因为开启InstantRun之后，很多类文件不会放在原本的dex中，需要单独去加载，ARouter默认不会去加载这些文件，因为安全原因，只有在开启了openDebug之后\n     ARouter才回去加载InstantRun产生的文件，所以在以上的情况下，需要在init**之前**调用openDebug\n \n4. TransformException:java.util.zip.ZipException: duplicate entry ....\n \n     ARouter有按组加载的机制，关于分组可以参考 6-1 部分，ARouter允许一个module中存在多个分组，但是不允许多个module中存在相同的分组，会导致映射文件冲突\n\n5. Kotlin类中的字段无法注入如何解决？\n    \n    首先，Kotlin中的字段是可以自动注入的，但是注入代码为了减少反射，使用的字段赋值的方式来注入的，Kotlin默认会生成set/get方法，并把属性设置为private\n    所以只要保证Kotlin中字段可见性不是private即可，简单解决可以在字段上添加 @JvmField \n\n6. 通过URL跳转之后，在intent中拿不到参数如何解决？\n    \n    需要注意的是，如果不使用自动注入，那么可以不写 `ARouter.getInstance().inject(this)`，但是需要取值的字段仍然需要标上 `@Autowired` 注解，因为\n    只有标上注解之后，ARouter才能知道以哪一种数据类型提取URL中的参数并放入Intent中，这样您才能在intent中获取到对应的参数\n    \n7. 新增页面之后，无法跳转？\n    \n    ARouter加载Dex中的映射文件会有一定耗时，所以ARouter会缓存映射文件，直到新版本升级(版本号或者versionCode变化)，而如果是开发版本(ARouter.openDebug())，\n    ARouter 每次启动都会重新加载映射文件，开发阶段一定要打开 Debug 功能\n\n#### 八、其他\n\n1. 沟通和交流\n\n    1. 钉钉交流群1\n    \n        ![qq](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/dingding-group-1.png)\n\n    2. QQ 交流群1\n    \n        ![qq](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/qq-group-1.png)\n\n    3. QQ 交流群2\n        \n        ![qq](https://raw.githubusercontent.com/alibaba/ARouter/master/demo/qq-group-2.png)\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'com.alibaba.arouter'\n\nandroid {\n    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)\n    buildToolsVersion BUILDTOOLS_VERSION\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)\n        versionName \"0.0.1\"\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    signingConfigs {\n        debug {\n            storeFile file(\"./doc/debug/debug.keystore\")\n            storePassword \"android\"\n            keyAlias \"androiddebugkey\"\n            keyPassword \"android\"\n        }\n    }\n\n    buildTypes {\n        debug {\n            minifyEnabled false\n            signingConfig signingConfigs.debug\n        }\n\n        release {\n            minifyEnabled true\n            signingConfig signingConfigs.debug\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation project(':arouter-api')\n\n    // 开发中依赖对方的 service 包\n    implementation project(':module-java-export')\n    implementation project(':module-java')\n\n    implementation project(':module-kotlin')\n\n    implementation \"com.android.support:support-v4:${SUPPORT_LIB_VERSION}\"\n    implementation \"com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}\"\n}"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "-optimizationpasses 5\n-dontpreverify\n-dontwarn\n-ignorewarnings\n-verbose\n-overloadaggressively\n-dontusemixedcaseclassnames\n-dontskipnonpubliclibraryclasses\n-dontskipnonpubliclibraryclassmembers\n-keepattributes EnclosingMethod, *Annotation*, *JavascriptInterface*, InnerClasses, Signature\n-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*\n\n# Android Framework\n-keep public class * extends android.app.Activity\n-keep public class * extends android.app.Application\n-keep public class * extends android.app.Service\n-keep public class * extends android.content.BroadcastReceiver\n-keep public class * extends android.content.ContentProvider\n-keep public class * extends android.app.backup.BackupAgentHelper\n-keep public class * extends android.preference.Preference\n-keep public class com.android.vending.licensing.ILicensingService\n\n-keepclasseswithmembers class * {\n    public <init>(android.content.Context,android.util.AttributeSet);\n}\n\n-keepclasseswithmembers class * {\n    public <init>(android.content.Context,android.util.AttributeSet,int);\n}\n\n-keepclassmembers class * implements java.io.Serializable {\n   static final long serialVersionUID;\n   private static final java.io.ObjectStreamField[] serialPersistentFields;\n   !static !transient <fields>;\n   private void writeObject(java.io.ObjectOutputStream);\n   private void readObject(java.io.ObjectInputStream);\n   java.lang.Object writeReplace();\n   java.lang.Object readResolve();\n}\n\n-keep class * extends android.os.Parcelable {\n    public static final android.os.Parcelable$Creator *;\n}\n\n-keepclasseswithmembernames class * {\n    native <methods>;\n}\n\n-keepclasseswithmembers,allowshrinking class * {\n    native <methods>;\n}\n\n-keepclassmembers enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n-keepclassmembers class **.R$* {\n    public static <fields>;\n}\n\n-keepclassmembers class * extends android.content.Context {\n   public void *(android.view.View);\n   public void *(android.view.MenuItem);\n}\n\n-keepclassmembers class * extends android.app.Activity {\n   public void *(android.view.View);  \n}\n\n-dontwarn com.alibaba.fastjson.**\n-keep class com.alibaba.fastjson.**{*;}\n\n# ARouter\n-keep public class com.alibaba.android.arouter.routes.**{*;}\n-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}\n-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider"
  },
  {
    "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.alibaba.android.arouter.demo\">\n\n    <application\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"ARouter demo\"\n        android:theme=\"@style/Base.Theme.AppCompat\">\n        <activity android:name=\".SchemeFilterActivity\">\n\n            <!-- Scheme -->\n            <intent-filter>\n                <data\n                    android:host=\"m.aliyun.com\"\n                    android:scheme=\"arouter\"/>\n\n                <action android:name=\"android.intent.action.VIEW\"/>\n\n                <category android:name=\"android.intent.category.DEFAULT\"/>\n                <category android:name=\"android.intent.category.BROWSABLE\"/>\n            </intent-filter>\n\n            <!-- App Links -->\n            <intent-filter android:autoVerify=\"true\">\n                <action android:name=\"android.intent.action.VIEW\"/>\n\n                <category android:name=\"android.intent.category.DEFAULT\"/>\n                <category android:name=\"android.intent.category.BROWSABLE\"/>\n\n                <data\n                    android:host=\"m.aliyun.com\"\n                    android:scheme=\"http\"/>\n                <data\n                    android:host=\"m.aliyun.com\"\n                    android:scheme=\"https\"/>\n            </intent-filter>\n        </activity>\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/assets/scheme-test.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <title></title>\n</head>\n\n<body>\n\n<h2>跳转测试</h2>\n\n<h2>自定义Scheme[通常来说都是这样的]</h2>\n<p><a href=\"arouter://m.aliyun.com/test/activity1\">arouter://m.aliyun.com/test/activity1</a></p>\n<p><a href=\"arouter://m.aliyun.com/test/activity1?url=https%3a%2f%2fm.abc.com%3fa%3db%26c%3dd\">测试URL Encode情况</a></p>\n<p><a href=\"arouter://m.aliyun.com/test/activity1?name=alex&age=18&boy=true&high=180&obj=%7b%22name%22%3a%22jack%22%2c%22id%22%3a666%7d\">arouter://m.aliyun.com/test/activity1?name=alex&age=18&boy=true&high=180&obj={\"name\":\"jack\",\"id\":\"666\"}</a></p>\n<p><a href=\"arouter://m.aliyun.com/test/activity2\">arouter://m.aliyun.com/test/activity2</a></p>\n<p><a href=\"arouter://m.aliyun.com/test/activity2?key1=value1\">arouter://m.aliyun.com/test/activity2?key1=value1</a></p>\n<p><a href=\"arouter://m.aliyun.com/test/activity3?name=alex&age=18&boy=true&high=180\">arouter://m.aliyun.com/test/activity3?name=alex&age=18&boy=true&high=180</a></p>\n\n<h2>App Links[防止被App屏蔽]</h2>\n<p><a href=\"http://m.aliyun.com/test/activity1\">http://m.aliyun.com/test/activity1</a></p>\n<p><a href=\"http://m.aliyun.com/test/activity2\">http://m.aliyun.com/test/activity2</a></p>\n\n</body>\n</html>"
  },
  {
    "path": "app/src/main/java/com/alibaba/android/arouter/demo/MainActivity.java",
    "content": "package com.alibaba.android.arouter.demo;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.ActivityOptionsCompat;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.alibaba.android.arouter.demo.module1.testactivity.TestDynamicActivity;\nimport com.alibaba.android.arouter.demo.service.model.TestObj;\nimport com.alibaba.android.arouter.demo.service.model.TestParcelable;\nimport com.alibaba.android.arouter.demo.service.model.TestSerializable;\nimport com.alibaba.android.arouter.demo.service.HelloService;\nimport com.alibaba.android.arouter.demo.module1.testservice.SingleService;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.NavCallback;\nimport com.alibaba.android.arouter.facade.enums.RouteType;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.template.IRouteGroup;\nimport com.alibaba.android.arouter.launcher.ARouter;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class MainActivity extends AppCompatActivity implements View.OnClickListener {\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n    }\n\n    /**\n     * Called when a view has been clicked.\n     *\n     * @param v The view that was clicked.\n     */\n    @Override\n    public void onClick(View v) {\n        // Build test data.\n        TestSerializable testSerializable = new TestSerializable(\"Titanic\", 555);\n        TestParcelable testParcelable = new TestParcelable(\"jack\", 666);\n        TestObj testObj = new TestObj(\"Rose\", 777);\n        List<TestObj> objList = new ArrayList<>();\n        objList.add(testObj);\n        Map<String, List<TestObj>> map = new HashMap<>();\n        map.put(\"testMap\", objList);\n\n        switch (v.getId()) {\n            case R.id.openLog:\n                ARouter.openLog();\n//                ARouter.printStackTrace();\n                break;\n            case R.id.openDebug:\n                ARouter.openDebug();\n                break;\n            case R.id.init:\n                // 调试模式不是必须开启，但是为了防止有用户开启了InstantRun，但是\n                // 忘了开调试模式，导致无法使用Demo，如果使用了InstantRun，必须在\n                // 初始化之前开启调试模式，但是上线前需要关闭，InstantRun仅用于开\n                // 发阶段，线上开启调试模式有安全风险，可以使用BuildConfig.DEBUG\n                // 来区分环境\n                ARouter.openDebug();\n                ARouter.init(getApplication());\n                break;\n            case R.id.normalNavigation:\n                ARouter.getInstance()\n                        .build(\"/test/activity2\")\n                        .navigation();\n\n                // 也可以通过依赖对方提供的二方包来约束入参\n                // 非必须，可以通过这种方式调用\n                // Entrance.redirect2Test1Activity(\"张飞\", 48, this);\n                break;\n            case R.id.kotlinNavigation:\n                ARouter.getInstance()\n                        .build(\"/kotlin/test\")\n                        .withString(\"name\", \"老王\")\n                        .withInt(\"age\", 23)\n                        .navigation();\n                break;\n            case R.id.normalNavigationWithParams:\n                // ARouter.getInstance()\n                //         .build(\"/test/activity2\")\n                //         .withString(\"key1\", \"value1\")\n                //         .navigation();\n\n                Uri testUriMix = Uri.parse(\"arouter://m.aliyun.com/test/activity2\");\n                ARouter.getInstance().build(testUriMix)\n                        .withString(\"key1\", \"value1\")\n                        .navigation();\n\n                break;\n            case R.id.oldVersionAnim:\n                ARouter.getInstance()\n                        .build(\"/test/activity2\")\n                        .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)\n                        .navigation(this);\n                break;\n            case R.id.newVersionAnim:\n                if (Build.VERSION.SDK_INT >= 16) {\n                    ActivityOptionsCompat compat = ActivityOptionsCompat.\n                            makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);\n\n                    ARouter.getInstance()\n                            .build(\"/test/activity2\")\n                            .withOptionsCompat(compat)\n                            .navigation();\n                } else {\n                    Toast.makeText(this, \"API < 16,不支持新版本动画\", Toast.LENGTH_SHORT).show();\n                }\n                break;\n            case R.id.interceptor:\n                ARouter.getInstance()\n                        .build(\"/test/activity4\")\n                        .navigation(this, new NavCallback() {\n                            @Override\n                            public void onArrival(Postcard postcard) {\n\n                            }\n\n                            @Override\n                            public void onInterrupt(Postcard postcard) {\n                                Log.d(\"ARouter\", \"被拦截了\");\n                            }\n                        });\n                break;\n            case R.id.navByUrl:\n                ARouter.getInstance()\n                        .build(\"/test/webview\")\n                        .withString(\"url\", \"file:///android_asset/scheme-test.html\")\n                        .navigation();\n                break;\n            case R.id.autoInject:\n                ARouter.getInstance().build(\"/test/activity1\")\n                        .withString(\"name\", \"老王\")\n                        .withInt(\"age\", 18)\n                        .withBoolean(\"boy\", true)\n                        .withLong(\"high\", 180)\n                        .withString(\"url\", \"https://a.b.c\")\n                        .withSerializable(\"ser\", testSerializable)\n                        .withParcelable(\"pac\", testParcelable)\n                        .withObject(\"obj\", testObj)\n                        .withObject(\"objList\", objList)\n                        .withObject(\"map\", map)\n                        .navigation();\n                break;\n            case R.id.navByName:\n                ((HelloService) ARouter.getInstance().build(\"/yourservicegroupname/hello\").navigation()).sayHello(\"mike\");\n                break;\n            case R.id.navByType:\n                ARouter.getInstance().navigation(HelloService.class).sayHello(\"mike\");\n                break;\n            case R.id.navToMoudle1:\n                ARouter.getInstance().build(\"/module/1\").navigation();\n                break;\n            case R.id.navToMoudle2:\n                // 这个页面主动指定了Group名\n                ARouter.getInstance().build(\"/module/2\", \"m2\").navigation();\n                break;\n            case R.id.destroy:\n                ARouter.getInstance().destroy();\n                break;\n            case R.id.failNav:\n                ARouter.getInstance().build(\"/xxx/xxx\").navigation(this, new NavCallback() {\n                    @Override\n                    public void onFound(Postcard postcard) {\n                        Log.d(\"ARouter\", \"找到了\");\n                    }\n\n                    @Override\n                    public void onLost(Postcard postcard) {\n                        Log.d(\"ARouter\", \"找不到了\");\n                    }\n\n                    @Override\n                    public void onArrival(Postcard postcard) {\n                        Log.d(\"ARouter\", \"跳转完了\");\n                    }\n\n                    @Override\n                    public void onInterrupt(Postcard postcard) {\n                        Log.d(\"ARouter\", \"被拦截了\");\n                    }\n                });\n                break;\n            case R.id.callSingle:\n                ARouter.getInstance().navigation(SingleService.class).sayHello(\"Mike\");\n                break;\n            case R.id.failNav2:\n                ARouter.getInstance().build(\"/xxx/xxx\").navigation();\n                break;\n            case R.id.failNav3:\n                ARouter.getInstance().navigation(MainActivity.class);\n                break;\n            case R.id.normalNavigation2:\n                ARouter.getInstance()\n                        .build(\"/test/activity2\")\n                        .navigation(this, 666);\n                break;\n            case R.id.getFragment:\n                Fragment fragment = (Fragment) ARouter.getInstance().build(\"/test/fragment\")\n                        .withString(\"name\", \"老王\")\n                        .withInt(\"age\", 18)\n                        .withBoolean(\"boy\", true)\n                        .withLong(\"high\", 180)\n                        .withString(\"url\", \"https://a.b.c\")\n                        .withSerializable(\"ser\", testSerializable)\n                        .withParcelable(\"pac\", testParcelable)\n                        .withObject(\"obj\", testObj)\n                        .withObject(\"objList\", objList)\n                        .withObject(\"map\", map).navigation();\n                Toast.makeText(this, \"找到Fragment:\" + fragment.toString(), Toast.LENGTH_SHORT).show();\n                break;\n            case R.id.addGroup:\n                ARouter.getInstance().addRouteGroup(new IRouteGroup() {\n                    @Override\n                    public void loadInto(Map<String, RouteMeta> atlas) {\n                        atlas.put(\"/dynamic/activity\", RouteMeta.build(\n                                RouteType.ACTIVITY,\n                                TestDynamicActivity.class,\n                                \"/dynamic/activity\",\n                                \"dynamic\", 0, 0));\n                    }\n                });\n                break;\n            case R.id.dynamicNavigation:\n                // 该页面未配置 Route 注解，动态注册到 ARouter\n                ARouter.getInstance().build(\"/dynamic/activity\")\n                        .withString(\"name\", \"老王\")\n                        .withInt(\"age\", 18)\n                        .withBoolean(\"boy\", true)\n                        .withLong(\"high\", 180)\n                        .withString(\"url\", \"https://a.b.c\")\n                        .withSerializable(\"ser\", testSerializable)\n                        .withParcelable(\"pac\", testParcelable)\n                        .withObject(\"obj\", testObj)\n                        .withObject(\"objList\", objList)\n                        .withObject(\"map\", map).navigation(this);\n                break;\n            default:\n                break;\n        }\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n\n        switch (requestCode) {\n            case 666:\n                Log.e(\"activityResult\", String.valueOf(resultCode));\n                break;\n            default:\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/alibaba/android/arouter/demo/SchemeFilterActivity.java",
    "content": "package com.alibaba.android.arouter.demo;\n\nimport android.app.Activity;\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.NavCallback;\nimport com.alibaba.android.arouter.launcher.ARouter;\n\npublic class SchemeFilterActivity extends Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n//        直接通过ARouter处理外部Uri\n        Uri uri = getIntent().getData();\n        ARouter.getInstance().build(uri).navigation(this, new NavCallback() {\n            @Override\n            public void onArrival(Postcard postcard) {\n                finish();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/anim/slide_in_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"2000\"\n        android:fromYDelta=\"100%p\"\n        android:toYDelta=\"0\"/>\n    <alpha\n        android:duration=\"2000\"\n        android:fromAlpha=\"0.0\"\n        android:toAlpha=\"1.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/anim/slide_out_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"2000\"\n        android:fromYDelta=\"0%p\"\n        android:toYDelta=\"100%p\"/>\n    <alpha\n        android:duration=\"2000\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0.0\"/>\n</set>"
  },
  {
    "path": "app/src/main/res/drawable/bg_test_area.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"#FFFFFF\"/>\n    <corners android:radius=\"5dp\" />\n</shape>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:id=\"@+id/activity_main\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\"\n        tools:context=\"com.alibaba.android.arouter.demo.MainActivity\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:padding=\"5dp\"\n                android:text=\"基本设置\" />\n\n            <Button\n                android:id=\"@+id/openLog\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"打开日志并打印堆栈\" />\n\n            <Button\n                android:id=\"@+id/openDebug\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"开启调试模式(InstantRun需要开启)\"\n                android:visibility=\"gone\" />\n\n            <Button\n                android:id=\"@+id/init\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"初始化ARouter\"\n                android:textColor=\"@color/red\" />\n\n            <Button\n                android:id=\"@+id/destroy\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"关闭ARouter\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"基础功能(请先初始化)\" />\n\n            <Button\n                android:id=\"@+id/normalNavigation\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"简单的应用内跳转\" />\n\n            <Button\n                android:id=\"@+id/kotlinNavigation\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转到Kotlin页面\" />\n\n            <Button\n                android:id=\"@+id/normalNavigation2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转ForResult\" />\n\n            <Button\n                android:id=\"@+id/getFragment\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"获取Fragment实例\" />\n\n            <Button\n                android:id=\"@+id/normalNavigationWithParams\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"携带参数的应用内跳转\" />\n\n            <Button\n                android:id=\"@+id/oldVersionAnim\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"旧版本转场动画\" />\n\n            <Button\n                android:id=\"@+id/newVersionAnim\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"新版本转场动画\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"进阶用法(请先初始化)\" />\n\n            <Button\n                android:id=\"@+id/navByUrl\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"通过URL跳转\" />\n\n            <Button\n                android:id=\"@+id/interceptor\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"拦截器测试\" />\n\n            <Button\n                android:id=\"@+id/autoInject\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"依赖注入(参照代码)\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"服务管理(请先初始化)\" />\n\n            <Button\n                android:id=\"@+id/navByName\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"ByName调用服务\" />\n\n            <Button\n                android:id=\"@+id/navByType\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"ByType调用服务\" />\n\n            <Button\n                android:id=\"@+id/callSingle\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"调用单类\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"多模块测试(请先初始化)\" />\n\n            <Button\n                android:id=\"@+id/navToMoudle1\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转到模块1\" />\n\n            <Button\n                android:id=\"@+id/navToMoudle2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转到模块2\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"跳转失败测试(请先初始化)\" />\n\n            <Button\n                android:id=\"@+id/failNav\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转失败，单独降级\" />\n\n            <Button\n                android:id=\"@+id/failNav2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"跳转失败，全局降级\" />\n\n            <Button\n                android:id=\"@+id/failNav3\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"服务调用失败\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/between_cell\"\n            android:background=\"@drawable/bg_test_area\"\n            android:orientation=\"vertical\"\n            android:padding=\"@dimen/test_area_padding\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"5dp\"\n                android:text=\"动态增加路由测试\" />\n\n            <Button\n                android:id=\"@+id/addGroup\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"动态增加路由\" />\n\n            <Button\n                android:id=\"@+id/dynamicNavigation\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onClick\"\n                android:text=\"动态路由测试\" />\n        </LinearLayout>\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/values/color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"red\">#f15533</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">10dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n    <dimen name=\"between_cell\">3dp</dimen>\n    <dimen name=\"test_area_padding\">10dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n</resources>\n"
  },
  {
    "path": "arouter-annotation/build.gradle",
    "content": "apply plugin: 'java'\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n    targetCompatibility = JavaVersion.VERSION_1_8\n}\n\napply from: rootProject.file('gradle/publish.gradle')"
  },
  {
    "path": "arouter-annotation/gradle.properties",
    "content": "POM_NAME=ARouter Annotations\nPOM_ARTIFACT_ID=arouter-annotation\nPOM_PACKAGING=jar\nPOM_DESCRIPTION=The annotation used in arouter api\nVERSION_NAME=1.0.6"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/annotation/Autowired.java",
    "content": "package com.alibaba.android.arouter.facade.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 * Annotation for field, which need autowired.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/20 下午4:26\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.CLASS)\npublic @interface Autowired {\n\n    // Mark param's name or service name.\n    String name() default \"\";\n\n    // If required, app will be crash when value is null.\n    // Primitive type wont be check!\n    boolean required() default false;\n\n    // Description of the field\n    String desc() default \"\";\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/annotation/Interceptor.java",
    "content": "package com.alibaba.android.arouter.facade.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 * Mark a interceptor to interception the route.\n * BE ATTENTION : This annotation can be mark the implements of #{IInterceptor} ONLY!!!\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 14:03\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.CLASS)\npublic @interface Interceptor {\n    /**\n     * The priority of interceptor, ARouter will be excute them follow the priority.\n     */\n    int priority();\n\n    /**\n     * The name of interceptor, may be used to generate javadoc.\n     */\n    String name() default \"Default\";\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/annotation/Param.java",
    "content": "package com.alibaba.android.arouter.facade.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 * Used for mark param of page.\n * THIS ANNOTATION WAS DEPRECATED, USE 'Autowired' PLEASE!\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2016/11/22 18:01\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.CLASS)\n@Deprecated\npublic @interface Param {\n    /**\n     * The field name\n     */\n    String name() default \"\";\n\n    /**\n     * The description of the field\n     */\n    String desc() default \"No desc.\";\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/annotation/Route.java",
    "content": "package com.alibaba.android.arouter.facade.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 * Mark a page can be route by router.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/15 下午9:29\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.CLASS)\npublic @interface Route {\n\n    /**\n     * Path of route\n     */\n    String path();\n\n    /**\n     * Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!\n     */\n    String group() default \"\";\n\n    /**\n     * Name of route, used to generate javadoc.\n     */\n    String name() default \"\";\n\n    /**\n     * Extra data, can be set by user.\n     * Ps. U should use the integer num sign the switch, by bits. 10001010101010\n     */\n    int extras() default Integer.MIN_VALUE;\n\n    /**\n     * The priority of route.\n     */\n    int priority() default -1;\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/enums/RouteType.java",
    "content": "package com.alibaba.android.arouter.facade.enums;\n\n/**\n * Type of route enum.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 22:33\n */\npublic enum RouteType {\n    ACTIVITY(0, \"android.app.Activity\"),\n    SERVICE(1, \"android.app.Service\"),\n    PROVIDER(2, \"com.alibaba.android.arouter.facade.template.IProvider\"),\n    CONTENT_PROVIDER(-1, \"android.app.ContentProvider\"),\n    BOARDCAST(-1, \"\"),\n    METHOD(-1, \"\"),\n    FRAGMENT(-1, \"android.app.Fragment\"),\n    UNKNOWN(-1, \"Unknown route type\");\n\n    int id;\n    String className;\n\n    public int getId() {\n        return id;\n    }\n\n    public RouteType setId(int id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public RouteType setClassName(String className) {\n        this.className = className;\n        return this;\n    }\n\n    RouteType(int id, String className) {\n        this.id = id;\n        this.className = className;\n    }\n\n    public static RouteType parse(String name) {\n        for (RouteType routeType : RouteType.values()) {\n            if (routeType.getClassName().equals(name)) {\n                return routeType;\n            }\n        }\n\n        return UNKNOWN;\n    }\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/enums/TypeKind.java",
    "content": "package com.alibaba.android.arouter.facade.enums;\n\n/**\n * Kind of field type.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2017-03-16 19:13:38\n */\npublic enum TypeKind {\n    // Base type\n    BOOLEAN,\n    BYTE,\n    SHORT,\n    INT,\n    LONG,\n    CHAR,\n    FLOAT,\n    DOUBLE,\n\n    // Other type\n    STRING,\n    SERIALIZABLE,\n    PARCELABLE,\n    OBJECT;\n}\n"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/model/RouteMeta.java",
    "content": "package com.alibaba.android.arouter.facade.model;\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.enums.RouteType;\n\nimport java.util.Map;\n\nimport javax.lang.model.element.Element;\n\n/**\n * It contains basic route information.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/24 09:45\n */\npublic class RouteMeta {\n    private RouteType type;         // Type of route\n    private Element rawType;        // Raw type of route\n    private Class<?> destination;   // Destination\n    private String path;            // Path of route\n    private String group;           // Group of route\n    private int priority = -1;      // The smaller the number, the higher the priority\n    private int extra;              // Extra data\n    private Map<String, Integer> paramsType;  // Param type\n    private String name;\n\n    private Map<String, Autowired> injectConfig;  // Cache inject config.\n\n    public RouteMeta() {\n    }\n\n    /**\n     * For versions of 'compiler' less than 1.0.7, contain 1.0.7\n     *\n     * @param type        type\n     * @param destination destination\n     * @param path        path\n     * @param group       group\n     * @param priority    priority\n     * @param extra       extra\n     * @return this\n     */\n    public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, int priority, int extra) {\n        return new RouteMeta(type, null, destination, null, path, group, null, priority, extra);\n    }\n\n    /**\n     * For versions of 'compiler' greater than 1.0.7\n     *\n     * @param type        type\n     * @param destination destination\n     * @param path        path\n     * @param group       group\n     * @param paramsType  paramsType\n     * @param priority    priority\n     * @param extra       extra\n     * @return this\n     */\n    public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {\n        return new RouteMeta(type, null, destination, null, path, group, paramsType, priority, extra);\n    }\n\n    /**\n     * Type\n     *\n     * @param route       route\n     * @param destination destination\n     * @param type        type\n     */\n    public RouteMeta(Route route, Class<?> destination, RouteType type) {\n        this(type, null, destination, route.name(), route.path(), route.group(), null, route.priority(), route.extras());\n    }\n\n    /**\n     * Type\n     *\n     * @param route      route\n     * @param rawType    rawType\n     * @param type       type\n     * @param paramsType paramsType\n     */\n    public RouteMeta(Route route, Element rawType, RouteType type, Map<String, Integer> paramsType) {\n        this(type, rawType, null, route.name(), route.path(), route.group(), paramsType, route.priority(), route.extras());\n    }\n\n    /**\n     * Type\n     *\n     * @param type        type\n     * @param rawType     rawType\n     * @param destination destination\n     * @param path        path\n     * @param group       group\n     * @param paramsType  paramsType\n     * @param priority    priority\n     * @param extra       extra\n     */\n    public RouteMeta(RouteType type, Element rawType, Class<?> destination, String name, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {\n        this.type = type;\n        this.name = name;\n        this.destination = destination;\n        this.rawType = rawType;\n        this.path = path;\n        this.group = group;\n        this.paramsType = paramsType;\n        this.priority = priority;\n        this.extra = extra;\n    }\n\n    public Map<String, Integer> getParamsType() {\n        return paramsType;\n    }\n\n    public RouteMeta setParamsType(Map<String, Integer> paramsType) {\n        this.paramsType = paramsType;\n        return this;\n    }\n\n    public Map<String, Autowired> getInjectConfig() {\n        return injectConfig;\n    }\n\n    public void setInjectConfig(Map<String, Autowired> injectConfig) {\n        this.injectConfig = injectConfig;\n    }\n\n    public Element getRawType() {\n        return rawType;\n    }\n\n    public RouteMeta setRawType(Element rawType) {\n        this.rawType = rawType;\n        return this;\n    }\n\n    public RouteType getType() {\n        return type;\n    }\n\n    public RouteMeta setType(RouteType type) {\n        this.type = type;\n        return this;\n    }\n\n    public Class<?> getDestination() {\n        return destination;\n    }\n\n    public RouteMeta setDestination(Class<?> destination) {\n        this.destination = destination;\n        return this;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public RouteMeta setPath(String path) {\n        this.path = path;\n        return this;\n    }\n\n    public String getGroup() {\n        return group;\n    }\n\n    public RouteMeta setGroup(String group) {\n        this.group = group;\n        return this;\n    }\n\n    public int getPriority() {\n        return priority;\n    }\n\n    public RouteMeta setPriority(int priority) {\n        this.priority = priority;\n        return this;\n    }\n\n    public int getExtra() {\n        return extra;\n    }\n\n    public RouteMeta setExtra(int extra) {\n        this.extra = extra;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String toString() {\n        return \"RouteMeta{\" +\n                \"type=\" + type +\n                \", rawType=\" + rawType +\n                \", destination=\" + destination +\n                \", path='\" + path + '\\'' +\n                \", group='\" + group + '\\'' +\n                \", priority=\" + priority +\n                \", extra=\" + extra +\n                \", paramsType=\" + paramsType +\n                \", name='\" + name + '\\'' +\n                '}';\n    }\n}"
  },
  {
    "path": "arouter-annotation/src/main/java/com/alibaba/android/arouter/facade/model/TypeWrapper.java",
    "content": "package com.alibaba.android.arouter.facade.model;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\n\n/**\n * Used for get type of target object.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 17/10/26 11:56:22\n */\npublic class TypeWrapper<T> {\n    protected final Type type;\n\n    protected TypeWrapper() {\n        Type superClass = getClass().getGenericSuperclass();\n\n        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];\n    }\n\n    public Type getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "arouter-api/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)\n    buildToolsVersion BUILDTOOLS_VERSION\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [AROUTER_MODULE_NAME: project.getName()]\n            }\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    buildTypes {\n        release {\n            debuggable false\n            minifyEnabled false\n        }\n\n        lintOptions { abortOnError false }\n    }\n}\n\ndependencies {\n    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'\n    api 'com.alibaba:arouter-annotation:1.0.6'\n    implementation \"com.android.support:support-v4:${SUPPORT_LIB_VERSION}\"\n}\n\napply from: rootProject.file('gradle/publish.gradle')"
  },
  {
    "path": "arouter-api/gradle.properties",
    "content": "POM_NAME=ARouter SDK\nPOM_ARTIFACT_ID=arouter-api\nPOM_PACKAGING=aar\nPOM_DESCRIPTION=Route framework for android\nVERSION_NAME=1.5.2"
  },
  {
    "path": "arouter-api/src/main/AndroidManifest.xml",
    "content": "<manifest\n    package=\"com.alibaba.android.arouter\">\n</manifest>\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/base/UniqueKeyTreeMap.java",
    "content": "package com.alibaba.android.arouter.base;\n\nimport java.util.TreeMap;\n\n/**\n * TreeMap with unique key.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/22 下午5:01\n */\npublic class UniqueKeyTreeMap<K, V> extends TreeMap<K, V> {\n    private String tipText;\n\n    public UniqueKeyTreeMap(String exceptionText) {\n        super();\n\n        tipText = exceptionText;\n    }\n\n    @Override\n    public V put(K key, V value) {\n        if (containsKey(key)) {\n            throw new RuntimeException(String.format(tipText, key));\n        } else {\n            return super.put(key, value);\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/AutowiredLifecycleCallback.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Build;\nimport android.os.Bundle;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\n\n/**\n * LifecycleCallback for autowired.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/21 上午11:28\n */\n@Deprecated\n@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)\npublic class AutowiredLifecycleCallback implements Application.ActivityLifecycleCallbacks {\n    @Override\n    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {\n        ARouter.getInstance().inject(activity);\n    }\n\n    @Override\n    public void onActivityStarted(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityResumed(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityPaused(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivityStopped(Activity activity) {\n\n    }\n\n    @Override\n    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {\n\n    }\n\n    @Override\n    public void onActivityDestroyed(Activity activity) {\n\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/AutowiredServiceImpl.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport android.content.Context;\nimport android.util.LruCache;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.service.AutowiredService;\nimport com.alibaba.android.arouter.facade.template.ISyringe;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static com.alibaba.android.arouter.utils.Consts.SUFFIX_AUTOWIRED;\n\n/**\n * param inject service impl.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/28 下午6:08\n */\n@Route(path = \"/arouter/service/autowired\")\npublic class AutowiredServiceImpl implements AutowiredService {\n    private LruCache<String, ISyringe> classCache;\n    private List<String> blackList;\n\n    @Override\n    public void init(Context context) {\n        classCache = new LruCache<>(50);\n        blackList = new ArrayList<>();\n    }\n\n    @Override\n    public void autowire(Object instance) {\n        doInject(instance, null);\n    }\n\n    /**\n     * Recursive injection\n     *\n     * @param instance who call me.\n     * @param parent   parent of me.\n     */\n    private void doInject(Object instance, Class<?> parent) {\n        Class<?> clazz = null == parent ? instance.getClass() : parent;\n\n        ISyringe syringe = getSyringe(clazz);\n        if (null != syringe) {\n            syringe.inject(instance);\n        }\n\n        Class<?> superClazz = clazz.getSuperclass();\n        // has parent and its not the class of framework.\n        if (null != superClazz && !superClazz.getName().startsWith(\"android\")) {\n            doInject(instance, superClazz);\n        }\n    }\n\n    private ISyringe getSyringe(Class<?> clazz) {\n        String className = clazz.getName();\n\n        try {\n            if (!blackList.contains(className)) {\n                ISyringe syringeHelper = classCache.get(className);\n                if (null == syringeHelper) {  // No cache.\n                    syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();\n                }\n                classCache.put(className, syringeHelper);\n                return syringeHelper;\n            }\n        } catch (Exception e) {\n            blackList.add(className);    // This instance need not autowired.\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/InstrumentationHook.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport android.app.Activity;\nimport android.app.Instrumentation;\nimport android.content.Intent;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.alibaba.android.arouter.utils.Consts;\nimport com.alibaba.android.arouter.utils.TextUtils;\n\nimport java.lang.reflect.Field;\n\n\n/**\n * Use ARouter.getInstance().inject(this) now!\n *\n * Hook the instrumentation, inject values for activity's field.\n * Support normal activity only, not contain unit test.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2016/11/24 16:42\n */\n@Deprecated\npublic class InstrumentationHook extends Instrumentation {\n    /**\n     * Hook the instrumentation's newActivity, inject\n     * <p>\n     * Perform instantiation of the process's {@link Activity} object.  The\n     * default implementation provides the normal system behavior.\n     *\n     * @param cl        The ClassLoader with which to instantiate the object.\n     * @param className The name of the class implementing the Activity\n     *                  object.\n     * @param intent    The Intent object that specified the activity class being\n     *                  instantiated.\n     * @return The newly instantiated Activity object.\n     */\n    public Activity newActivity(ClassLoader cl, String className,\n                                Intent intent)\n            throws InstantiationException, IllegalAccessException,\n            ClassNotFoundException {\n\n//        return (Activity)cl.loadClass(className).newInstance();\n\n        Class<?> targetActivity = cl.loadClass(className);\n        Object instanceOfTarget = targetActivity.newInstance();\n\n        if (ARouter.canAutoInject()) {\n            String[] autoInjectParams = intent.getStringArrayExtra(ARouter.AUTO_INJECT);\n            if (null != autoInjectParams && autoInjectParams.length > 0) {\n                for (String paramsName : autoInjectParams) {\n                    Object value = intent.getExtras().get(TextUtils.getLeft(paramsName));\n                    if (null != value) {\n                        try {\n                            Field injectField = targetActivity.getDeclaredField(TextUtils.getLeft(paramsName));\n                            injectField.setAccessible(true);\n                            injectField.set(instanceOfTarget, value);\n                        } catch (Exception e) {\n                            ARouter.logger.error(Consts.TAG, \"Inject values for activity error! [\" + e.getMessage() + \"]\");\n                        }\n                    }\n                }\n            }\n        }\n\n        return (Activity) instanceOfTarget;\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/InterceptorServiceImpl.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport android.content.Context;\n\nimport com.alibaba.android.arouter.exception.HandlerException;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\nimport com.alibaba.android.arouter.facade.service.InterceptorService;\nimport com.alibaba.android.arouter.facade.template.IInterceptor;\nimport com.alibaba.android.arouter.thread.CancelableCountDownLatch;\nimport com.alibaba.android.arouter.utils.MapUtils;\n\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport static com.alibaba.android.arouter.launcher.ARouter.logger;\nimport static com.alibaba.android.arouter.utils.Consts.TAG;\n\n/**\n * All of interceptors\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/23 下午2:09\n */\n@Route(path = \"/arouter/service/interceptor\")\npublic class InterceptorServiceImpl implements InterceptorService {\n    private static boolean interceptorHasInit;\n    private static final Object interceptorInitLock = new Object();\n\n    @Override\n    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {\n        if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {\n\n            checkInterceptorsInitStatus();\n\n            if (!interceptorHasInit) {\n                callback.onInterrupt(new HandlerException(\"Interceptors initialization takes too much time.\"));\n                return;\n            }\n\n            LogisticsCenter.executor.execute(new Runnable() {\n                @Override\n                public void run() {\n                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());\n                    try {\n                        _execute(0, interceptorCounter, postcard);\n                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);\n                        if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.\n                            callback.onInterrupt(new HandlerException(\"The interceptor processing timed out.\"));\n                        } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.\n                            callback.onInterrupt((Throwable) postcard.getTag());\n                        } else {\n                            callback.onContinue(postcard);\n                        }\n                    } catch (Exception e) {\n                        callback.onInterrupt(e);\n                    }\n                }\n            });\n        } else {\n            callback.onContinue(postcard);\n        }\n    }\n\n    /**\n     * Excute interceptor\n     *\n     * @param index    current interceptor index\n     * @param counter  interceptor counter\n     * @param postcard routeMeta\n     */\n    private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {\n        if (index < Warehouse.interceptors.size()) {\n            IInterceptor iInterceptor = Warehouse.interceptors.get(index);\n            iInterceptor.process(postcard, new InterceptorCallback() {\n                @Override\n                public void onContinue(Postcard postcard) {\n                    // Last interceptor excute over with no exception.\n                    counter.countDown();\n                    _execute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.\n                }\n\n                @Override\n                public void onInterrupt(Throwable exception) {\n                    // Last interceptor execute over with fatal exception.\n\n                    postcard.setTag(null == exception ? new HandlerException(\"No message.\") : exception);    // save the exception message for backup.\n                    counter.cancel();\n                    // Be attention, maybe the thread in callback has been changed,\n                    // then the catch block(L207) will be invalid.\n                    // The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!\n//                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.\n//                        throw new HandlerException(exception.getMessage());\n//                    }\n                }\n            });\n        }\n    }\n\n    @Override\n    public void init(final Context context) {\n        LogisticsCenter.executor.execute(new Runnable() {\n            @Override\n            public void run() {\n                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {\n                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {\n                        Class<? extends IInterceptor> interceptorClass = entry.getValue();\n                        try {\n                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();\n                            iInterceptor.init(context);\n                            Warehouse.interceptors.add(iInterceptor);\n                        } catch (Exception ex) {\n                            throw new HandlerException(TAG + \"ARouter init interceptor error! name = [\" + interceptorClass.getName() + \"], reason = [\" + ex.getMessage() + \"]\");\n                        }\n                    }\n\n                    interceptorHasInit = true;\n\n                    logger.info(TAG, \"ARouter interceptors init over.\");\n\n                    synchronized (interceptorInitLock) {\n                        interceptorInitLock.notifyAll();\n                    }\n                }\n            }\n        });\n    }\n\n    private static void checkInterceptorsInitStatus() {\n        synchronized (interceptorInitLock) {\n            while (!interceptorHasInit) {\n                try {\n                    interceptorInitLock.wait(10 * 1000);\n                } catch (InterruptedException e) {\n                    throw new HandlerException(TAG + \"Interceptor init cost too much time error! reason = [\" + e.getMessage() + \"]\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/LogisticsCenter.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport android.content.Context;\nimport android.net.Uri;\n\nimport com.alibaba.android.arouter.exception.HandlerException;\nimport com.alibaba.android.arouter.exception.NoRouteFoundException;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.enums.TypeKind;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.template.IInterceptorGroup;\nimport com.alibaba.android.arouter.facade.template.IProvider;\nimport com.alibaba.android.arouter.facade.template.IProviderGroup;\nimport com.alibaba.android.arouter.facade.template.IRouteGroup;\nimport com.alibaba.android.arouter.facade.template.IRouteRoot;\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.alibaba.android.arouter.utils.ClassUtils;\nimport com.alibaba.android.arouter.utils.Consts;\nimport com.alibaba.android.arouter.utils.MapUtils;\nimport com.alibaba.android.arouter.utils.PackageUtils;\nimport com.alibaba.android.arouter.utils.TextUtils;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport static com.alibaba.android.arouter.launcher.ARouter.logger;\nimport static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_CACHE_KEY;\nimport static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_KEY_MAP;\nimport static com.alibaba.android.arouter.utils.Consts.DOT;\nimport static com.alibaba.android.arouter.utils.Consts.ROUTE_ROOT_PAKCAGE;\nimport static com.alibaba.android.arouter.utils.Consts.SDK_NAME;\nimport static com.alibaba.android.arouter.utils.Consts.SEPARATOR;\nimport static com.alibaba.android.arouter.utils.Consts.SUFFIX_INTERCEPTORS;\nimport static com.alibaba.android.arouter.utils.Consts.SUFFIX_PROVIDERS;\nimport static com.alibaba.android.arouter.utils.Consts.SUFFIX_ROOT;\nimport static com.alibaba.android.arouter.utils.Consts.TAG;\n\n/**\n * LogisticsCenter contains all of the map.\n * <p>\n * 1. Creates instance when it is first used.\n * 2. Handler Multi-Module relationship map(*)\n * 3. Complex logic to solve duplicate group definition\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 15:02\n */\npublic class LogisticsCenter {\n    private static Context mContext;\n    static ThreadPoolExecutor executor;\n    private static boolean registerByPlugin;\n\n    /**\n     * arouter-auto-register plugin will generate code inside this method\n     * call this method to register all Routers, Interceptors and Providers\n     */\n    private static void loadRouterMap() {\n        registerByPlugin = false;\n        // auto generate register code by gradle plugin: arouter-auto-register\n        // looks like below:\n        // registerRouteRoot(new ARouter..Root..modulejava());\n        // registerRouteRoot(new ARouter..Root..modulekotlin());\n    }\n\n    /**\n     * register by class name\n     * Sacrificing a bit of efficiency to solve\n     * the problem that the main dex file size is too large\n     */\n    private static void register(String className) {\n        if (!TextUtils.isEmpty(className)) {\n            try {\n                Class<?> clazz = Class.forName(className);\n                Object obj = clazz.getConstructor().newInstance();\n                if (obj instanceof IRouteRoot) {\n                    registerRouteRoot((IRouteRoot) obj);\n                } else if (obj instanceof IProviderGroup) {\n                    registerProvider((IProviderGroup) obj);\n                } else if (obj instanceof IInterceptorGroup) {\n                    registerInterceptor((IInterceptorGroup) obj);\n                } else {\n                    logger.info(TAG, \"register failed, class name: \" + className\n                            + \" should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.\");\n                }\n            } catch (Exception e) {\n                logger.error(TAG,\"register class error:\" + className, e);\n            }\n        }\n    }\n\n    /**\n     * method for arouter-auto-register plugin to register Routers\n     * @param routeRoot IRouteRoot implementation class in the package: com.alibaba.android.arouter.core.routers\n     */\n    private static void registerRouteRoot(IRouteRoot routeRoot) {\n        markRegisteredByPlugin();\n        if (routeRoot != null) {\n            routeRoot.loadInto(Warehouse.groupsIndex);\n        }\n    }\n\n    /**\n     * method for arouter-auto-register plugin to register Interceptors\n     * @param interceptorGroup IInterceptorGroup implementation class in the package: com.alibaba.android.arouter.core.routers\n     */\n    private static void registerInterceptor(IInterceptorGroup interceptorGroup) {\n        markRegisteredByPlugin();\n        if (interceptorGroup != null) {\n            interceptorGroup.loadInto(Warehouse.interceptorsIndex);\n        }\n    }\n\n    /**\n     * method for arouter-auto-register plugin to register Providers\n     * @param providerGroup IProviderGroup implementation class in the package: com.alibaba.android.arouter.core.routers\n     */\n    private static void registerProvider(IProviderGroup providerGroup) {\n        markRegisteredByPlugin();\n        if (providerGroup != null) {\n            providerGroup.loadInto(Warehouse.providersIndex);\n        }\n    }\n\n    /**\n     * mark already registered by arouter-auto-register plugin\n     */\n    private static void markRegisteredByPlugin() {\n        if (!registerByPlugin) {\n            registerByPlugin = true;\n        }\n    }\n\n    /**\n     * LogisticsCenter init, load all metas in memory. Demand initialization\n     */\n    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {\n        mContext = context;\n        executor = tpe;\n\n        try {\n            long startInit = System.currentTimeMillis();\n            //load by plugin first\n            loadRouterMap();\n            if (registerByPlugin) {\n                logger.info(TAG, \"Load router map by arouter-auto-register plugin.\");\n            } else {\n                Set<String> routerMap;\n\n                // It will rebuild router map every times when debuggable.\n                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {\n                    logger.info(TAG, \"Run with debug mode or new install, rebuild router map.\");\n                    // These class was generated by arouter-compiler.\n                    routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);\n                    if (!routerMap.isEmpty()) {\n                        context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();\n                    }\n\n                    PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.\n                } else {\n                    logger.info(TAG, \"Load router map from cache.\");\n                    routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));\n                }\n\n                logger.info(TAG, \"Find router map finished, map size = \" + routerMap.size() + \", cost \" + (System.currentTimeMillis() - startInit) + \" ms.\");\n                startInit = System.currentTimeMillis();\n\n                for (String className : routerMap) {\n                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {\n                        // This one of root elements, load root.\n                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);\n                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {\n                        // Load interceptorMeta\n                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);\n                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {\n                        // Load providerIndex\n                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);\n                    }\n                }\n            }\n\n            logger.info(TAG, \"Load root element finished, cost \" + (System.currentTimeMillis() - startInit) + \" ms.\");\n\n            if (Warehouse.groupsIndex.size() == 0) {\n                logger.error(TAG, \"No mapping files were found, check your configuration please!\");\n            }\n\n            if (ARouter.debuggable()) {\n                logger.debug(TAG, String.format(Locale.getDefault(), \"LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]\", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));\n            }\n        } catch (Exception e) {\n            throw new HandlerException(TAG + \"ARouter init logistics center exception! [\" + e.getMessage() + \"]\");\n        }\n    }\n\n    /**\n     * Build postcard by serviceName\n     *\n     * @param serviceName interfaceName\n     * @return postcard\n     */\n    public static Postcard buildProvider(String serviceName) {\n        RouteMeta meta = Warehouse.providersIndex.get(serviceName);\n\n        if (null == meta) {\n            return null;\n        } else {\n            return new Postcard(meta.getPath(), meta.getGroup());\n        }\n    }\n\n    /**\n     * Completion the postcard by route metas\n     *\n     * @param postcard Incomplete postcard, should complete by this method.\n     */\n    public synchronized static void completion(Postcard postcard) {\n        if (null == postcard) {\n            throw new NoRouteFoundException(TAG + \"No postcard!\");\n        }\n\n        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());\n        if (null == routeMeta) {\n            // Maybe its does't exist, or didn't load.\n            if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {\n                throw new NoRouteFoundException(TAG + \"There is no route match the path [\" + postcard.getPath() + \"], in group [\" + postcard.getGroup() + \"]\");\n            } else {\n                // Load route and cache it into memory, then delete from metas.\n                try {\n                    if (ARouter.debuggable()) {\n                        logger.debug(TAG, String.format(Locale.getDefault(), \"The group [%s] starts loading, trigger by [%s]\", postcard.getGroup(), postcard.getPath()));\n                    }\n\n                    addRouteGroupDynamic(postcard.getGroup(), null);\n\n                    if (ARouter.debuggable()) {\n                        logger.debug(TAG, String.format(Locale.getDefault(), \"The group [%s] has already been loaded, trigger by [%s]\", postcard.getGroup(), postcard.getPath()));\n                    }\n                } catch (Exception e) {\n                    throw new HandlerException(TAG + \"Fatal exception when loading group meta. [\" + e.getMessage() + \"]\");\n                }\n\n                completion(postcard);   // Reload\n            }\n        } else {\n            postcard.setDestination(routeMeta.getDestination());\n            postcard.setType(routeMeta.getType());\n            postcard.setPriority(routeMeta.getPriority());\n            postcard.setExtra(routeMeta.getExtra());\n\n            Uri rawUri = postcard.getUri();\n            if (null != rawUri) {   // Try to set params into bundle.\n                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);\n                Map<String, Integer> paramsType = routeMeta.getParamsType();\n\n                if (MapUtils.isNotEmpty(paramsType)) {\n                    // Set value by its type, just for params which annotation by @Param\n                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {\n                        setValue(postcard,\n                                params.getValue(),\n                                params.getKey(),\n                                resultMap.get(params.getKey()));\n                    }\n\n                    // Save params name which need auto inject.\n                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));\n                }\n\n                // Save raw uri\n                postcard.withString(ARouter.RAW_URI, rawUri.toString());\n            }\n\n            switch (routeMeta.getType()) {\n                case PROVIDER:  // if the route is provider, should find its instance\n                    // Its provider, so it must implement IProvider\n                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();\n                    IProvider instance = Warehouse.providers.get(providerMeta);\n                    if (null == instance) { // There's no instance of this provider\n                        IProvider provider;\n                        try {\n                            provider = providerMeta.getConstructor().newInstance();\n                            provider.init(mContext);\n                            Warehouse.providers.put(providerMeta, provider);\n                            instance = provider;\n                        } catch (Exception e) {\n                            logger.error(TAG, \"Init provider failed!\", e);\n                            throw new HandlerException(\"Init provider failed!\");\n                        }\n                    }\n                    postcard.setProvider(instance);\n                    postcard.greenChannel();    // Provider should skip all of interceptors\n                    break;\n                case FRAGMENT:\n                    postcard.greenChannel();    // Fragment needn't interceptors\n                default:\n                    break;\n            }\n        }\n    }\n\n    /**\n     * Set value by known type\n     *\n     * @param postcard postcard\n     * @param typeDef  type\n     * @param key      key\n     * @param value    value\n     */\n    private static void setValue(Postcard postcard, Integer typeDef, String key, String value) {\n        if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {\n            return;\n        }\n\n        try {\n            if (null != typeDef) {\n                if (typeDef == TypeKind.BOOLEAN.ordinal()) {\n                    postcard.withBoolean(key, Boolean.parseBoolean(value));\n                } else if (typeDef == TypeKind.BYTE.ordinal()) {\n                    postcard.withByte(key, Byte.parseByte(value));\n                } else if (typeDef == TypeKind.SHORT.ordinal()) {\n                    postcard.withShort(key, Short.parseShort(value));\n                } else if (typeDef == TypeKind.INT.ordinal()) {\n                    postcard.withInt(key, Integer.parseInt(value));\n                } else if (typeDef == TypeKind.LONG.ordinal()) {\n                    postcard.withLong(key, Long.parseLong(value));\n                } else if (typeDef == TypeKind.FLOAT.ordinal()) {\n                    postcard.withFloat(key, Float.parseFloat(value));\n                } else if (typeDef == TypeKind.DOUBLE.ordinal()) {\n                    postcard.withDouble(key, Double.parseDouble(value));\n                } else if (typeDef == TypeKind.STRING.ordinal()) {\n                    postcard.withString(key, value);\n                } else if (typeDef == TypeKind.PARCELABLE.ordinal()) {\n                    // TODO : How to description parcelable value with string?\n                } else if (typeDef == TypeKind.OBJECT.ordinal()) {\n                    postcard.withString(key, value);\n                } else {    // Compatible compiler sdk 1.0.3, in that version, the string type = 18\n                    postcard.withString(key, value);\n                }\n            } else {\n                postcard.withString(key, value);\n            }\n        } catch (Throwable ex) {\n            logger.warning(Consts.TAG, \"LogisticsCenter setValue failed! \" + ex.getMessage());\n        }\n    }\n\n    /**\n     * Suspend business, clear cache.\n     */\n    public static void suspend() {\n        Warehouse.clear();\n    }\n\n    public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {\n        if (Warehouse.groupsIndex.containsKey(groupName)){\n            // If this group is included, but it has not been loaded\n            // load this group first, because dynamic route has high priority.\n            Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);\n            Warehouse.groupsIndex.remove(groupName);\n        }\n\n        // cover old group.\n        if (null != group) {\n            group.loadInto(Warehouse.routes);\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/core/Warehouse.java",
    "content": "package com.alibaba.android.arouter.core;\n\nimport com.alibaba.android.arouter.base.UniqueKeyTreeMap;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.template.IInterceptor;\nimport com.alibaba.android.arouter.facade.template.IProvider;\nimport com.alibaba.android.arouter.facade.template.IRouteGroup;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Storage of route meta and other data.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/23 下午1:39\n */\nclass Warehouse {\n    // Cache route and metas\n    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();\n    static Map<String, RouteMeta> routes = new HashMap<>();\n\n    // Cache provider\n    static Map<Class, IProvider> providers = new HashMap<>();\n    static Map<String, RouteMeta> providersIndex = new HashMap<>();\n\n    // Cache interceptor\n    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>(\"More than one interceptors use same priority [%s]\");\n    static List<IInterceptor> interceptors = new ArrayList<>();\n\n    static void clear() {\n        routes.clear();\n        groupsIndex.clear();\n        providers.clear();\n        providersIndex.clear();\n        interceptors.clear();\n        interceptorsIndex.clear();\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/exception/HandlerException.java",
    "content": "package com.alibaba.android.arouter.exception;\n\n/**\n * 主流程的处理异常\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 15/12/7 上午10:30\n */\npublic class HandlerException extends RuntimeException {\n    public HandlerException(String detailMessage) {\n        super(detailMessage);\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/exception/InitException.java",
    "content": "package com.alibaba.android.arouter.exception;\n\n/**\n * 初始化相关异常\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2015-12-07 14:17:30\n */\npublic class InitException extends RuntimeException {\n    public InitException(String detailMessage) {\n        super(detailMessage);\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/exception/NoRouteFoundException.java",
    "content": "package com.alibaba.android.arouter.exception;\n\n/**\n * As its name\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/24 10:43\n */\npublic class NoRouteFoundException extends RuntimeException {\n    /**\n     * Constructs a new {@code RuntimeException} with the current stack trace\n     * and the specified detail message.\n     *\n     * @param detailMessage the detail message for this exception.\n     */\n    public NoRouteFoundException(String detailMessage) {\n        super(detailMessage);\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/Postcard.java",
    "content": "package com.alibaba.android.arouter.facade;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.support.annotation.Nullable;\nimport android.support.annotation.RequiresApi;\nimport android.support.v4.app.ActivityOptionsCompat;\nimport android.util.SparseArray;\n\nimport com.alibaba.android.arouter.facade.callback.NavigationCallback;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.service.SerializationService;\nimport com.alibaba.android.arouter.facade.template.IProvider;\nimport com.alibaba.android.arouter.launcher.ARouter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\n\n/**\n * A container that contains the roadmap.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.1.0\n * @since 16/8/22 19:16\n */\npublic final class Postcard extends RouteMeta {\n    // Base\n    private Uri uri;\n    private Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!\n    private Bundle mBundle;         // Data to transform\n    private int flags = 0;         // Flags of route\n    private int timeout = 300;      // Navigation timeout, TimeUnit.Second\n    private IProvider provider;     // It will be set value, if this postcard was provider.\n    private boolean greenChannel;\n    private SerializationService serializationService;\n    private Context context;        // May application or activity, check instance type before use it.\n    private String action;\n\n    // Animation\n    private Bundle optionsCompat;    // The transition animation of activity\n    private int enterAnim = -1;\n    private int exitAnim = -1;\n\n    public Bundle getOptionsBundle() {\n        return optionsCompat;\n    }\n\n    public int getEnterAnim() {\n        return enterAnim;\n    }\n\n    public int getExitAnim() {\n        return exitAnim;\n    }\n\n    public IProvider getProvider() {\n        return provider;\n    }\n\n    public Postcard setProvider(IProvider provider) {\n        this.provider = provider;\n        return this;\n    }\n\n    public Postcard() {\n        this(null, null);\n    }\n\n    public Postcard(String path, String group) {\n        this(path, group, null, null);\n    }\n\n    public Postcard(String path, String group, Uri uri, Bundle bundle) {\n        setPath(path);\n        setGroup(group);\n        setUri(uri);\n        this.mBundle = (null == bundle ? new Bundle() : bundle);\n    }\n\n    public boolean isGreenChannel() {\n        return greenChannel;\n    }\n\n    public Object getTag() {\n        return tag;\n    }\n\n    public Postcard setTag(Object tag) {\n        this.tag = tag;\n        return this;\n    }\n\n    public Bundle getExtras() {\n        return mBundle;\n    }\n\n    public int getTimeout() {\n        return timeout;\n    }\n\n    /**\n     * Set timeout of navigation this time.\n     *\n     * @param timeout timeout\n     * @return this\n     */\n    public Postcard setTimeout(int timeout) {\n        this.timeout = timeout;\n        return this;\n    }\n\n    public Uri getUri() {\n        return uri;\n    }\n\n    public Postcard setUri(Uri uri) {\n        this.uri = uri;\n        return this;\n    }\n\n    /**\n     * Navigation to the route with path in postcard.\n     * No param, will be use application context.\n     */\n    public Object navigation() {\n        return navigation(null);\n    }\n\n    /**\n     * Navigation to the route with path in postcard.\n     *\n     * @param context Activity and so on.\n     */\n    public Object navigation(Context context) {\n        return navigation(context, null);\n    }\n\n    /**\n     * Navigation to the route with path in postcard.\n     *\n     * @param context Activity and so on.\n     */\n    public Object navigation(Context context, NavigationCallback callback) {\n        return ARouter.getInstance().navigation(context, this, -1, callback);\n    }\n\n    /**\n     * Navigation to the route with path in postcard.\n     *\n     * @param mContext    Activity and so on.\n     * @param requestCode startActivityForResult's param\n     */\n    public void navigation(Activity mContext, int requestCode) {\n        navigation(mContext, requestCode, null);\n    }\n\n    /**\n     * Navigation to the route with path in postcard.\n     *\n     * @param mContext    Activity and so on.\n     * @param requestCode startActivityForResult's param\n     */\n    public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {\n        ARouter.getInstance().navigation(mContext, this, requestCode, callback);\n    }\n\n    /**\n     * Green channel, it will skip all of interceptors.\n     *\n     * @return this\n     */\n    public Postcard greenChannel() {\n        this.greenChannel = true;\n        return this;\n    }\n\n    /**\n     * BE ATTENTION TO THIS METHOD WAS <P>SET, NOT ADD!</P>\n     */\n    public Postcard with(Bundle bundle) {\n        if (null != bundle) {\n            mBundle = bundle;\n        }\n\n        return this;\n    }\n\n    /**\n     * Set special flags controlling how this intent is handled.  Most values\n     * here depend on the type of component being executed by the Intent,\n     * specifically the FLAG_ACTIVITY_* flags are all for use with\n     * {@link Context#startActivity Context.startActivity()} and the\n     * FLAG_RECEIVER_* flags are all for use with\n     * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.\n     */\n    public Postcard withFlags(int flag) {\n        this.flags = flag;\n        return this;\n    }\n\n    /**\n     * Add additional flags to the intent (or with existing flags\n     * value).\n     *\n     * @param flags The new flags to set.\n     * @return Returns the same Intent object, for chaining multiple calls\n     * into a single statement.\n     * @see #withFlags\n     */\n    public Postcard addFlags(int flags) {\n        this.flags |= flags;\n        return this;\n    }\n\n    public int getFlags() {\n        return flags;\n    }\n\n    /**\n     * Set object value, the value will be convert to string by 'Fastjson'\n     *\n     * @param key   a String, or null\n     * @param value a Object, or null\n     * @return current\n     */\n    public Postcard withObject(@Nullable String key, @Nullable Object value) {\n        serializationService = ARouter.getInstance().navigation(SerializationService.class);\n        mBundle.putString(key, serializationService.object2Json(value));\n        return this;\n    }\n\n    // Follow api copy from #{Bundle}\n\n    /**\n     * Inserts a String value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a String, or null\n     * @return current\n     */\n    public Postcard withString(@Nullable String key, @Nullable String value) {\n        mBundle.putString(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a Boolean value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a boolean\n     * @return current\n     */\n    public Postcard withBoolean(@Nullable String key, boolean value) {\n        mBundle.putBoolean(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a short value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a short\n     * @return current\n     */\n    public Postcard withShort(@Nullable String key, short value) {\n        mBundle.putShort(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts an int value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value an int\n     * @return current\n     */\n    public Postcard withInt(@Nullable String key, int value) {\n        mBundle.putInt(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a long value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a long\n     * @return current\n     */\n    public Postcard withLong(@Nullable String key, long value) {\n        mBundle.putLong(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a double value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a double\n     * @return current\n     */\n    public Postcard withDouble(@Nullable String key, double value) {\n        mBundle.putDouble(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a byte value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a byte\n     * @return current\n     */\n    public Postcard withByte(@Nullable String key, byte value) {\n        mBundle.putByte(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a char value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a char\n     * @return current\n     */\n    public Postcard withChar(@Nullable String key, char value) {\n        mBundle.putChar(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a float value into the mapping of this Bundle, replacing\n     * any existing value for the given key.\n     *\n     * @param key   a String, or null\n     * @param value a float\n     * @return current\n     */\n    public Postcard withFloat(@Nullable String key, float value) {\n        mBundle.putFloat(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a CharSequence value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a CharSequence, or null\n     * @return current\n     */\n    public Postcard withCharSequence(@Nullable String key, @Nullable CharSequence value) {\n        mBundle.putCharSequence(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a Parcelable value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a Parcelable object, or null\n     * @return current\n     */\n    public Postcard withParcelable(@Nullable String key, @Nullable Parcelable value) {\n        mBundle.putParcelable(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts an array of Parcelable values into the mapping of this Bundle,\n     * replacing any existing value for the given key.  Either key or value may\n     * be null.\n     *\n     * @param key   a String, or null\n     * @param value an array of Parcelable objects, or null\n     * @return current\n     */\n    public Postcard withParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {\n        mBundle.putParcelableArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a List of Parcelable values into the mapping of this Bundle,\n     * replacing any existing value for the given key.  Either key or value may\n     * be null.\n     *\n     * @param key   a String, or null\n     * @param value an ArrayList of Parcelable objects, or null\n     * @return current\n     */\n    public Postcard withParcelableArrayList(@Nullable String key, @Nullable ArrayList<? extends Parcelable> value) {\n        mBundle.putParcelableArrayList(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a SparceArray of Parcelable values into the mapping of this\n     * Bundle, replacing any existing value for the given key.  Either key\n     * or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a SparseArray of Parcelable objects, or null\n     * @return current\n     */\n    public Postcard withSparseParcelableArray(@Nullable String key, @Nullable SparseArray<? extends Parcelable> value) {\n        mBundle.putSparseParcelableArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts an ArrayList value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value an ArrayList object, or null\n     * @return current\n     */\n    public Postcard withIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {\n        mBundle.putIntegerArrayList(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts an ArrayList value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value an ArrayList object, or null\n     * @return current\n     */\n    public Postcard withStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {\n        mBundle.putStringArrayList(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts an ArrayList value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value an ArrayList object, or null\n     * @return current\n     */\n    public Postcard withCharSequenceArrayList(@Nullable String key, @Nullable ArrayList<CharSequence> value) {\n        mBundle.putCharSequenceArrayList(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a Serializable value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a Serializable object, or null\n     * @return current\n     */\n    public Postcard withSerializable(@Nullable String key, @Nullable Serializable value) {\n        mBundle.putSerializable(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a byte array value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a byte array object, or null\n     * @return current\n     */\n    public Postcard withByteArray(@Nullable String key, @Nullable byte[] value) {\n        mBundle.putByteArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a short array value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a short array object, or null\n     * @return current\n     */\n    public Postcard withShortArray(@Nullable String key, @Nullable short[] value) {\n        mBundle.putShortArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a char array value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a char array object, or null\n     * @return current\n     */\n    public Postcard withCharArray(@Nullable String key, @Nullable char[] value) {\n        mBundle.putCharArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a float array value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a float array object, or null\n     * @return current\n     */\n    public Postcard withFloatArray(@Nullable String key, @Nullable float[] value) {\n        mBundle.putFloatArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a CharSequence array value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a CharSequence array object, or null\n     * @return current\n     */\n    public Postcard withCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {\n        mBundle.putCharSequenceArray(key, value);\n        return this;\n    }\n\n    /**\n     * Inserts a Bundle value into the mapping of this Bundle, replacing\n     * any existing value for the given key.  Either key or value may be null.\n     *\n     * @param key   a String, or null\n     * @param value a Bundle object, or null\n     * @return current\n     */\n    public Postcard withBundle(@Nullable String key, @Nullable Bundle value) {\n        mBundle.putBundle(key, value);\n        return this;\n    }\n\n    /**\n     * Set normal transition anim\n     *\n     * @param enterAnim enter\n     * @param exitAnim  exit\n     * @return current\n     */\n    public Postcard withTransition(int enterAnim, int exitAnim) {\n        this.enterAnim = enterAnim;\n        this.exitAnim = exitAnim;\n        return this;\n    }\n\n    /**\n     * Set options compat\n     *\n     * @param compat compat\n     * @return this\n     */\n    @RequiresApi(16)\n    public Postcard withOptionsCompat(ActivityOptionsCompat compat) {\n        if (null != compat) {\n            this.optionsCompat = compat.toBundle();\n        }\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"Postcard{\" +\n                \"uri=\" + uri +\n                \", tag=\" + tag +\n                \", mBundle=\" + mBundle +\n                \", flags=\" + flags +\n                \", timeout=\" + timeout +\n                \", provider=\" + provider +\n                \", greenChannel=\" + greenChannel +\n                \", optionsCompat=\" + optionsCompat +\n                \", enterAnim=\" + enterAnim +\n                \", exitAnim=\" + exitAnim +\n                \"}\\n\" +\n                super.toString();\n    }\n\n    public String getAction() {\n        return action;\n    }\n\n    public Postcard withAction(String action) {\n        this.action = action;\n        return this;\n    }\n\n    public Context getContext() {\n        return context;\n    }\n\n    public void setContext(Context context) {\n        this.context = context;\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/InterceptorCallback.java",
    "content": "package com.alibaba.android.arouter.facade.callback;\n\nimport com.alibaba.android.arouter.facade.Postcard;\n\n/**\n * The callback of interceptor.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/4 17:36\n */\npublic interface InterceptorCallback {\n\n    /**\n     * Continue process\n     *\n     * @param postcard route meta\n     */\n    void onContinue(Postcard postcard);\n\n    /**\n     * Interrupt process, pipeline will be destroy when this method called.\n     *\n     * @param exception Reson of interrupt.\n     */\n    void onInterrupt(Throwable exception);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/NavCallback.java",
    "content": "package com.alibaba.android.arouter.facade.callback;\n\nimport com.alibaba.android.arouter.facade.Postcard;\n\n/**\n * Easy to use navigation callback.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/4/10 下午12:59\n */\npublic abstract class NavCallback implements NavigationCallback {\n    @Override\n    public void onFound(Postcard postcard) {\n        // Do nothing\n    }\n\n    @Override\n    public void onLost(Postcard postcard) {\n        // Do nothing\n    }\n\n    @Override\n    public abstract void onArrival(Postcard postcard);\n\n    @Override\n    public void onInterrupt(Postcard postcard) {\n        // Do nothing\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/NavigationCallback.java",
    "content": "package com.alibaba.android.arouter.facade.callback;\n\nimport com.alibaba.android.arouter.facade.Postcard;\n\n/**\n * Callback after navigation.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2016/9/22 14:15\n */\npublic interface NavigationCallback {\n\n    /**\n     * Callback when find the destination.\n     *\n     * @param postcard meta\n     */\n    void onFound(Postcard postcard);\n\n    /**\n     * Callback after lose your way.\n     *\n     * @param postcard meta\n     */\n    void onLost(Postcard postcard);\n\n    /**\n     * Callback after navigation.\n     *\n     * @param postcard meta\n     */\n    void onArrival(Postcard postcard);\n\n    /**\n     * Callback on interrupt.\n     *\n     * @param postcard meta\n     */\n    void onInterrupt(Postcard postcard);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/AutowiredService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Service for autowired.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/28 下午6:06\n */\npublic interface AutowiredService extends IProvider {\n\n    /**\n     * Autowired core.\n     * @param instance the instance who need autowired.\n     */\n    void autowire(Object instance);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/ClassLoaderService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Get class by user, maybe someone use InstantRun and other tech will move dex files.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/23 下午12:16\n */\npublic interface ClassLoaderService extends IProvider {\n    Class<?> forName();\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/DegradeService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport android.content.Context;\n\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Provide degrade service for router, you can do something when route has lost.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2016/9/22 14:51\n */\npublic interface DegradeService extends IProvider {\n\n    /**\n     * Router has lost.\n     *\n     * @param postcard meta\n     */\n    void onLost(Context context, Postcard postcard);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/InterceptorService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Interceptor service\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/23 下午2:06\n */\npublic interface InterceptorService extends IProvider {\n\n    /**\n     * Do interceptions\n     */\n    void doInterceptions(Postcard postcard, InterceptorCallback callback);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/PathReplaceService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport android.net.Uri;\n\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Preprocess your path\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2016/12/9 16:48\n */\npublic interface PathReplaceService extends IProvider {\n\n    /**\n     * For normal path.\n     *\n     * @param path raw path\n     */\n    String forString(String path);\n\n    /**\n     * For uri type.\n     *\n     * @param uri raw uri\n     */\n    Uri forUri(Uri uri);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/PretreatmentService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport android.content.Context;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * Pretreatment service used for check if need navigation.\n *\n * @author zhilong [Contact me.](mailto:zhilong.liu@aliyun.com)\n * @version 1.0\n * @since 2019-05-08 11:53\n */\npublic interface PretreatmentService extends IProvider {\n    /**\n     * Do something before navigation.\n     *\n     * @param context  context\n     * @param postcard meta\n     * @return if need navigation.\n     */\n    boolean onPretreatment(Context context, Postcard postcard);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/SerializationService.java",
    "content": "package com.alibaba.android.arouter.facade.service;\n\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\nimport java.lang.reflect.Type;\n\n/**\n * Used for parse json string.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/4/10 下午1:43\n */\npublic interface SerializationService extends IProvider {\n\n    /**\n     * Parse json to object\n     *\n     * USE @parseObject PLEASE\n     *\n     * @param input json string\n     * @param clazz object type\n     * @return instance of object\n     */\n    @Deprecated\n    <T> T json2Object(String input, Class<T> clazz);\n\n    /**\n     * Object to json\n     *\n     * @param instance obj\n     * @return json string\n     */\n    String object2Json(Object instance);\n\n    /**\n     * Parse json to object\n     *\n     * @param input json string\n     * @param clazz object type\n     * @return instance of object\n     */\n    <T> T parseObject(String input, Type clazz);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IInterceptor.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\n\n/**\n * Used for inject custom logic when navigation.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 13:56\n */\npublic interface IInterceptor extends IProvider {\n\n    /**\n     * The operation of this interceptor.\n     *\n     * @param postcard meta\n     * @param callback cb\n     */\n    void process(Postcard postcard, InterceptorCallback callback);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IInterceptorGroup.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport java.util.Map;\n\n/**\n * Template of interceptor group.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/29 09:51\n */\npublic interface IInterceptorGroup {\n    /**\n     * Load interceptor to input\n     *\n     * @param interceptor input\n     */\n    void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/ILogger.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport com.alibaba.android.arouter.utils.Consts;\n\n/**\n * Logger\n *\n * @author 正纬 <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/5/16 下午5:39\n */\npublic interface ILogger {\n\n    boolean isShowLog = false;\n    boolean isShowStackTrace = false;\n    String defaultTag = Consts.TAG;\n\n    void showLog(boolean isShowLog);\n\n    void showStackTrace(boolean isShowStackTrace);\n\n    void debug(String tag, String message);\n\n    void info(String tag, String message);\n\n    void warning(String tag, String message);\n\n    void error(String tag, String message);\n\n    void error(String tag, String message, Throwable e);\n\n    void monitor(String message);\n\n    boolean isMonitorMode();\n\n    String getDefaultTag();\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IPolicy.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\n/**\n * Store policy.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/24 10:15\n */\n@Deprecated\npublic interface IPolicy {\n    int getFlag();\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IProvider.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport android.content.Context;\n\n/**\n * Provider interface, base of other interface.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 23:08\n */\npublic interface IProvider {\n\n    /**\n     * Do your init work in this method, it well be call when processor has been load.\n     *\n     * @param context ctx\n     */\n    void init(Context context);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IProviderGroup.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\n\nimport java.util.Map;\n\n/**\n * Template of provider group.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/08/30 12:42\n */\npublic interface IProviderGroup {\n    /**\n     * Load providers map to input\n     *\n     * @param providers input\n     */\n    void loadInto(Map<String, RouteMeta> providers);\n}"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IRouteGroup.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\n\nimport java.util.Map;\n\n/**\n * Group element.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 16:37\n */\npublic interface IRouteGroup {\n    /**\n     * Fill the atlas with routes in group.\n     */\n    void loadInto(Map<String, RouteMeta> atlas);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IRouteRoot.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\nimport java.util.Map;\n\n/**\n * Root element.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 16:36\n */\npublic interface IRouteRoot {\n\n    /**\n     * Load routes to input\n     * @param routes input\n     */\n    void loadInto(Map<String, Class<? extends IRouteGroup>> routes);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/ISyringe.java",
    "content": "package com.alibaba.android.arouter.facade.template;\n\n/**\n * Template of syringe\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/20 下午4:41\n */\npublic interface ISyringe {\n    void inject(Object target);\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/launcher/ARouter.java",
    "content": "package com.alibaba.android.arouter.launcher;\n\nimport android.app.Application;\nimport android.content.Context;\nimport android.net.Uri;\n\nimport com.alibaba.android.arouter.exception.InitException;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.NavigationCallback;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.template.ILogger;\nimport com.alibaba.android.arouter.facade.template.IRouteGroup;\nimport com.alibaba.android.arouter.utils.Consts;\n\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * ARouter facade\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/16 14:36\n */\npublic final class ARouter {\n    // Key of raw uri\n    public static final String RAW_URI = \"NTeRQWvye18AkPd6G\";\n    public static final String AUTO_INJECT = \"wmHzgD4lOj5o4241\";\n\n    private volatile static ARouter instance = null;\n    private volatile static boolean hasInit = false;\n    public static ILogger logger;\n\n    private ARouter() {\n    }\n\n    /**\n     * Init, it must be call before used router.\n     */\n    public static void init(Application application) {\n        if (!hasInit) {\n            logger = _ARouter.logger;\n            _ARouter.logger.info(Consts.TAG, \"ARouter init start.\");\n            hasInit = _ARouter.init(application);\n\n            if (hasInit) {\n                _ARouter.afterInit();\n            }\n\n            _ARouter.logger.info(Consts.TAG, \"ARouter init over.\");\n        }\n    }\n\n    /**\n     * Get instance of router. A\n     * All feature U use, will be starts here.\n     */\n    public static ARouter getInstance() {\n        if (!hasInit) {\n            throw new InitException(\"ARouter::Init::Invoke init(context) first!\");\n        } else {\n            if (instance == null) {\n                synchronized (ARouter.class) {\n                    if (instance == null) {\n                        instance = new ARouter();\n                    }\n                }\n            }\n            return instance;\n        }\n    }\n\n    public static synchronized void openDebug() {\n        _ARouter.openDebug();\n    }\n\n    public static boolean debuggable() {\n        return _ARouter.debuggable();\n    }\n\n    public static synchronized void openLog() {\n        _ARouter.openLog();\n    }\n\n    public static synchronized void printStackTrace() {\n        _ARouter.printStackTrace();\n    }\n\n    public static synchronized void setExecutor(ThreadPoolExecutor tpe) {\n        _ARouter.setExecutor(tpe);\n    }\n\n    public synchronized void destroy() {\n        _ARouter.destroy();\n        hasInit = false;\n    }\n\n    /**\n     * The interface is not stable enough, use 'ARouter.inject();';\n     */\n    @Deprecated\n    public static synchronized void enableAutoInject() {\n        _ARouter.enableAutoInject();\n    }\n\n    @Deprecated\n    public static boolean canAutoInject() {\n        return _ARouter.canAutoInject();\n    }\n\n    /**\n     * The interface is not stable enough, use 'ARouter.inject();';\n     */\n    @Deprecated\n    public static void attachBaseContext() {\n        _ARouter.attachBaseContext();\n    }\n\n    public static synchronized void monitorMode() {\n        _ARouter.monitorMode();\n    }\n\n    public static boolean isMonitorMode() {\n        return _ARouter.isMonitorMode();\n    }\n\n    public static void setLogger(ILogger userLogger) {\n        _ARouter.setLogger(userLogger);\n    }\n\n    /**\n     * Inject params and services.\n     */\n    public void inject(Object thiz) {\n        _ARouter.inject(thiz);\n    }\n\n    /**\n     * Build the roadmap, draw a postcard.\n     *\n     * @param path Where you go.\n     */\n    public Postcard build(String path) {\n        return _ARouter.getInstance().build(path);\n    }\n\n    /**\n     * Build the roadmap, draw a postcard.\n     *\n     * @param path  Where you go.\n     * @param group The group of path.\n     */\n    @Deprecated\n    public Postcard build(String path, String group) {\n        return _ARouter.getInstance().build(path, group, false);\n    }\n\n    /**\n     * Build the roadmap, draw a postcard.\n     *\n     * @param url the path\n     */\n    public Postcard build(Uri url) {\n        return _ARouter.getInstance().build(url);\n    }\n\n    /**\n     * Launch the navigation by type\n     *\n     * @param service interface of service\n     * @param <T>     return type\n     * @return instance of service\n     */\n    public <T> T navigation(Class<? extends T> service) {\n        return _ARouter.getInstance().navigation(service);\n    }\n\n    /**\n     * Launch the navigation.\n     *\n     * @param mContext    .\n     * @param postcard    .\n     * @param requestCode Set for startActivityForResult\n     * @param callback    cb\n     */\n    public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {\n        return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);\n    }\n\n    /**\n     * Add route group dynamic.\n     * @param group route group.\n     * @return add result.\n     */\n    public boolean addRouteGroup(IRouteGroup group) {\n        return _ARouter.getInstance().addRouteGroup(group);\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/launcher/_ARouter.java",
    "content": "package com.alibaba.android.arouter.launcher;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.v4.app.ActivityCompat;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.alibaba.android.arouter.core.InstrumentationHook;\nimport com.alibaba.android.arouter.core.LogisticsCenter;\nimport com.alibaba.android.arouter.exception.HandlerException;\nimport com.alibaba.android.arouter.exception.InitException;\nimport com.alibaba.android.arouter.exception.NoRouteFoundException;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\nimport com.alibaba.android.arouter.facade.callback.NavigationCallback;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.android.arouter.facade.service.*;\nimport com.alibaba.android.arouter.facade.template.ILogger;\nimport com.alibaba.android.arouter.facade.template.IRouteGroup;\nimport com.alibaba.android.arouter.thread.DefaultPoolExecutor;\nimport com.alibaba.android.arouter.utils.Consts;\nimport com.alibaba.android.arouter.utils.DefaultLogger;\nimport com.alibaba.android.arouter.utils.TextUtils;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * ARouter core (Facade patten)\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/16 14:39\n */\nfinal class _ARouter {\n    static ILogger logger = new DefaultLogger(Consts.TAG);\n    private volatile static boolean monitorMode = false;\n    private volatile static boolean debuggable = false;\n    private volatile static boolean autoInject = false;\n    private volatile static _ARouter instance = null;\n    private volatile static boolean hasInit = false;\n    private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();\n    private static Handler mHandler;\n    private static Context mContext;\n\n    private static InterceptorService interceptorService;\n\n    private _ARouter() {\n    }\n\n    protected static synchronized boolean init(Application application) {\n        mContext = application;\n        LogisticsCenter.init(mContext, executor);\n        logger.info(Consts.TAG, \"ARouter init success!\");\n        hasInit = true;\n        mHandler = new Handler(Looper.getMainLooper());\n\n        return true;\n    }\n\n    /**\n     * Destroy arouter, it can be used only in debug mode.\n     */\n    static synchronized void destroy() {\n        if (debuggable()) {\n            hasInit = false;\n            LogisticsCenter.suspend();\n            logger.info(Consts.TAG, \"ARouter destroy success!\");\n        } else {\n            logger.error(Consts.TAG, \"Destroy can be used in debug mode only!\");\n        }\n    }\n\n    protected static _ARouter getInstance() {\n        if (!hasInit) {\n            throw new InitException(\"ARouterCore::Init::Invoke init(context) first!\");\n        } else {\n            if (instance == null) {\n                synchronized (_ARouter.class) {\n                    if (instance == null) {\n                        instance = new _ARouter();\n                    }\n                }\n            }\n            return instance;\n        }\n    }\n\n    static synchronized void openDebug() {\n        debuggable = true;\n        logger.info(Consts.TAG, \"ARouter openDebug\");\n    }\n\n    static synchronized void openLog() {\n        logger.showLog(true);\n        logger.info(Consts.TAG, \"ARouter openLog\");\n    }\n\n    @Deprecated\n    static synchronized void enableAutoInject() {\n        autoInject = true;\n    }\n\n    @Deprecated\n    static boolean canAutoInject() {\n        return autoInject;\n    }\n\n    @Deprecated\n    static void attachBaseContext() {\n        Log.i(Consts.TAG, \"ARouter start attachBaseContext\");\n        try {\n            Class<?> mMainThreadClass = Class.forName(\"android.app.ActivityThread\");\n\n            // Get current main thread.\n            Method getMainThread = mMainThreadClass.getDeclaredMethod(\"currentActivityThread\");\n            getMainThread.setAccessible(true);\n            Object currentActivityThread = getMainThread.invoke(null);\n\n            // The field contain instrumentation.\n            Field mInstrumentationField = mMainThreadClass.getDeclaredField(\"mInstrumentation\");\n            mInstrumentationField.setAccessible(true);\n\n            // Hook current instrumentation\n            mInstrumentationField.set(currentActivityThread, new InstrumentationHook());\n            Log.i(Consts.TAG, \"ARouter hook instrumentation success!\");\n        } catch (Exception ex) {\n            Log.e(Consts.TAG, \"ARouter hook instrumentation failed! [\" + ex.getMessage() + \"]\");\n        }\n    }\n\n    static synchronized void printStackTrace() {\n        logger.showStackTrace(true);\n        logger.info(Consts.TAG, \"ARouter printStackTrace\");\n    }\n\n    static synchronized void setExecutor(ThreadPoolExecutor tpe) {\n        executor = tpe;\n    }\n\n    static synchronized void monitorMode() {\n        monitorMode = true;\n        logger.info(Consts.TAG, \"ARouter monitorMode on\");\n    }\n\n    static boolean isMonitorMode() {\n        return monitorMode;\n    }\n\n    static boolean debuggable() {\n        return debuggable;\n    }\n\n    static void setLogger(ILogger userLogger) {\n        if (null != userLogger) {\n            logger = userLogger;\n        }\n    }\n\n    static void inject(Object thiz) {\n        AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build(\"/arouter/service/autowired\").navigation());\n        if (null != autowiredService) {\n            autowiredService.autowire(thiz);\n        }\n    }\n\n    /**\n     * Build postcard by path and default group\n     */\n    protected Postcard build(String path) {\n        if (TextUtils.isEmpty(path)) {\n            throw new HandlerException(Consts.TAG + \"Parameter is invalid!\");\n        } else {\n            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);\n            if (null != pService) {\n                path = pService.forString(path);\n            }\n            return build(path, extractGroup(path), true);\n        }\n    }\n\n    /**\n     * Build postcard by uri\n     */\n    protected Postcard build(Uri uri) {\n        if (null == uri || TextUtils.isEmpty(uri.toString())) {\n            throw new HandlerException(Consts.TAG + \"Parameter invalid!\");\n        } else {\n            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);\n            if (null != pService) {\n                uri = pService.forUri(uri);\n            }\n            return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);\n        }\n    }\n\n    /**\n     * Build postcard by path and group\n     */\n    protected Postcard build(String path, String group, Boolean afterReplace) {\n        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {\n            throw new HandlerException(Consts.TAG + \"Parameter is invalid!\");\n        } else {\n            if (!afterReplace) {\n                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);\n                if (null != pService) {\n                    path = pService.forString(path);\n                }\n            }\n            return new Postcard(path, group);\n        }\n    }\n\n    /**\n     * Extract the default group from path.\n     */\n    private String extractGroup(String path) {\n        if (TextUtils.isEmpty(path) || !path.startsWith(\"/\")) {\n            throw new HandlerException(Consts.TAG + \"Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!\");\n        }\n\n        try {\n            String defaultGroup = path.substring(1, path.indexOf(\"/\", 1));\n            if (TextUtils.isEmpty(defaultGroup)) {\n                throw new HandlerException(Consts.TAG + \"Extract the default group failed! There's nothing between 2 '/'!\");\n            } else {\n                return defaultGroup;\n            }\n        } catch (Exception e) {\n            logger.warning(Consts.TAG, \"Failed to extract default group! \" + e.getMessage());\n            return null;\n        }\n    }\n\n    static void afterInit() {\n        // Trigger interceptor init, use byName.\n        interceptorService = (InterceptorService) ARouter.getInstance().build(\"/arouter/service/interceptor\").navigation();\n    }\n\n    protected <T> T navigation(Class<? extends T> service) {\n        try {\n            Postcard postcard = LogisticsCenter.buildProvider(service.getName());\n\n            // Compatible 1.0.5 compiler sdk.\n            // Earlier versions did not use the fully qualified name to get the service\n            if (null == postcard) {\n                // No service, or this service in old version.\n                postcard = LogisticsCenter.buildProvider(service.getSimpleName());\n            }\n\n            if (null == postcard) {\n                return null;\n            }\n\n            // Set application to postcard.\n            postcard.setContext(mContext);\n\n            LogisticsCenter.completion(postcard);\n            return (T) postcard.getProvider();\n        } catch (NoRouteFoundException ex) {\n            logger.warning(Consts.TAG, ex.getMessage());\n            return null;\n        }\n    }\n\n    /**\n     * Use router navigation.\n     *\n     * @param context     Activity or null.\n     * @param postcard    Route metas\n     * @param requestCode RequestCode\n     * @param callback    cb\n     */\n    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {\n        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);\n        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {\n            // Pretreatment failed, navigation canceled.\n            return null;\n        }\n\n        // Set context to postcard.\n        postcard.setContext(null == context ? mContext : context);\n\n        try {\n            LogisticsCenter.completion(postcard);\n        } catch (NoRouteFoundException ex) {\n            logger.warning(Consts.TAG, ex.getMessage());\n\n            if (debuggable()) {\n                // Show friendly tips for user.\n                runInMainThread(new Runnable() {\n                    @Override\n                    public void run() {\n                        Toast.makeText(mContext, \"There's no route matched!\\n\" +\n                                \" Path = [\" + postcard.getPath() + \"]\\n\" +\n                                \" Group = [\" + postcard.getGroup() + \"]\", Toast.LENGTH_LONG).show();\n                    }\n                });\n            }\n\n            if (null != callback) {\n                callback.onLost(postcard);\n            } else {\n                // No callback for this invoke, then we use the global degrade service.\n                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);\n                if (null != degradeService) {\n                    degradeService.onLost(context, postcard);\n                }\n            }\n\n            return null;\n        }\n\n        if (null != callback) {\n            callback.onFound(postcard);\n        }\n\n        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.\n            interceptorService.doInterceptions(postcard, new InterceptorCallback() {\n                /**\n                 * Continue process\n                 *\n                 * @param postcard route meta\n                 */\n                @Override\n                public void onContinue(Postcard postcard) {\n                    _navigation(postcard, requestCode, callback);\n                }\n\n                /**\n                 * Interrupt process, pipeline will be destory when this method called.\n                 *\n                 * @param exception Reson of interrupt.\n                 */\n                @Override\n                public void onInterrupt(Throwable exception) {\n                    if (null != callback) {\n                        callback.onInterrupt(postcard);\n                    }\n\n                    logger.info(Consts.TAG, \"Navigation failed, termination by interceptor : \" + exception.getMessage());\n                }\n            });\n        } else {\n            return _navigation(postcard, requestCode, callback);\n        }\n\n        return null;\n    }\n\n    private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {\n        final Context currentContext = postcard.getContext();\n\n        switch (postcard.getType()) {\n            case ACTIVITY:\n                // Build intent\n                final Intent intent = new Intent(currentContext, postcard.getDestination());\n                intent.putExtras(postcard.getExtras());\n\n                // Set flags.\n                int flags = postcard.getFlags();\n                if (0 != flags) {\n                    intent.setFlags(flags);\n                }\n\n                // Non activity, need FLAG_ACTIVITY_NEW_TASK\n                if (!(currentContext instanceof Activity)) {\n                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                }\n\n                // Set Actions\n                String action = postcard.getAction();\n                if (!TextUtils.isEmpty(action)) {\n                    intent.setAction(action);\n                }\n\n                // Navigation in main looper.\n                runInMainThread(new Runnable() {\n                    @Override\n                    public void run() {\n                        startActivity(requestCode, currentContext, intent, postcard, callback);\n                    }\n                });\n\n                break;\n            case PROVIDER:\n                return postcard.getProvider();\n            case BOARDCAST:\n            case CONTENT_PROVIDER:\n            case FRAGMENT:\n                Class<?> fragmentMeta = postcard.getDestination();\n                try {\n                    Object instance = fragmentMeta.getConstructor().newInstance();\n                    if (instance instanceof Fragment) {\n                        ((Fragment) instance).setArguments(postcard.getExtras());\n                    } else if (instance instanceof android.support.v4.app.Fragment) {\n                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());\n                    }\n\n                    return instance;\n                } catch (Exception ex) {\n                    logger.error(Consts.TAG, \"Fetch fragment instance error, \" + TextUtils.formatStackTrace(ex.getStackTrace()));\n                }\n            case METHOD:\n            case SERVICE:\n            default:\n                return null;\n        }\n\n        return null;\n    }\n\n    /**\n     * Be sure execute in main thread.\n     *\n     * @param runnable code\n     */\n    private void runInMainThread(Runnable runnable) {\n        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {\n            mHandler.post(runnable);\n        } else {\n            runnable.run();\n        }\n    }\n\n    /**\n     * Start activity\n     *\n     * @see ActivityCompat\n     */\n    private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {\n        if (requestCode >= 0) {  // Need start for result\n            if (currentContext instanceof Activity) {\n                ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());\n            } else {\n                logger.warning(Consts.TAG, \"Must use [navigation(activity, ...)] to support [startActivityForResult]\");\n            }\n        } else {\n            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());\n        }\n\n        if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.\n            ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());\n        }\n\n        if (null != callback) { // Navigation over.\n            callback.onArrival(postcard);\n        }\n    }\n\n    boolean addRouteGroup(IRouteGroup group) {\n        if (null == group) {\n            return false;\n        }\n\n        String groupName = null;\n\n        try {\n            // Extract route meta.\n            Map<String, RouteMeta> dynamicRoute = new HashMap<>();\n            group.loadInto(dynamicRoute);\n\n            // Check route meta.\n            for (Map.Entry<String, RouteMeta> route : dynamicRoute.entrySet()) {\n                String path = route.getKey();\n                String groupByExtract = extractGroup(path);\n                RouteMeta meta = route.getValue();\n\n                if (null == groupName) {\n                    groupName = groupByExtract;\n                }\n\n                if (null == groupName || !groupName.equals(groupByExtract) || !groupName.equals(meta.getGroup())) {\n                    // Group name not consistent\n                    return false;\n                }\n            }\n\n            LogisticsCenter.addRouteGroupDynamic(groupName, group);\n\n            logger.info(Consts.TAG, \"Add route group [\" + groupName + \"] finish, \" + dynamicRoute.size() + \" new route meta.\");\n\n            return true;\n        } catch (Exception exception) {\n            logger.error(Consts.TAG, \"Add route group dynamic exception!\", exception);\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/thread/CancelableCountDownLatch.java",
    "content": "package com.alibaba.android.arouter.thread;\n\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * As its name.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/29 15:48\n */\npublic class CancelableCountDownLatch extends CountDownLatch {\n    /**\n     * Constructs a {@code CountDownLatch} initialized with the given count.\n     *\n     * @param count the number of times {@link #countDown} must be invoked\n     *              before threads can pass through {@link #await}\n     * @throws IllegalArgumentException if {@code count} is negative\n     */\n    public CancelableCountDownLatch(int count) {\n        super(count);\n    }\n\n    public void cancel() {\n        while (getCount() > 0) {\n            countDown();\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/thread/DefaultPoolExecutor.java",
    "content": "package com.alibaba.android.arouter.thread;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.alibaba.android.arouter.utils.Consts;\nimport com.alibaba.android.arouter.utils.TextUtils;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Executors\n *\n * @author 正纬 <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/4/28 下午4:07\n */\npublic class DefaultPoolExecutor extends ThreadPoolExecutor {\n    //    Thread args\n    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();\n    private static final int INIT_THREAD_COUNT = CPU_COUNT + 1;\n    private static final int MAX_THREAD_COUNT = INIT_THREAD_COUNT;\n    private static final long SURPLUS_THREAD_LIFE = 30L;\n\n    private static volatile DefaultPoolExecutor instance;\n\n    public static DefaultPoolExecutor getInstance() {\n        if (null == instance) {\n            synchronized (DefaultPoolExecutor.class) {\n                if (null == instance) {\n                    instance = new DefaultPoolExecutor(\n                            INIT_THREAD_COUNT,\n                            MAX_THREAD_COUNT,\n                            SURPLUS_THREAD_LIFE,\n                            TimeUnit.SECONDS,\n                            new ArrayBlockingQueue<Runnable>(64),\n                            new DefaultThreadFactory());\n                }\n            }\n        }\n        return instance;\n    }\n\n    private DefaultPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {\n        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectedExecutionHandler() {\n            @Override\n            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {\n                ARouter.logger.error(Consts.TAG, \"Task rejected, too many task!\");\n            }\n        });\n    }\n\n    /*\n     *  线程执行结束，顺便看一下有么有什么乱七八糟的异常\n     *\n     * @param r the runnable that has completed\n     * @param t the exception that caused termination, or null if\n     */\n    @Override\n    protected void afterExecute(Runnable r, Throwable t) {\n        super.afterExecute(r, t);\n        if (t == null && r instanceof Future<?>) {\n            try {\n                ((Future<?>) r).get();\n            } catch (CancellationException ce) {\n                t = ce;\n            } catch (ExecutionException ee) {\n                t = ee.getCause();\n            } catch (InterruptedException ie) {\n                Thread.currentThread().interrupt(); // ignore/reset\n            }\n        }\n        if (t != null) {\n            ARouter.logger.warning(Consts.TAG, \"Running task appeared exception! Thread [\" + Thread.currentThread().getName() + \"], because [\" + t.getMessage() + \"]\\n\" + TextUtils.formatStackTrace(t.getStackTrace()));\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/thread/DefaultThreadFactory.java",
    "content": "package com.alibaba.android.arouter.thread;\n\nimport android.support.annotation.NonNull;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.alibaba.android.arouter.utils.Consts;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * 线程池工厂类\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 15/12/25 上午10:51\n */\npublic class DefaultThreadFactory implements ThreadFactory {\n    private static final AtomicInteger poolNumber = new AtomicInteger(1);\n\n    private final AtomicInteger threadNumber = new AtomicInteger(1);\n    private final ThreadGroup group;\n    private final String namePrefix;\n\n    public DefaultThreadFactory() {\n        SecurityManager s = System.getSecurityManager();\n        group = (s != null) ? s.getThreadGroup() :\n                Thread.currentThread().getThreadGroup();\n        namePrefix = \"ARouter task pool No.\" + poolNumber.getAndIncrement() + \", thread No.\";\n    }\n\n    public Thread newThread(@NonNull Runnable runnable) {\n        String threadName = namePrefix + threadNumber.getAndIncrement();\n        ARouter.logger.info(Consts.TAG, \"Thread production, name is [\" + threadName + \"]\");\n        Thread thread = new Thread(group, runnable, threadName, 0);\n        if (thread.isDaemon()) {   //设为非后台线程\n            thread.setDaemon(false);\n        }\n        if (thread.getPriority() != Thread.NORM_PRIORITY) { //优先级为normal\n            thread.setPriority(Thread.NORM_PRIORITY);\n        }\n\n        // 捕获多线程处理中的异常\n        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {\n            @Override\n            public void uncaughtException(Thread thread, Throwable ex) {\n                ARouter.logger.info(Consts.TAG, \"Running task appeared exception! Thread [\" + thread.getName() + \"], because [\" + ex.getMessage() + \"]\");\n            }\n        });\n        return thread;\n    }\n}"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/ClassUtils.java",
    "content": "package com.alibaba.android.arouter.utils;\n\n// Copy from galaxy sdk ${com.alibaba.android.galaxy.utils.ClassUtils}\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\nimport android.util.Log;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\nimport com.alibaba.android.arouter.thread.DefaultPoolExecutor;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport dalvik.system.DexFile;\n\n/**\n * Scanner, find out class with any conditions, copy from google source code.\n *\n * @author 正纬 <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/6/27 下午10:58\n */\npublic class ClassUtils {\n    private static final String EXTRACTED_NAME_EXT = \".classes\";\n    private static final String EXTRACTED_SUFFIX = \".zip\";\n\n    private static final String SECONDARY_FOLDER_NAME = \"code_cache\" + File.separator + \"secondary-dexes\";\n\n    private static final String PREFS_FILE = \"multidex.version\";\n    private static final String KEY_DEX_NUMBER = \"dex.number\";\n\n    private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;\n    private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;\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     * @param context     U know\n     * @param packageName 包名\n     * @return 所有class的集合\n     */\n    public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {\n        final Set<String> classNames = new HashSet<>();\n\n        List<String> paths = getSourcePaths(context);\n        final CountDownLatch parserCtl = new CountDownLatch(paths.size());\n\n        for (final String path : paths) {\n            DefaultPoolExecutor.getInstance().execute(new Runnable() {\n                @Override\n                public void run() {\n                    DexFile dexfile = null;\n\n                    try {\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\n                        Enumeration<String> dexEntries = dexfile.entries();\n                        while (dexEntries.hasMoreElements()) {\n                            String className = dexEntries.nextElement();\n                            if (className.startsWith(packageName)) {\n                                classNames.add(className);\n                            }\n                        }\n                    } catch (Throwable ignore) {\n                        Log.e(\"ARouter\", \"Scan map file in dex files made error.\", ignore);\n                    } finally {\n                        if (null != dexfile) {\n                            try {\n                                dexfile.close();\n                            } catch (Throwable ignore) {\n                            }\n                        }\n\n                        parserCtl.countDown();\n                    }\n                }\n            });\n        }\n\n        parserCtl.await();\n\n        Log.d(Consts.TAG, \"Filter \" + 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//        如果VM已经支持了MultiDex，就不要去Secondary Folder加载 Classesx.zip了，那里已经么有了\n//        通过是否存在sp中的multidex.version是不准确的，因为从低版本升级上来的用户，是包含这个sp配置的\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        if (ARouter.debuggable()) { // Search instant run support only debuggable\n            sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));\n        }\n        return sourcePaths;\n    }\n\n    /**\n     * Get instant run dex path, used to catch the branch usingApkSplits=false.\n     */\n    private static List<String> tryLoadInstantRunDexFile(ApplicationInfo applicationInfo) {\n        List<String> instantRunSourcePaths = new ArrayList<>();\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && null != applicationInfo.splitSourceDirs) {\n            // add the split apk, normally for InstantRun, and newest version.\n            instantRunSourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));\n            Log.d(Consts.TAG, \"Found InstantRun support\");\n        } else {\n            try {\n                // This man is reflection from Google instant run sdk, he will tell me where the dex files go.\n                Class pathsByInstantRun = Class.forName(\"com.android.tools.fd.runtime.Paths\");\n                Method getDexFileDirectory = pathsByInstantRun.getMethod(\"getDexFileDirectory\", String.class);\n                String instantRunDexPath = (String) getDexFileDirectory.invoke(null, applicationInfo.packageName);\n\n                File instantRunFilePath = new File(instantRunDexPath);\n                if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {\n                    File[] dexFile = instantRunFilePath.listFiles();\n                    for (File file : dexFile) {\n                        if (null != file && file.exists() && file.isFile() && file.getName().endsWith(\".dex\")) {\n                            instantRunSourcePaths.add(file.getAbsolutePath());\n                        }\n                    }\n                    Log.d(Consts.TAG, \"Found InstantRun support\");\n                }\n\n            } catch (Exception e) {\n                Log.e(Consts.TAG, \"InstantRun support error, \" + e.getMessage());\n            }\n        }\n\n        return instantRunSourcePaths;\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()) {    // YunOS需要特殊判断\n                vmName = \"'YunOS'\";\n                isMultidexCapable = Integer.valueOf(System.getProperty(\"ro.build.version.sdk\")) >= 21;\n            } else {    // 非YunOS原生Android\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\n        Log.i(Consts.TAG, \"VM with name \" + vmName + (isMultidexCapable ? \" has multidex support\" : \" does not have multidex support\"));\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}"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/Consts.java",
    "content": "package com.alibaba.android.arouter.utils;\n\n/**\n * ARouter constants.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 9:38\n */\npublic final class Consts {\n    public static final String SDK_NAME = \"ARouter\";\n    public static final String TAG = SDK_NAME + \"::\";\n    public static final String SEPARATOR = \"$$\";\n    public static final String SUFFIX_ROOT = \"Root\";\n    public static final String SUFFIX_INTERCEPTORS = \"Interceptors\";\n    public static final String SUFFIX_PROVIDERS = \"Providers\";\n    public static final String SUFFIX_AUTOWIRED = SEPARATOR + SDK_NAME + SEPARATOR + \"Autowired\";\n    public static final String DOT = \".\";\n    public static final String ROUTE_ROOT_PAKCAGE = \"com.alibaba.android.arouter.routes\";\n\n    public static final String AROUTER_SP_CACHE_KEY = \"SP_AROUTER_CACHE\";\n    public static final String AROUTER_SP_KEY_MAP = \"ROUTER_MAP\";\n\n    public static final String LAST_VERSION_NAME = \"LAST_VERSION_NAME\";\n    public static final String LAST_VERSION_CODE = \"LAST_VERSION_CODE\";\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/DefaultLogger.java",
    "content": "package com.alibaba.android.arouter.utils;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.alibaba.android.arouter.facade.template.ILogger;\n\n/**\n * Default logger\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2015-12-08 21:44:10\n */\npublic class DefaultLogger implements ILogger {\n\n    private static boolean isShowLog = false;\n    private static boolean isShowStackTrace = false;\n    private static boolean isMonitorMode = false;\n\n    private String defaultTag = \"ARouter\";\n\n    public void showLog(boolean showLog) {\n        isShowLog = showLog;\n    }\n\n    public void showStackTrace(boolean showStackTrace) {\n        isShowStackTrace = showStackTrace;\n    }\n\n    public void showMonitor(boolean showMonitor) {\n        isMonitorMode = showMonitor;\n    }\n\n    public DefaultLogger() {\n    }\n\n    public DefaultLogger(String defaultTag) {\n        this.defaultTag = defaultTag;\n    }\n\n    @Override\n    public void debug(String tag, String message) {\n        if (isShowLog) {\n            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];\n            Log.d(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));\n        }\n    }\n\n    @Override\n    public void info(String tag, String message) {\n        if (isShowLog) {\n            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];\n            Log.i(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));\n        }\n    }\n\n    @Override\n    public void warning(String tag, String message) {\n        if (isShowLog) {\n            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];\n            Log.w(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));\n        }\n    }\n\n    @Override\n    public void error(String tag, String message) {\n        if (isShowLog) {\n            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];\n            Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));\n        }\n    }\n\n    @Override\n    public void error(String tag, String message, Throwable e) {\n        if (isShowLog) {\n            Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message, e);\n        }\n    }\n\n\n    @Override\n    public void monitor(String message) {\n        if (isShowLog && isMonitorMode()) {\n            StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];\n            Log.d(defaultTag + \"::monitor\", message + getExtInfo(stackTraceElement));\n        }\n    }\n\n    @Override\n    public boolean isMonitorMode() {\n        return isMonitorMode;\n    }\n\n    @Override\n    public String getDefaultTag() {\n        return defaultTag;\n    }\n\n    public static String getExtInfo(StackTraceElement stackTraceElement) {\n        if (isShowStackTrace) {\n            String separator = \" & \";\n            StringBuilder sb = new StringBuilder(\"[\");\n\n            String threadName = Thread.currentThread().getName();\n            String fileName = stackTraceElement.getFileName();\n            String className = stackTraceElement.getClassName();\n            String methodName = stackTraceElement.getMethodName();\n            long threadID = Thread.currentThread().getId();\n            int lineNumber = stackTraceElement.getLineNumber();\n\n            sb.append(\"ThreadId=\").append(threadID).append(separator);\n            sb.append(\"ThreadName=\").append(threadName).append(separator);\n            sb.append(\"FileName=\").append(fileName).append(separator);\n            sb.append(\"ClassName=\").append(className).append(separator);\n            sb.append(\"MethodName=\").append(methodName).append(separator);\n            sb.append(\"LineNumber=\").append(lineNumber);\n\n            sb.append(\" ] \");\n            return sb.toString();\n        }\n\n        return \"\";\n    }\n}"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/MapUtils.java",
    "content": "package com.alibaba.android.arouter.utils;\n\nimport java.util.Map;\n\n/**\n * Copy from apache common.\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/3/10 下午3:26\n */\npublic class MapUtils {\n    /**\n     * Null-safe check if the specified map is not empty.\n     * <p>\n     * Null returns false.\n     *\n     * @param map  the map to check, may be null\n     * @return true if non-null and non-empty\n     * @since 3.2\n     */\n    public static boolean isNotEmpty(final Map<?,?> map) {\n        return !isEmpty(map);\n    }\n\n    /**\n     * Null-safe check if the specified map is empty.\n     * <p>\n     * Null returns true.\n     *\n     * @param map  the map to check, may be null\n     * @return true if empty or null\n     * @since 3.2\n     */\n    public static boolean isEmpty(final Map<?,?> map) {\n        return map == null || map.isEmpty();\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/PackageUtils.java",
    "content": "package com.alibaba.android.arouter.utils;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\n\nimport static com.alibaba.android.arouter.launcher.ARouter.logger;\nimport static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_CACHE_KEY;\nimport static com.alibaba.android.arouter.utils.Consts.LAST_VERSION_CODE;\nimport static com.alibaba.android.arouter.utils.Consts.LAST_VERSION_NAME;\n\n/**\n * Android package utils\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/8/8 下午8:19\n */\npublic class PackageUtils {\n    private static String NEW_VERSION_NAME;\n    private static int NEW_VERSION_CODE;\n\n    public static boolean isNewVersion(Context context) {\n        PackageInfo packageInfo = getPackageInfo(context);\n        if (null != packageInfo) {\n            String versionName = packageInfo.versionName;\n            int versionCode = packageInfo.versionCode;\n\n            SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);\n            if (!versionName.equals(sp.getString(LAST_VERSION_NAME, null)) || versionCode != sp.getInt(LAST_VERSION_CODE, -1)) {\n                // new version\n                NEW_VERSION_NAME = versionName;\n                NEW_VERSION_CODE = versionCode;\n\n                return true;\n            } else {\n                return false;\n            }\n        } else {\n            return true;\n        }\n    }\n\n    public static void updateVersion(Context context) {\n        if (!android.text.TextUtils.isEmpty(NEW_VERSION_NAME) && NEW_VERSION_CODE != 0) {\n            SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);\n            sp.edit().putString(LAST_VERSION_NAME, NEW_VERSION_NAME).putInt(LAST_VERSION_CODE, NEW_VERSION_CODE).apply();\n        }\n    }\n\n    private static PackageInfo getPackageInfo(Context context) {\n        PackageInfo packageInfo = null;\n        try {\n            packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS);\n        } catch (Exception ex) {\n            logger.error(Consts.TAG, \"Get package info error.\");\n        }\n\n        return packageInfo;\n    }\n}\n"
  },
  {
    "path": "arouter-api/src/main/java/com/alibaba/android/arouter/utils/TextUtils.java",
    "content": "package com.alibaba.android.arouter.utils;\n\nimport android.net.Uri;\n\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Text utils\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/9/9 14:40\n */\npublic class TextUtils {\n\n    public static boolean isEmpty(final CharSequence cs) {\n        return cs == null || cs.length() == 0;\n    }\n\n    /**\n     * Print thread stack\n     */\n    public static String formatStackTrace(StackTraceElement[] stackTrace) {\n        StringBuilder sb = new StringBuilder();\n        for (StackTraceElement element : stackTrace) {\n            sb.append(\"    at \").append(element.toString());\n            sb.append(\"\\n\");\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Split query parameters\n     * @param rawUri raw uri\n     * @return map with params\n     */\n    public static Map<String, String> splitQueryParameters(Uri rawUri) {\n        String query = rawUri.getEncodedQuery();\n\n        if (query == null) {\n            return Collections.emptyMap();\n        }\n\n        Map<String, String> paramMap = new LinkedHashMap<>();\n        int start = 0;\n        do {\n            int next = query.indexOf('&', start);\n            int end = (next == -1) ? query.length() : next;\n\n            int separator = query.indexOf('=', start);\n            if (separator > end || separator == -1) {\n                separator = end;\n            }\n\n            String name = query.substring(start, separator);\n\n            if (!android.text.TextUtils.isEmpty(name)) {\n                String value = (separator == end ? \"\" : query.substring(separator + 1, end));\n                paramMap.put(Uri.decode(name), Uri.decode(value));\n            }\n\n            // Move start to end of name.\n            start = end + 1;\n        } while (start < query.length());\n\n        return Collections.unmodifiableMap(paramMap);\n    }\n\n    /**\n     * Split key with |\n     *\n     * @param key raw key\n     * @return left key\n     */\n    public static String getLeft(String key) {\n        if (key.contains(\"|\") && !key.endsWith(\"|\")) {\n            return key.substring(0, key.indexOf(\"|\"));\n        } else {\n            return key;\n        }\n    }\n\n    /**\n     * Split key with |\n     *\n     * @param key raw key\n     * @return right key\n     */\n    public static String getRight(String key) {\n        if (key.contains(\"|\") && !key.startsWith(\"|\")) {\n            return key.substring(key.indexOf(\"|\") + 1);\n        } else {\n            return key;\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/build.gradle",
    "content": "apply plugin: 'java'\n\ncompileJava {\n    sourceCompatibility = '1.8'\n    targetCompatibility = '1.8'\n}\n\ndependencies {\n    implementation 'com.alibaba:arouter-annotation:1.0.6'\n\n    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'\n    compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'\n\n    implementation 'com.squareup:javapoet:1.8.0'\n\n    implementation 'org.apache.commons:commons-lang3:3.5'\n    implementation 'org.apache.commons:commons-collections4:4.1'\n\n    implementation 'com.alibaba:fastjson:1.2.69'\n}\n\napply from: rootProject.file('gradle/publish.gradle')"
  },
  {
    "path": "arouter-compiler/gradle.properties",
    "content": "POM_NAME=ARouter Compiler\nPOM_ARTIFACT_ID=arouter-compiler\nPOM_PACKAGING=jar\nPOM_DESCRIPTION=Compiler for ARouter to find route\nVERSION_NAME=1.5.2"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/entity/RouteDoc.java",
    "content": "package com.alibaba.android.arouter.compiler.entity;\n\nimport com.alibaba.fastjson.annotation.JSONField;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.List;\n\n/**\n * Description route info, used for generate router map\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2018/8/9 11:59 AM\n */\npublic class RouteDoc {\n    @JSONField(ordinal = 1)\n    private String group;\n    @JSONField(ordinal = 2)\n    private String path;\n    @JSONField(ordinal = 3)\n    private String description;\n    @JSONField(ordinal = 4)\n    private String prototype;\n    @JSONField(ordinal = 5)\n    private String className;\n    @JSONField(ordinal = 6)\n    private String type;\n    @JSONField(ordinal = 7)\n    private int mark;\n    @JSONField(ordinal = 8)\n    private List<Param> params;\n\n    public String getGroup() {\n        return group;\n    }\n\n    public void setGroup(String group) {\n        this.group = group;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    public String getPrototype() {\n        return prototype;\n    }\n\n    public void setPrototype(String prototype) {\n        this.prototype = prototype;\n    }\n\n    public void addPrototype(String prototype) {\n        if (StringUtils.isNotEmpty(getPrototype())) {\n            setPrototype(prototype);\n        } else {\n            setPrototype(getPrototype() + \", \" + prototype);\n        }\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public void setClassName(String className) {\n        this.className = className;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        if (StringUtils.isNotEmpty(description)) {\n            this.description = description;\n        }\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public int getMark() {\n        return mark;\n    }\n\n    public void setMark(int mark) {\n        this.mark = mark;\n    }\n\n    public List<Param> getParams() {\n        return params;\n    }\n\n    public void setParams(List<Param> params) {\n        this.params = params;\n    }\n\n    public static class Param {\n        @JSONField(ordinal = 1)\n        private String key;\n        @JSONField(ordinal = 2)\n        private String type;\n        @JSONField(ordinal = 3)\n        private String description;\n        @JSONField(ordinal = 4)\n        private boolean required;\n\n        public String getKey() {\n            return key;\n        }\n\n        public void setKey(String key) {\n            this.key = key;\n        }\n\n        public String getType() {\n            return type;\n        }\n\n        public void setType(String type) {\n            this.type = type;\n        }\n\n        public String getDescription() {\n            return description;\n        }\n\n        public void setDescription(String description) {\n            if (StringUtils.isNotEmpty(description)) {\n                this.description = description;\n            }\n        }\n\n        public boolean isRequired() {\n            return required;\n        }\n\n        public void setRequired(boolean required) {\n            this.required = required;\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/AutowiredProcessor.java",
    "content": "package com.alibaba.android.arouter.compiler.processor;\n\nimport com.alibaba.android.arouter.compiler.utils.Consts;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.enums.TypeKind;\nimport com.google.auto.service.AutoService;\nimport com.squareup.javapoet.ClassName;\nimport com.squareup.javapoet.CodeBlock;\nimport com.squareup.javapoet.FieldSpec;\nimport com.squareup.javapoet.JavaFile;\nimport com.squareup.javapoet.MethodSpec;\nimport com.squareup.javapoet.ParameterSpec;\nimport com.squareup.javapoet.TypeName;\nimport com.squareup.javapoet.TypeSpec;\n\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.annotation.processing.SupportedAnnotationTypes;\nimport javax.annotation.processing.SupportedOptions;\nimport javax.annotation.processing.SupportedSourceVersion;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.AnnotationMirror;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.Modifier;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.TypeMirror;\n\nimport static com.alibaba.android.arouter.compiler.utils.Consts.*;\nimport static javax.lang.model.element.Modifier.PUBLIC;\n\n/**\n * Processor used to create autowired helper\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/20 下午5:56\n */\n@AutoService(Processor.class)\n@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})\npublic class AutowiredProcessor extends BaseProcessor {\n    private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>();   // Contain field need autowired and his super class.\n    private static final ClassName ARouterClass = ClassName.get(\"com.alibaba.android.arouter.launcher\", \"ARouter\");\n    private static final ClassName AndroidLog = ClassName.get(\"android.util\", \"Log\");\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnvironment) {\n        super.init(processingEnvironment);\n\n        logger.info(\">>> AutowiredProcessor init. <<<\");\n    }\n\n    @Override\n    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {\n        if (CollectionUtils.isNotEmpty(set)) {\n            try {\n                logger.info(\">>> Found autowired field, start... <<<\");\n                categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));\n                generateHelper();\n\n            } catch (Exception e) {\n                logger.error(e);\n            }\n            return true;\n        }\n\n        return false;\n    }\n\n    private void generateHelper() throws IOException, IllegalAccessException {\n        TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);\n        TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);\n        TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();\n        TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();\n        TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();\n        TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();\n\n        // Build input param name.\n        ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, \"target\").build();\n\n        if (MapUtils.isNotEmpty(parentAndChild)) {\n            for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {\n                // Build method : 'inject'\n                MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)\n                        .addAnnotation(Override.class)\n                        .addModifiers(PUBLIC)\n                        .addParameter(objectParamSpec);\n\n                TypeElement parent = entry.getKey();\n                List<Element> childs = entry.getValue();\n\n                String qualifiedName = parent.getQualifiedName().toString();\n                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf(\".\"));\n                String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;\n\n                logger.info(\">>> Start process \" + childs.size() + \" field in \" + parent.getSimpleName() + \" ... <<<\");\n\n                TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)\n                        .addJavadoc(WARNING_TIPS)\n                        .addSuperinterface(ClassName.get(type_ISyringe))\n                        .addModifiers(PUBLIC);\n\n                FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), \"serializationService\", Modifier.PRIVATE).build();\n                helper.addField(jsonServiceField);\n\n                injectMethodBuilder.addStatement(\"serializationService = $T.getInstance().navigation($T.class)\", ARouterClass, ClassName.get(type_JsonService));\n                injectMethodBuilder.addStatement(\"$T substitute = ($T)target\", ClassName.get(parent), ClassName.get(parent));\n\n                // Generate method body, start inject.\n                for (Element element : childs) {\n                    Autowired fieldConfig = element.getAnnotation(Autowired.class);\n                    String fieldName = element.getSimpleName().toString();\n                    if (types.isSubtype(element.asType(), iProvider)) {  // It's provider\n                        if (\"\".equals(fieldConfig.name())) {    // User has not set service path, then use byType.\n\n                            // Getter\n                            injectMethodBuilder.addStatement(\n                                    \"substitute.\" + fieldName + \" = $T.getInstance().navigation($T.class)\",\n                                    ARouterClass,\n                                    ClassName.get(element.asType())\n                            );\n                        } else {    // use byName\n                            // Getter\n                            injectMethodBuilder.addStatement(\n                                    \"substitute.\" + fieldName + \" = ($T)$T.getInstance().build($S).navigation()\",\n                                    ClassName.get(element.asType()),\n                                    ARouterClass,\n                                    fieldConfig.name()\n                            );\n                        }\n\n                        // Validator\n                        if (fieldConfig.required()) {\n                            injectMethodBuilder.beginControlFlow(\"if (substitute.\" + fieldName + \" == null)\");\n                            injectMethodBuilder.addStatement(\n                                    \"throw new RuntimeException(\\\"The field '\" + fieldName + \"' is null, in class '\\\" + $T.class.getName() + \\\"!\\\")\", ClassName.get(parent));\n                            injectMethodBuilder.endControlFlow();\n                        }\n                    } else {    // It's normal intent value\n                        String originalValue = \"substitute.\" + fieldName;\n                        String statement = \"substitute.\" + fieldName + \" = \" + buildCastCode(element) + \"substitute.\";\n                        boolean isActivity = false;\n                        if (types.isSubtype(parent.asType(), activityTm)) {  // Activity, then use getIntent()\n                            isActivity = true;\n                            statement += \"getIntent().\";\n                        } else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {   // Fragment, then use getArguments()\n                            statement += \"getArguments().\";\n                        } else {\n                            throw new IllegalAccessException(\"The field [\" + fieldName + \"] need autowired from intent, its parent must be activity or fragment!\");\n                        }\n\n                        statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));\n                        if (statement.startsWith(\"serializationService.\")) {   // Not mortals\n                            injectMethodBuilder.beginControlFlow(\"if (null != serializationService)\");\n                            injectMethodBuilder.addStatement(\n                                    \"substitute.\" + fieldName + \" = \" + statement,\n                                    (StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),\n                                    ClassName.get(element.asType())\n                            );\n                            injectMethodBuilder.nextControlFlow(\"else\");\n                            injectMethodBuilder.addStatement(\n                                    \"$T.e(\\\"\" + Consts.TAG + \"\\\", \\\"You want automatic inject the field '\" + fieldName + \"' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\\\")\", AndroidLog, ClassName.get(parent));\n                            injectMethodBuilder.endControlFlow();\n                        } else {\n                            injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());\n                        }\n\n                        // Validator\n                        if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) {  // Primitive wont be check.\n                            injectMethodBuilder.beginControlFlow(\"if (null == substitute.\" + fieldName + \")\");\n                            injectMethodBuilder.addStatement(\n                                    \"$T.e(\\\"\" + Consts.TAG + \"\\\", \\\"The field '\" + fieldName + \"' is null, in class '\\\" + $T.class.getName() + \\\"!\\\")\", AndroidLog, ClassName.get(parent));\n                            injectMethodBuilder.endControlFlow();\n                        }\n                    }\n                }\n\n                helper.addMethod(injectMethodBuilder.build());\n\n                // Generate autowire helper\n                JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);\n\n                logger.info(\">>> \" + parent.getSimpleName() + \" has been processed, \" + fileName + \" has been generated. <<<\");\n            }\n\n            logger.info(\">>> Autowired processor stop. <<<\");\n        }\n    }\n\n    private boolean isKtClass(Element element) {\n        for (AnnotationMirror annotationMirror : elementUtils.getAllAnnotationMirrors(element)) {\n            if (annotationMirror.getAnnotationType().toString().contains(\"kotlin\")) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    private String buildCastCode(Element element) {\n        if (typeUtils.typeExchange(element) == TypeKind.SERIALIZABLE.ordinal()) {\n            return CodeBlock.builder().add(\"($T) \", ClassName.get(element.asType())).build().toString();\n        }\n        return \"\";\n    }\n\n    /**\n     * Build param inject statement\n     */\n    private String buildStatement(String originalValue, String statement, int type, boolean isActivity, boolean isKt) {\n        switch (TypeKind.values()[type]) {\n            case BOOLEAN:\n                statement += \"getBoolean\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case BYTE:\n                statement += \"getByte\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case SHORT:\n                statement += \"getShort\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case INT:\n                statement += \"getInt\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case LONG:\n                statement += \"getLong\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case CHAR:\n                statement += \"getChar\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case FLOAT:\n                statement += \"getFloat\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case DOUBLE:\n                statement += \"getDouble\" + (isActivity ? \"Extra\" : \"\") + \"($S, \" + originalValue + \")\";\n                break;\n            case STRING:\n                statement += (isActivity ? (\"getExtras() == null ? \" + originalValue + \" : substitute.getIntent().getExtras().getString($S\") : (\"getString($S\")) + \", \" + originalValue + \")\";\n                break;\n            case SERIALIZABLE:\n                statement += (isActivity ? (\"getSerializableExtra($S)\") : (\"getSerializable($S)\"));\n                break;\n            case PARCELABLE:\n                statement += (isActivity ? (\"getParcelableExtra($S)\") : (\"getParcelable($S)\"));\n                break;\n            case OBJECT:\n                statement = \"serializationService.parseObject(substitute.\" + (isActivity ? \"getIntent().\" : \"getArguments().\") + (isActivity ? \"getStringExtra($S)\" : \"getString($S)\") + \", new \" + TYPE_WRAPPER + \"<$T>(){}.getType())\";\n                break;\n        }\n\n        return statement;\n    }\n\n    /**\n     * Categories field, find his papa.\n     *\n     * @param elements Field need autowired\n     */\n    private void categories(Set<? extends Element> elements) throws IllegalAccessException {\n        if (CollectionUtils.isNotEmpty(elements)) {\n            for (Element element : elements) {\n                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();\n\n                if (element.getModifiers().contains(Modifier.PRIVATE)) {\n                    throw new IllegalAccessException(\"The inject fields CAN NOT BE 'private'!!! please check field [\"\n                            + element.getSimpleName() + \"] in class [\" + enclosingElement.getQualifiedName() + \"]\");\n                }\n\n                if (parentAndChild.containsKey(enclosingElement)) { // Has categries\n                    parentAndChild.get(enclosingElement).add(element);\n                } else {\n                    List<Element> childs = new ArrayList<>();\n                    childs.add(element);\n                    parentAndChild.put(enclosingElement, childs);\n                }\n            }\n\n            logger.info(\"categories finished.\");\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/BaseProcessor.java",
    "content": "package com.alibaba.android.arouter.compiler.processor;\n\nimport com.alibaba.android.arouter.compiler.utils.Logger;\nimport com.alibaba.android.arouter.compiler.utils.TypeUtils;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.annotation.processing.AbstractProcessor;\nimport javax.annotation.processing.Filer;\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.util.Elements;\nimport javax.lang.model.util.Types;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static com.alibaba.android.arouter.compiler.utils.Consts.*;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.NO_MODULE_NAME_TIPS;\n\n/**\n * Base Processor\n *\n * @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)\n * @version 1.0\n * @since 2019-03-01 12:31\n */\npublic abstract class BaseProcessor extends AbstractProcessor {\n    Filer mFiler;\n    Logger logger;\n    Types types;\n    Elements elementUtils;\n    TypeUtils typeUtils;\n    // Module name, maybe its 'app' or others\n    String moduleName = null;\n    // If need generate router doc\n    boolean generateDoc;\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnv) {\n        super.init(processingEnv);\n\n        mFiler = processingEnv.getFiler();\n        types = processingEnv.getTypeUtils();\n        elementUtils = processingEnv.getElementUtils();\n        typeUtils = new TypeUtils(types, elementUtils);\n        logger = new Logger(processingEnv.getMessager());\n\n        // Attempt to get user configuration [moduleName]\n        Map<String, String> options = processingEnv.getOptions();\n        if (MapUtils.isNotEmpty(options)) {\n            moduleName = options.get(KEY_MODULE_NAME);\n            generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));\n        }\n\n        if (StringUtils.isNotEmpty(moduleName)) {\n            moduleName = moduleName.replaceAll(\"[^0-9a-zA-Z_]+\", \"\");\n\n            logger.info(\"The user has configuration the module name, it was [\" + moduleName + \"]\");\n        } else {\n            logger.error(NO_MODULE_NAME_TIPS);\n            throw new RuntimeException(\"ARouter::Compiler >>> No module name, for more information, look at gradle log.\");\n        }\n    }\n\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n    @Override\n    public Set<String> getSupportedOptions() {\n        return new HashSet<String>() {{\n            this.add(KEY_MODULE_NAME);\n            this.add(KEY_GENERATE_DOC_NAME);\n        }};\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/InterceptorProcessor.java",
    "content": "package com.alibaba.android.arouter.compiler.processor;\n\nimport com.alibaba.android.arouter.compiler.utils.Consts;\nimport com.alibaba.android.arouter.facade.annotation.Interceptor;\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.ParameterSpec;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.TypeSpec;\nimport com.squareup.javapoet.WildcardTypeName;\n\nimport org.apache.commons.collections4.CollectionUtils;\n\nimport java.io.IOException;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\n\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.annotation.processing.SupportedAnnotationTypes;\nimport javax.annotation.processing.SupportedOptions;\nimport javax.annotation.processing.SupportedSourceVersion;\nimport javax.lang.model.SourceVersion;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.TypeMirror;\n\nimport static com.alibaba.android.arouter.compiler.utils.Consts.*;\nimport static javax.lang.model.element.Modifier.PUBLIC;\n\n/**\n * Process the annotation of #{@link Interceptor}\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/23 14:11\n */\n@AutoService(Processor.class)\n@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)\npublic class InterceptorProcessor extends BaseProcessor {\n    private Map<Integer, Element> interceptors = new TreeMap<>();\n    private TypeMirror iInterceptor = null;\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnv) {\n        super.init(processingEnv);\n\n        iInterceptor = elementUtils.getTypeElement(Consts.IINTERCEPTOR).asType();\n\n        logger.info(\">>> InterceptorProcessor init. <<<\");\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param annotations\n     * @param roundEnv\n     */\n    @Override\n    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n        if (CollectionUtils.isNotEmpty(annotations)) {\n            Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);\n            try {\n                parseInterceptors(elements);\n            } catch (Exception e) {\n                logger.error(e);\n            }\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Parse interceptor.\n     *\n     * @param elements elements of interceptor.\n     */\n    private void parseInterceptors(Set<? extends Element> elements) throws IOException {\n        if (CollectionUtils.isNotEmpty(elements)) {\n            logger.info(\">>> Found interceptors, size is \" + elements.size() + \" <<<\");\n\n            // Verify and cache, sort incidentally.\n            for (Element element : elements) {\n                if (verify(element)) {  // Check the interceptor meta\n                    logger.info(\"A interceptor verify over, its \" + element.asType());\n                    Interceptor interceptor = element.getAnnotation(Interceptor.class);\n\n                    Element lastInterceptor = interceptors.get(interceptor.priority());\n                    if (null != lastInterceptor) { // Added, throw exceptions\n                        throw new IllegalArgumentException(\n                                String.format(Locale.getDefault(), \"More than one interceptors use same priority [%d], They are [%s] and [%s].\",\n                                        interceptor.priority(),\n                                        lastInterceptor.getSimpleName(),\n                                        element.getSimpleName())\n                        );\n                    }\n\n                    interceptors.put(interceptor.priority(), element);\n                } else {\n                    logger.error(\"A interceptor verify failed, its \" + element.asType());\n                }\n            }\n\n            // Interface of ARouter.\n            TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);\n            TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);\n\n            /**\n             *  Build input type, format as :\n             *\n             *  ```Map<Integer, Class<? extends IInterceptor>>```\n             */\n            ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(\n                    ClassName.get(Map.class),\n                    ClassName.get(Integer.class),\n                    ParameterizedTypeName.get(\n                            ClassName.get(Class.class),\n                            WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))\n                    )\n            );\n\n            // Build input param name.\n            ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, \"interceptors\").build();\n\n            // Build method : 'loadInto'\n            MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)\n                    .addAnnotation(Override.class)\n                    .addModifiers(PUBLIC)\n                    .addParameter(tollgateParamSpec);\n\n            // Generate\n            if (null != interceptors && interceptors.size() > 0) {\n                // Build method body\n                for (Map.Entry<Integer, Element> entry : interceptors.entrySet()) {\n                    loadIntoMethodOfTollgateBuilder.addStatement(\"interceptors.put(\" + entry.getKey() + \", $T.class)\", ClassName.get((TypeElement) entry.getValue()));\n                }\n            }\n\n            // Write to disk(Write file even interceptors is empty.)\n            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,\n                    TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)\n                            .addModifiers(PUBLIC)\n                            .addJavadoc(WARNING_TIPS)\n                            .addMethod(loadIntoMethodOfTollgateBuilder.build())\n                            .addSuperinterface(ClassName.get(type_IInterceptorGroup))\n                            .build()\n            ).build().writeTo(mFiler);\n\n            logger.info(\">>> Interceptor group write over. <<<\");\n        }\n    }\n\n    /**\n     * Verify inteceptor meta\n     *\n     * @param element Interceptor taw type\n     * @return verify result\n     */\n    private boolean verify(Element element) {\n        Interceptor interceptor = element.getAnnotation(Interceptor.class);\n        // It must be implement the interface IInterceptor and marked with annotation Interceptor.\n        return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/RouteProcessor.java",
    "content": "package com.alibaba.android.arouter.compiler.processor;\n\nimport com.alibaba.android.arouter.compiler.entity.RouteDoc;\nimport com.alibaba.android.arouter.compiler.utils.Consts;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.enums.RouteType;\nimport com.alibaba.android.arouter.facade.enums.TypeKind;\nimport com.alibaba.android.arouter.facade.model.RouteMeta;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\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.ParameterSpec;\nimport com.squareup.javapoet.ParameterizedTypeName;\nimport com.squareup.javapoet.TypeSpec;\nimport com.squareup.javapoet.WildcardTypeName;\n\nimport org.apache.commons.collections4.CollectionUtils;\nimport org.apache.commons.collections4.MapUtils;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\n\nimport javax.annotation.processing.ProcessingEnvironment;\nimport javax.annotation.processing.Processor;\nimport javax.annotation.processing.RoundEnvironment;\nimport javax.annotation.processing.SupportedAnnotationTypes;\nimport javax.lang.model.element.Element;\nimport javax.lang.model.element.TypeElement;\nimport javax.lang.model.type.DeclaredType;\nimport javax.lang.model.type.TypeMirror;\nimport javax.tools.StandardLocation;\n\nimport static com.alibaba.android.arouter.compiler.utils.Consts.ACTIVITY;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.ANNOTATION_TYPE_AUTOWIRED;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.ANNOTATION_TYPE_ROUTE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.FRAGMENT;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.IPROVIDER_GROUP;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.IROUTE_GROUP;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.ITROUTE_ROOT;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.METHOD_LOAD_INTO;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_GROUP;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_PROVIDER;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_ROOT;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.PACKAGE_OF_GENERATE_DOCS;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.PACKAGE_OF_GENERATE_FILE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.SEPARATOR;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.SERVICE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.WARNING_TIPS;\nimport static javax.lang.model.element.Modifier.PUBLIC;\n\n/**\n * A processor used for find route.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/15 下午10:08\n */\n@AutoService(Processor.class)\n@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})\npublic class RouteProcessor extends BaseProcessor {\n    private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.\n    private Map<String, String> rootMap = new TreeMap<>();  // Map of root metas, used for generate class file in order.\n\n    private TypeMirror iProvider = null;\n    private Writer docWriter;       // Writer used for write doc\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnv) {\n        super.init(processingEnv);\n\n        if (generateDoc) {\n            try {\n                docWriter = mFiler.createResource(\n                        StandardLocation.SOURCE_OUTPUT,\n                        PACKAGE_OF_GENERATE_DOCS,\n                        \"arouter-map-of-\" + moduleName + \".json\"\n                ).openWriter();\n            } catch (IOException e) {\n                logger.error(\"Create doc writer failed, because \" + e.getMessage());\n            }\n        }\n\n        iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();\n\n        logger.info(\">>> RouteProcessor init. <<<\");\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param annotations\n     * @param roundEnv\n     */\n    @Override\n    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n        if (CollectionUtils.isNotEmpty(annotations)) {\n            Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);\n            try {\n                logger.info(\">>> Found routes, start... <<<\");\n                this.parseRoutes(routeElements);\n\n            } catch (Exception e) {\n                logger.error(e);\n            }\n            return true;\n        }\n\n        return false;\n    }\n\n    private void parseRoutes(Set<? extends Element> routeElements) throws IOException {\n        if (CollectionUtils.isNotEmpty(routeElements)) {\n            // prepare the type an so on.\n\n            logger.info(\">>> Found routes, size is \" + routeElements.size() + \" <<<\");\n\n            rootMap.clear();\n\n            TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();\n            TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();\n            TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();\n            TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();\n\n            // Interface of ARouter\n            TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);\n            TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);\n            ClassName routeMetaCn = ClassName.get(RouteMeta.class);\n            ClassName routeTypeCn = ClassName.get(RouteType.class);\n\n            /*\n               Build input type, format as :\n\n               ```Map<String, Class<? extends IRouteGroup>>```\n             */\n            ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(\n                    ClassName.get(Map.class),\n                    ClassName.get(String.class),\n                    ParameterizedTypeName.get(\n                            ClassName.get(Class.class),\n                            WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))\n                    )\n            );\n\n            /*\n\n              ```Map<String, RouteMeta>```\n             */\n            ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(\n                    ClassName.get(Map.class),\n                    ClassName.get(String.class),\n                    ClassName.get(RouteMeta.class)\n            );\n\n            /*\n              Build input param name.\n             */\n            ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, \"routes\").build();\n            ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, \"atlas\").build();\n            ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, \"providers\").build();  // Ps. its param type same as groupParamSpec!\n\n            /*\n              Build method : 'loadInto'\n             */\n            MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)\n                    .addAnnotation(Override.class)\n                    .addModifiers(PUBLIC)\n                    .addParameter(rootParamSpec);\n\n            //  Follow a sequence, find out metas of group first, generate java file, then statistics them as root.\n            for (Element element : routeElements) {\n                TypeMirror tm = element.asType();\n                Route route = element.getAnnotation(Route.class);\n                RouteMeta routeMeta;\n\n                // Activity or Fragment\n                if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {\n                    // Get all fields annotation by @Autowired\n                    Map<String, Integer> paramsType = new HashMap<>();\n                    Map<String, Autowired> injectConfig = new HashMap<>();\n                    injectParamCollector(element, paramsType, injectConfig);\n\n                    if (types.isSubtype(tm, type_Activity)) {\n                        // Activity\n                        logger.info(\">>> Found activity route: \" + tm.toString() + \" <<<\");\n                        routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);\n                    } else {\n                        // Fragment\n                        logger.info(\">>> Found fragment route: \" + tm.toString() + \" <<<\");\n                        routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);\n                    }\n\n                    routeMeta.setInjectConfig(injectConfig);\n                } else if (types.isSubtype(tm, iProvider)) {         // IProvider\n                    logger.info(\">>> Found provider route: \" + tm.toString() + \" <<<\");\n                    routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);\n                } else if (types.isSubtype(tm, type_Service)) {           // Service\n                    logger.info(\">>> Found service route: \" + tm.toString() + \" <<<\");\n                    routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);\n                } else {\n                    throw new RuntimeException(\"The @Route is marked on unsupported class, look at [\" + tm.toString() + \"].\");\n                }\n\n                categories(routeMeta);\n            }\n\n            MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)\n                    .addAnnotation(Override.class)\n                    .addModifiers(PUBLIC)\n                    .addParameter(providerParamSpec);\n\n            Map<String, List<RouteDoc>> docSource = new HashMap<>();\n\n            // Start generate java source, structure is divided into upper and lower levels, used for demand initialization.\n            for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {\n                String groupName = entry.getKey();\n\n                MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)\n                        .addAnnotation(Override.class)\n                        .addModifiers(PUBLIC)\n                        .addParameter(groupParamSpec);\n\n                List<RouteDoc> routeDocList = new ArrayList<>();\n\n                // Build group method body\n                Set<RouteMeta> groupData = entry.getValue();\n                for (RouteMeta routeMeta : groupData) {\n                    RouteDoc routeDoc = extractDocInfo(routeMeta);\n\n                    ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());\n\n                    switch (routeMeta.getType()) {\n                        case PROVIDER:  // Need cache provider's super class\n                            List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();\n                            for (TypeMirror tm : interfaces) {\n                                routeDoc.addPrototype(tm.toString());\n\n                                if (types.isSameType(tm, iProvider)) {   // Its implements iProvider interface himself.\n                                    // This interface extend the IProvider, so it can be used for mark provider\n                                    loadIntoMethodOfProviderBuilder.addStatement(\n                                            \"providers.put($S, $T.build($T.\" + routeMeta.getType() + \", $T.class, $S, $S, null, \" + routeMeta.getPriority() + \", \" + routeMeta.getExtra() + \"))\",\n                                            (routeMeta.getRawType()).toString(),\n                                            routeMetaCn,\n                                            routeTypeCn,\n                                            className,\n                                            routeMeta.getPath(),\n                                            routeMeta.getGroup());\n                                } else if (types.isSubtype(tm, iProvider)) {\n                                    // This interface extend the IProvider, so it can be used for mark provider\n                                    loadIntoMethodOfProviderBuilder.addStatement(\n                                            \"providers.put($S, $T.build($T.\" + routeMeta.getType() + \", $T.class, $S, $S, null, \" + routeMeta.getPriority() + \", \" + routeMeta.getExtra() + \"))\",\n                                            tm.toString(),    // So stupid, will duplicate only save class name.\n                                            routeMetaCn,\n                                            routeTypeCn,\n                                            className,\n                                            routeMeta.getPath(),\n                                            routeMeta.getGroup());\n                                }\n                            }\n                            break;\n                        default:\n                            break;\n                    }\n\n                    // Make map body for paramsType\n                    StringBuilder mapBodyBuilder = new StringBuilder();\n                    Map<String, Integer> paramsType = routeMeta.getParamsType();\n                    Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();\n                    if (MapUtils.isNotEmpty(paramsType)) {\n                        List<RouteDoc.Param> paramList = new ArrayList<>();\n\n                        for (Map.Entry<String, Integer> types : paramsType.entrySet()) {\n                            mapBodyBuilder.append(\"put(\\\"\").append(types.getKey()).append(\"\\\", \").append(types.getValue()).append(\"); \");\n\n                            RouteDoc.Param param = new RouteDoc.Param();\n                            Autowired injectConfig = injectConfigs.get(types.getKey());\n                            param.setKey(types.getKey());\n                            param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());\n                            param.setDescription(injectConfig.desc());\n                            param.setRequired(injectConfig.required());\n\n                            paramList.add(param);\n                        }\n\n                        routeDoc.setParams(paramList);\n                    }\n                    String mapBody = mapBodyBuilder.toString();\n\n                    loadIntoMethodOfGroupBuilder.addStatement(\n                            \"atlas.put($S, $T.build($T.\" + routeMeta.getType() + \", $T.class, $S, $S, \" + (StringUtils.isEmpty(mapBody) ? null : (\"new java.util.HashMap<String, Integer>(){{\" + mapBodyBuilder.toString() + \"}}\")) + \", \" + routeMeta.getPriority() + \", \" + routeMeta.getExtra() + \"))\",\n                            routeMeta.getPath(),\n                            routeMetaCn,\n                            routeTypeCn,\n                            className,\n                            routeMeta.getPath().toLowerCase(),\n                            routeMeta.getGroup().toLowerCase());\n\n                    routeDoc.setClassName(className.toString());\n                    routeDocList.add(routeDoc);\n                }\n\n                // Generate groups\n                String groupFileName = NAME_OF_GROUP + groupName;\n                JavaFile.builder(PACKAGE_OF_GENERATE_FILE,\n                        TypeSpec.classBuilder(groupFileName)\n                                .addJavadoc(WARNING_TIPS)\n                                .addSuperinterface(ClassName.get(type_IRouteGroup))\n                                .addModifiers(PUBLIC)\n                                .addMethod(loadIntoMethodOfGroupBuilder.build())\n                                .build()\n                ).build().writeTo(mFiler);\n\n                logger.info(\">>> Generated group: \" + groupName + \"<<<\");\n                rootMap.put(groupName, groupFileName);\n                docSource.put(groupName, routeDocList);\n            }\n\n            if (MapUtils.isNotEmpty(rootMap)) {\n                // Generate root meta by group name, it must be generated before root, then I can find out the class of group.\n                for (Map.Entry<String, String> entry : rootMap.entrySet()) {\n                    loadIntoMethodOfRootBuilder.addStatement(\"routes.put($S, $T.class)\", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));\n                }\n            }\n\n            // Output route doc\n            if (generateDoc) {\n                docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));\n                docWriter.flush();\n                docWriter.close();\n            }\n\n            // Write provider into disk\n            String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;\n            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,\n                    TypeSpec.classBuilder(providerMapFileName)\n                            .addJavadoc(WARNING_TIPS)\n                            .addSuperinterface(ClassName.get(type_IProviderGroup))\n                            .addModifiers(PUBLIC)\n                            .addMethod(loadIntoMethodOfProviderBuilder.build())\n                            .build()\n            ).build().writeTo(mFiler);\n\n            logger.info(\">>> Generated provider map, name is \" + providerMapFileName + \" <<<\");\n\n            // Write root meta into disk.\n            String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;\n            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,\n                    TypeSpec.classBuilder(rootFileName)\n                            .addJavadoc(WARNING_TIPS)\n                            .addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))\n                            .addModifiers(PUBLIC)\n                            .addMethod(loadIntoMethodOfRootBuilder.build())\n                            .build()\n            ).build().writeTo(mFiler);\n\n            logger.info(\">>> Generated root, name is \" + rootFileName + \" <<<\");\n        }\n    }\n\n    /**\n     * Recursive inject config collector.\n     *\n     * @param element current element.\n     */\n    private void injectParamCollector(Element element, Map<String, Integer> paramsType, Map<String, Autowired> injectConfig) {\n        for (Element field : element.getEnclosedElements()) {\n            if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {\n                // It must be field, then it has annotation, but it not be provider.\n                Autowired paramConfig = field.getAnnotation(Autowired.class);\n                String injectName = StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name();\n                paramsType.put(injectName, typeUtils.typeExchange(field));\n                injectConfig.put(injectName, paramConfig);\n            }\n        }\n\n        // if has parent?\n        TypeMirror parent = ((TypeElement) element).getSuperclass();\n        if (parent instanceof DeclaredType) {\n            Element parentElement = ((DeclaredType) parent).asElement();\n            if (parentElement instanceof TypeElement && !((TypeElement) parentElement).getQualifiedName().toString().startsWith(\"android\")) {\n                injectParamCollector(parentElement, paramsType, injectConfig);\n            }\n        }\n    }\n\n    /**\n     * Extra doc info from route meta\n     *\n     * @param routeMeta meta\n     * @return doc\n     */\n    private RouteDoc extractDocInfo(RouteMeta routeMeta) {\n        RouteDoc routeDoc = new RouteDoc();\n        routeDoc.setGroup(routeMeta.getGroup());\n        routeDoc.setPath(routeMeta.getPath());\n        routeDoc.setDescription(routeMeta.getName());\n        routeDoc.setType(routeMeta.getType().name().toLowerCase());\n        routeDoc.setMark(routeMeta.getExtra());\n\n        return routeDoc;\n    }\n\n    /**\n     * Sort metas in group.\n     *\n     * @param routeMete metas.\n     */\n    private void categories(RouteMeta routeMete) {\n        if (routeVerify(routeMete)) {\n            logger.info(\">>> Start categories, group = \" + routeMete.getGroup() + \", path = \" + routeMete.getPath() + \" <<<\");\n            Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());\n            if (CollectionUtils.isEmpty(routeMetas)) {\n                Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {\n                    @Override\n                    public int compare(RouteMeta r1, RouteMeta r2) {\n                        try {\n                            return r1.getPath().compareTo(r2.getPath());\n                        } catch (NullPointerException npe) {\n                            logger.error(npe.getMessage());\n                            return 0;\n                        }\n                    }\n                });\n                routeMetaSet.add(routeMete);\n                groupMap.put(routeMete.getGroup(), routeMetaSet);\n            } else {\n                routeMetas.add(routeMete);\n            }\n        } else {\n            logger.warning(\">>> Route meta verify error, group is \" + routeMete.getGroup() + \" <<<\");\n        }\n    }\n\n    /**\n     * Verify the route meta\n     *\n     * @param meta raw meta\n     */\n    private boolean routeVerify(RouteMeta meta) {\n        String path = meta.getPath();\n\n        if (StringUtils.isEmpty(path) || !path.startsWith(\"/\")) {   // The path must be start with '/' and not empty!\n            return false;\n        }\n\n        if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)\n            try {\n                String defaultGroup = path.substring(1, path.indexOf(\"/\", 1));\n                if (StringUtils.isEmpty(defaultGroup)) {\n                    return false;\n                }\n\n                meta.setGroup(defaultGroup);\n                return true;\n            } catch (Exception e) {\n                logger.error(\"Failed to extract default group! \" + e.getMessage());\n                return false;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/Consts.java",
    "content": "package com.alibaba.android.arouter.compiler.utils;\n\n/**\n * Some consts used in processors\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/24 20:18\n */\npublic class Consts {\n    // Generate\n    public static final String SEPARATOR = \"$$\";\n    public static final String PROJECT = \"ARouter\";\n    public static final String TAG = PROJECT + \"::\";\n    public static final String WARNING_TIPS = \"DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER.\";\n    public static final String METHOD_LOAD_INTO = \"loadInto\";\n    public static final String METHOD_INJECT = \"inject\";\n    public static final String NAME_OF_ROOT = PROJECT + SEPARATOR + \"Root\";\n    public static final String NAME_OF_PROVIDER = PROJECT + SEPARATOR + \"Providers\";\n    public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + \"Group\" + SEPARATOR;\n    public static final String NAME_OF_INTERCEPTOR = PROJECT + SEPARATOR + \"Interceptors\";\n    public static final String NAME_OF_AUTOWIRED = SEPARATOR + PROJECT + SEPARATOR + \"Autowired\";\n    public static final String PACKAGE_OF_GENERATE_FILE = \"com.alibaba.android.arouter.routes\";\n    public static final String PACKAGE_OF_GENERATE_DOCS = \"com.alibaba.android.arouter.docs\";\n\n    // System interface\n    public static final String ACTIVITY = \"android.app.Activity\";\n    public static final String FRAGMENT = \"android.app.Fragment\";\n    public static final String FRAGMENT_V4 = \"android.support.v4.app.Fragment\";\n    public static final String SERVICE = \"android.app.Service\";\n    public static final String PARCELABLE = \"android.os.Parcelable\";\n\n    // Java type\n    private static final String LANG = \"java.lang\";\n    public static final String BYTE = LANG + \".Byte\";\n    public static final String SHORT = LANG + \".Short\";\n    public static final String INTEGER = LANG + \".Integer\";\n    public static final String LONG = LANG + \".Long\";\n    public static final String FLOAT = LANG + \".Float\";\n    public static final String DOUBEL = LANG + \".Double\";\n    public static final String BOOLEAN = LANG + \".Boolean\";\n    public static final String CHAR = LANG + \".Character\";\n    public static final String STRING = LANG + \".String\";\n    public static final String SERIALIZABLE = \"java.io.Serializable\";\n\n    // Custom interface\n    private static final String FACADE_PACKAGE = \"com.alibaba.android.arouter.facade\";\n    private static final String TEMPLATE_PACKAGE = \".template\";\n    private static final String SERVICE_PACKAGE = \".service\";\n    private static final String MODEL_PACKAGE = \".model\";\n    public static final String IPROVIDER = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IProvider\";\n    public static final String IPROVIDER_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IProviderGroup\";\n    public static final String IINTERCEPTOR = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IInterceptor\";\n    public static final String IINTERCEPTOR_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IInterceptorGroup\";\n    public static final String ITROUTE_ROOT = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IRouteRoot\";\n    public static final String IROUTE_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".IRouteGroup\";\n    public static final String ISYRINGE = FACADE_PACKAGE + TEMPLATE_PACKAGE + \".ISyringe\";\n    public static final String JSON_SERVICE = FACADE_PACKAGE + SERVICE_PACKAGE + \".SerializationService\";\n    public static final String TYPE_WRAPPER = FACADE_PACKAGE + MODEL_PACKAGE + \".TypeWrapper\";\n\n    // Log\n    static final String PREFIX_OF_LOGGER = PROJECT + \"::Compiler \";\n    public static final String NO_MODULE_NAME_TIPS = \"These no module name, at 'build.gradle', like :\\n\" +\n            \"android {\\n\" +\n            \"    defaultConfig {\\n\" +\n            \"        ...\\n\" +\n            \"        javaCompileOptions {\\n\" +\n            \"            annotationProcessorOptions {\\n\" +\n            \"                arguments = [AROUTER_MODULE_NAME: project.getName()]\\n\" +\n            \"            }\\n\" +\n            \"        }\\n\" +\n            \"    }\\n\" +\n            \"}\\n\";\n\n    // Options of processor\n    public static final String KEY_MODULE_NAME = \"AROUTER_MODULE_NAME\";\n    public static final String KEY_GENERATE_DOC_NAME = \"AROUTER_GENERATE_DOC\";\n\n    public static final String VALUE_ENABLE = \"enable\";\n\n    // Annotation type\n    public static final String ANNOTATION_TYPE_INTECEPTOR = FACADE_PACKAGE + \".annotation.Interceptor\";\n    public static final String ANNOTATION_TYPE_ROUTE = FACADE_PACKAGE + \".annotation.Route\";\n    public static final String ANNOTATION_TYPE_AUTOWIRED = FACADE_PACKAGE + \".annotation.Autowired\";\n}"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/Logger.java",
    "content": "package com.alibaba.android.arouter.compiler.utils;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport javax.annotation.processing.Messager;\nimport javax.tools.Diagnostic;\n\n/**\n * Simplify the message print.\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/8/22 上午11:48\n */\npublic class Logger {\n    private Messager msg;\n\n    public Logger(Messager messager) {\n        msg = messager;\n    }\n\n    /**\n     * Print info log.\n     */\n    public void info(CharSequence info) {\n        if (StringUtils.isNotEmpty(info)) {\n            msg.printMessage(Diagnostic.Kind.NOTE, Consts.PREFIX_OF_LOGGER + info);\n        }\n    }\n\n    public void error(CharSequence error) {\n        if (StringUtils.isNotEmpty(error)) {\n            msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + \"An exception is encountered, [\" + error + \"]\");\n        }\n    }\n\n    public void error(Throwable error) {\n        if (null != error) {\n            msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + \"An exception is encountered, [\" + error.getMessage() + \"]\" + \"\\n\" + formatStackTrace(error.getStackTrace()));\n        }\n    }\n\n    public void warning(CharSequence warning) {\n        if (StringUtils.isNotEmpty(warning)) {\n            msg.printMessage(Diagnostic.Kind.WARNING, Consts.PREFIX_OF_LOGGER + warning);\n        }\n    }\n\n    private String formatStackTrace(StackTraceElement[] stackTrace) {\n        StringBuilder sb = new StringBuilder();\n        for (StackTraceElement element : stackTrace) {\n            sb.append(\"    at \").append(element.toString());\n            sb.append(\"\\n\");\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/TypeUtils.java",
    "content": "package com.alibaba.android.arouter.compiler.utils;\n\nimport com.alibaba.android.arouter.facade.enums.TypeKind;\n\nimport javax.lang.model.element.Element;\nimport javax.lang.model.type.TypeMirror;\nimport javax.lang.model.util.Elements;\nimport javax.lang.model.util.Types;\n\nimport static com.alibaba.android.arouter.compiler.utils.Consts.BOOLEAN;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.BYTE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.DOUBEL;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.FLOAT;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.INTEGER;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.LONG;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.PARCELABLE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.SERIALIZABLE;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.SHORT;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.STRING;\nimport static com.alibaba.android.arouter.compiler.utils.Consts.CHAR;\n\n/**\n * Utils for type exchange\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/2/21 下午1:06\n */\npublic class TypeUtils {\n\n    private Types types;\n    private TypeMirror parcelableType;\n    private TypeMirror serializableType;\n\n    public TypeUtils(Types types, Elements elements) {\n        this.types = types;\n\n        parcelableType = elements.getTypeElement(PARCELABLE).asType();\n        serializableType = elements.getTypeElement(SERIALIZABLE).asType();\n    }\n\n    /**\n     * Diagnostics out the true java type\n     *\n     * @param element Raw type\n     * @return Type class of java\n     */\n    public int typeExchange(Element element) {\n        TypeMirror typeMirror = element.asType();\n\n        // Primitive\n        if (typeMirror.getKind().isPrimitive()) {\n            return element.asType().getKind().ordinal();\n        }\n\n        switch (typeMirror.toString()) {\n            case BYTE:\n                return TypeKind.BYTE.ordinal();\n            case SHORT:\n                return TypeKind.SHORT.ordinal();\n            case INTEGER:\n                return TypeKind.INT.ordinal();\n            case LONG:\n                return TypeKind.LONG.ordinal();\n            case FLOAT:\n                return TypeKind.FLOAT.ordinal();\n            case DOUBEL:\n                return TypeKind.DOUBLE.ordinal();\n            case BOOLEAN:\n                return TypeKind.BOOLEAN.ordinal();\n            case CHAR:\n                return TypeKind.CHAR.ordinal();\n            case STRING:\n                return TypeKind.STRING.ordinal();\n            default:\n                // Other side, maybe the PARCELABLE or SERIALIZABLE or OBJECT.\n                if (types.isSubtype(typeMirror, parcelableType)) {\n                    // PARCELABLE\n                    return TypeKind.PARCELABLE.ordinal();\n                } else if (types.isSubtype(typeMirror, serializableType)) {\n                    // SERIALIZABLE\n                    return TypeKind.SERIALIZABLE.ordinal();\n                } else {\n                    return TypeKind.OBJECT.ordinal();\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-compiler/src/main/resources/META-INF/gradle/incremental.annotation.processors",
    "content": "com.alibaba.android.arouter.compiler.processor.RouteProcessor,aggregating\ncom.alibaba.android.arouter.compiler.processor.AutowiredProcessor,aggregating\ncom.alibaba.android.arouter.compiler.processor.InterceptorProcessor,aggregating\n"
  },
  {
    "path": "arouter-gradle-plugin/build.gradle",
    "content": "apply plugin: 'groovy'\n\ndependencies {\n    compile gradleApi()\n    compile localGroovy()\n}\n\ndependencies {\n    compile 'com.android.tools.build:gradle:2.1.3'\n}\n\napply from: rootProject.file('gradle/publish.gradle')"
  },
  {
    "path": "arouter-gradle-plugin/gradle.properties",
    "content": "POM_NAME=ARouter Register\nPOM_ARTIFACT_ID=arouter-register\nPOM_PACKAGING=jar\nPOM_DESCRIPTION=Gradle plugin used for arouter route map register\nVERSION_NAME=1.0.2"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/core/RegisterCodeGenerator.groovy",
    "content": "package com.alibaba.android.arouter.register.core\n\nimport com.alibaba.android.arouter.register.utils.Logger\nimport com.alibaba.android.arouter.register.utils.ScanSetting\nimport org.apache.commons.io.IOUtils\nimport org.objectweb.asm.*\n\nimport java.util.jar.JarEntry\nimport java.util.jar.JarFile\nimport java.util.jar.JarOutputStream\nimport java.util.zip.ZipEntry\n/**\n * generate register code into LogisticsCenter.class\n * @author billy.qi email: qiyilike@163.com\n */\nclass RegisterCodeGenerator {\n    ScanSetting extension\n\n    private RegisterCodeGenerator(ScanSetting extension) {\n        this.extension = extension\n    }\n\n    static void insertInitCodeTo(ScanSetting registerSetting) {\n        if (registerSetting != null && !registerSetting.classList.isEmpty()) {\n            RegisterCodeGenerator processor = new RegisterCodeGenerator(registerSetting)\n            File file = RegisterTransform.fileContainsInitClass\n            if (file.getName().endsWith('.jar'))\n                processor.insertInitCodeIntoJarFile(file)\n        }\n    }\n\n    /**\n     * generate code into jar file\n     * @param jarFile the jar file which contains LogisticsCenter.class\n     * @return\n     */\n    private File insertInitCodeIntoJarFile(File jarFile) {\n        if (jarFile) {\n            def optJar = new File(jarFile.getParent(), jarFile.name + \".opt\")\n            if (optJar.exists())\n                optJar.delete()\n            def file = new JarFile(jarFile)\n            Enumeration enumeration = file.entries()\n            JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar))\n\n            while (enumeration.hasMoreElements()) {\n                JarEntry jarEntry = (JarEntry) enumeration.nextElement()\n                String entryName = jarEntry.getName()\n                ZipEntry zipEntry = new ZipEntry(entryName)\n                InputStream inputStream = file.getInputStream(jarEntry)\n                jarOutputStream.putNextEntry(zipEntry)\n                if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {\n\n                    Logger.i('Insert init code to class >> ' + entryName)\n\n                    def bytes = referHackWhenInit(inputStream)\n                    jarOutputStream.write(bytes)\n                } else {\n                    jarOutputStream.write(IOUtils.toByteArray(inputStream))\n                }\n                inputStream.close()\n                jarOutputStream.closeEntry()\n            }\n            jarOutputStream.close()\n            file.close()\n\n            if (jarFile.exists()) {\n                jarFile.delete()\n            }\n            optJar.renameTo(jarFile)\n        }\n        return jarFile\n    }\n\n    //refer hack class when object init\n    private byte[] referHackWhenInit(InputStream inputStream) {\n        ClassReader cr = new ClassReader(inputStream)\n        ClassWriter cw = new ClassWriter(cr, 0)\n        ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw)\n        cr.accept(cv, ClassReader.EXPAND_FRAMES)\n        return cw.toByteArray()\n    }\n\n    class MyClassVisitor extends ClassVisitor {\n\n        MyClassVisitor(int api, ClassVisitor cv) {\n            super(api, cv)\n        }\n\n        void visit(int version, int access, String name, String signature,\n                   String superName, String[] interfaces) {\n            super.visit(version, access, name, signature, superName, interfaces)\n        }\n        @Override\n        MethodVisitor visitMethod(int access, String name, String desc,\n                                  String signature, String[] exceptions) {\n            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions)\n            //generate code into this method\n            if (name == ScanSetting.GENERATE_TO_METHOD_NAME) {\n                mv = new RouteMethodVisitor(Opcodes.ASM5, mv)\n            }\n            return mv\n        }\n    }\n\n    class RouteMethodVisitor extends MethodVisitor {\n\n        RouteMethodVisitor(int api, MethodVisitor mv) {\n            super(api, mv)\n        }\n\n        @Override\n        void visitInsn(int opcode) {\n            //generate code before return\n            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {\n                extension.classList.each { name ->\n                    name = name.replaceAll(\"/\", \".\")\n                    mv.visitLdcInsn(name)//类名\n                    // generate invoke register method into LogisticsCenter.loadRouterMap()\n                    mv.visitMethodInsn(Opcodes.INVOKESTATIC\n                            , ScanSetting.GENERATE_TO_CLASS_NAME\n                            , ScanSetting.REGISTER_METHOD_NAME\n                            , \"(Ljava/lang/String;)V\"\n                            , false)\n                }\n            }\n            super.visitInsn(opcode)\n        }\n        @Override\n        void visitMaxs(int maxStack, int maxLocals) {\n            super.visitMaxs(maxStack + 4, maxLocals)\n        }\n    }\n}"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/core/RegisterTransform.groovy",
    "content": "package com.alibaba.android.arouter.register.core\n\nimport com.alibaba.android.arouter.register.utils.Logger\nimport com.alibaba.android.arouter.register.utils.ScanSetting\nimport com.alibaba.android.arouter.register.utils.ScanUtil\nimport com.android.build.api.transform.*\nimport com.android.build.gradle.internal.pipeline.TransformManager\nimport org.apache.commons.codec.digest.DigestUtils\nimport org.apache.commons.io.FileUtils\nimport org.gradle.api.Project\n\n/**\n * transform api\n * <p>\n *     1. Scan all classes to find which classes implement the specified interface\n *     2. Generate register code into class file: {@link ScanSetting#GENERATE_TO_CLASS_FILE_NAME}\n * @author billy.qi email: qiyilike@163.com\n * @since 17/3/21 11:48\n */\nclass RegisterTransform extends Transform {\n\n    Project project\n    static ArrayList<ScanSetting> registerList\n    static File fileContainsInitClass;\n\n    RegisterTransform(Project project) {\n        this.project = project\n    }\n\n    /**\n     * name of this transform\n     * @return\n     */\n    @Override\n    String getName() {\n        return ScanSetting.PLUGIN_NAME\n    }\n\n    @Override\n    Set<QualifiedContent.ContentType> getInputTypes() {\n        return TransformManager.CONTENT_CLASS\n    }\n\n    /**\n     * The plugin will scan all classes in the project\n     * @return\n     */\n    @Override\n    Set<QualifiedContent.Scope> getScopes() {\n        return TransformManager.SCOPE_FULL_PROJECT\n    }\n\n    @Override\n    boolean isIncremental() {\n        return false\n    }\n\n\n    @Override\n    void transform(Context context, Collection<TransformInput> inputs\n                   , Collection<TransformInput> referencedInputs\n                   , TransformOutputProvider outputProvider\n                   , boolean isIncremental) throws IOException, TransformException, InterruptedException {\n\n        Logger.i('Start scan register info in jar file.')\n\n        long startTime = System.currentTimeMillis()\n        boolean leftSlash = File.separator == '/'\n\n        if (!isIncremental){\n            outputProvider.deleteAll()\n        }\n\n        inputs.each { TransformInput input ->\n\n            // scan all jars\n            input.jarInputs.each { JarInput jarInput ->\n                String destName = jarInput.name\n                // rename jar files\n                def hexName = DigestUtils.md5Hex(jarInput.file.absolutePath)\n                if (destName.endsWith(\".jar\")) {\n                    destName = destName.substring(0, destName.length() - 4)\n                }\n                // input file\n                File src = jarInput.file\n                // output file\n                File dest = outputProvider.getContentLocation(destName + \"_\" + hexName, jarInput.contentTypes, jarInput.scopes, Format.JAR)\n\n                //scan jar file to find classes\n                if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {\n                    ScanUtil.scanJar(src, dest)\n                }\n                FileUtils.copyFile(src, dest)\n\n            }\n            // scan class files\n            input.directoryInputs.each { DirectoryInput directoryInput ->\n                File dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)\n                String root = directoryInput.file.absolutePath\n                if (!root.endsWith(File.separator))\n                    root += File.separator\n                directoryInput.file.eachFileRecurse { File file ->\n                    def path = file.absolutePath.replace(root, '')\n                    if (!leftSlash) {\n                        path = path.replaceAll(\"\\\\\\\\\", \"/\")\n                    }\n                    if(file.isFile() && ScanUtil.shouldProcessClass(path)){\n                        ScanUtil.scanClass(file)\n                    }\n                }\n\n                // copy to dest\n                FileUtils.copyDirectory(directoryInput.file, dest)\n            }\n        }\n\n        Logger.i('Scan finish, current cost time ' + (System.currentTimeMillis() - startTime) + \"ms\")\n\n        if (fileContainsInitClass) {\n            registerList.each { ext ->\n                Logger.i('Insert register code to file ' + fileContainsInitClass.absolutePath)\n\n                if (ext.classList.isEmpty()) {\n                    Logger.e(\"No class implements found for interface:\" + ext.interfaceName)\n                } else {\n                    ext.classList.each {\n                        Logger.i(it)\n                    }\n                    RegisterCodeGenerator.insertInitCodeTo(ext)\n                }\n            }\n        }\n\n        Logger.i(\"Generate code finish, current cost time: \" + (System.currentTimeMillis() - startTime) + \"ms\")\n    }\n}\n"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/launch/PluginLaunch.groovy",
    "content": "package com.alibaba.android.arouter.register.launch\n\nimport com.alibaba.android.arouter.register.utils.Logger\nimport com.android.build.gradle.AppExtension\nimport com.android.build.gradle.AppPlugin\nimport com.alibaba.android.arouter.register.utils.ScanSetting\nimport com.alibaba.android.arouter.register.core.RegisterTransform\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n/**\n * Simple version of AutoRegister plugin for ARouter\n * @author billy.qi email: qiyilike@163.com\n * @since 17/12/06 15:35\n */\npublic class PluginLaunch implements Plugin<Project> {\n\n    @Override\n    public void apply(Project project) {\n        def isApp = project.plugins.hasPlugin(AppPlugin)\n        //only application module needs this plugin to generate register code\n        if (isApp) {\n            Logger.make(project)\n\n            Logger.i('Project enable arouter-register plugin')\n\n            def android = project.extensions.getByType(AppExtension)\n            def transformImpl = new RegisterTransform(project)\n\n            //init arouter-auto-register settings\n            ArrayList<ScanSetting> list = new ArrayList<>(3)\n            list.add(new ScanSetting('IRouteRoot'))\n            list.add(new ScanSetting('IInterceptorGroup'))\n            list.add(new ScanSetting('IProviderGroup'))\n            RegisterTransform.registerList = list\n            //register this plugin\n            android.registerTransform(transformImpl)\n        }\n    }\n\n}\n"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/Logger.groovy",
    "content": "package com.alibaba.android.arouter.register.utils\n\nimport org.gradle.api.Project\n\n/**\n * Format log\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/12/18 下午2:43\n */\nclass Logger {\n    static org.gradle.api.logging.Logger logger\n\n    static void make(Project project) {\n        logger = project.getLogger()\n    }\n\n    static void i(String info) {\n        if (null != info && null != logger) {\n            logger.info(\"ARouter::Register >>> \" + info)\n        }\n    }\n\n    static void e(String error) {\n        if (null != error && null != logger) {\n            logger.error(\"ARouter::Register >>> \" + error)\n        }\n    }\n\n    static void w(String warning) {\n        if (null != warning && null != logger) {\n            logger.warn(\"ARouter::Register >>> \" + warning)\n        }\n    }\n}\n"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/ScanSetting.groovy",
    "content": "package com.alibaba.android.arouter.register.utils\n/**\n * register setting\n * @author billy.qi email: qiyilike@163.com\n * @since 17/3/28 11:48\n */\nclass ScanSetting {\n    static final String PLUGIN_NAME = \"com.alibaba.arouter\"\n    /**\n     * The register code is generated into this class\n     */\n    static final String GENERATE_TO_CLASS_NAME = 'com/alibaba/android/arouter/core/LogisticsCenter'\n    /**\n     * you know. this is the class file(or entry in jar file) name\n     */\n    static final String GENERATE_TO_CLASS_FILE_NAME = GENERATE_TO_CLASS_NAME + '.class'\n    /**\n     * The register code is generated into this method\n     */\n    static final String GENERATE_TO_METHOD_NAME = 'loadRouterMap'\n    /**\n     * The package name of the class generated by the annotationProcessor\n     */\n    static final String ROUTER_CLASS_PACKAGE_NAME = 'com/alibaba/android/arouter/routes/'\n    /**\n     * The package name of the interfaces\n     */\n    private static final INTERFACE_PACKAGE_NAME = 'com/alibaba/android/arouter/facade/template/'\n\n    /**\n     * register method name in class: {@link #GENERATE_TO_CLASS_NAME}\n     */\n    static final String REGISTER_METHOD_NAME = 'register'\n    /**\n     * scan for classes which implements this interface\n     */\n    String interfaceName = ''\n\n    /**\n     * jar file which contains class: {@link #GENERATE_TO_CLASS_NAME}\n     */\n    File fileContainsInitClass\n    /**\n     * scan result for {@link #interfaceName}\n     * class names in this list\n     */\n    ArrayList<String> classList = new ArrayList<>()\n\n    /**\n     * constructor for arouter-auto-register settings\n     * @param interfaceName interface to scan\n     */\n    ScanSetting(String interfaceName){\n        this.interfaceName = INTERFACE_PACKAGE_NAME + interfaceName\n    }\n\n}"
  },
  {
    "path": "arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/ScanUtil.groovy",
    "content": "package com.alibaba.android.arouter.register.utils\n\nimport com.alibaba.android.arouter.register.core.RegisterTransform\nimport org.objectweb.asm.ClassReader\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.ClassWriter\nimport org.objectweb.asm.Opcodes\n\nimport java.util.jar.JarEntry\nimport java.util.jar.JarFile\n\n/**\n * Scan all class in the package: com/alibaba/android/arouter/\n * find out all routers,interceptors and providers\n * @author billy.qi email: qiyilike@163.com\n * @since 17/3/20 11:48\n */\nclass ScanUtil {\n\n    /**\n     * scan jar file\n     * @param jarFile All jar files that are compiled into apk\n     * @param destFile dest file after this transform\n     */\n    static void scanJar(File jarFile, File destFile) {\n        if (jarFile) {\n            def file = new JarFile(jarFile)\n            Enumeration enumeration = file.entries()\n            while (enumeration.hasMoreElements()) {\n                JarEntry jarEntry = (JarEntry) enumeration.nextElement()\n                String entryName = jarEntry.getName()\n                if (entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)) {\n                    InputStream inputStream = file.getInputStream(jarEntry)\n                    scanClass(inputStream)\n                    inputStream.close()\n                } else if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {\n                    // mark this jar file contains LogisticsCenter.class\n                    // After the scan is complete, we will generate register code into this file\n                    RegisterTransform.fileContainsInitClass = destFile\n                }\n            }\n            file.close()\n        }\n    }\n\n    static boolean shouldProcessPreDexJar(String path) {\n        return !path.contains(\"com.android.support\") && !path.contains(\"/android/m2repository\")\n    }\n\n    static boolean shouldProcessClass(String entryName) {\n        return entryName != null && entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)\n    }\n\n    /**\n     * scan class file\n     * @param class file\n     */\n    static void scanClass(File file) {\n        scanClass(new FileInputStream(file))\n    }\n\n    static void scanClass(InputStream inputStream) {\n        ClassReader cr = new ClassReader(inputStream)\n        ClassWriter cw = new ClassWriter(cr, 0)\n        ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw)\n        cr.accept(cv, ClassReader.EXPAND_FRAMES)\n        inputStream.close()\n    }\n\n    static class ScanClassVisitor extends ClassVisitor {\n\n        ScanClassVisitor(int api, ClassVisitor cv) {\n            super(api, cv)\n        }\n\n        void visit(int version, int access, String name, String signature,\n                   String superName, String[] interfaces) {\n            super.visit(version, access, name, signature, superName, interfaces)\n            RegisterTransform.registerList.each { ext ->\n                if (ext.interfaceName && interfaces != null) {\n                    interfaces.each { itName ->\n                        if (itName == ext.interfaceName) {\n                            //fix repeated inject init code when Multi-channel packaging\n                            if (!ext.classList.contains(name)) {\n                                ext.classList.add(name)\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "arouter-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.alibaba.arouter.properties",
    "content": "implementation-class=com.alibaba.android.arouter.register.launch.PluginLaunch"
  },
  {
    "path": "arouter-idea-plugin/build.gradle",
    "content": "plugins {\n    id 'org.jetbrains.kotlin.jvm'\n    id \"org.jetbrains.intellij\" version '0.3.12'\n}\n\nintellij {\n    version support_idea_version\n    updateSinceUntilBuild false\n    plugins 'coverage'\n}\n\npatchPluginXml {\n    changeNotes \"\"\"\n      1.0.0\n      First Release\n      \"\"\"\n}\n\npublishPlugin {\n    username intellijPublishUsername\n    password intellijPublishPassword\n    channels \"beta\"\n}\n\ndependencies {\n    compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'\n}\n\ncompileKotlin {\n    kotlinOptions {\n        jvmTarget = \"1.8\"\n    }\n}\ncompileTestKotlin {\n    kotlinOptions {\n        jvmTarget = \"1.8\"\n    }\n}"
  },
  {
    "path": "arouter-idea-plugin/src/main/kotlin/com/alibaba/android/arouter/idea/extensions/NavigationLineMarker.kt",
    "content": "package com.alibaba.android.arouter.idea.extensions\n\nimport com.intellij.codeHighlighting.Pass\nimport com.intellij.codeInsight.daemon.GutterIconNavigationHandler\nimport com.intellij.codeInsight.daemon.LineMarkerInfo\nimport com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor\nimport com.intellij.navigation.NavigationItem\nimport com.intellij.notification.Notification\nimport com.intellij.notification.NotificationType\nimport com.intellij.notification.Notifications\nimport com.intellij.openapi.editor.markup.GutterIconRenderer\nimport com.intellij.openapi.util.IconLoader\nimport com.intellij.psi.*\nimport com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl\nimport com.intellij.psi.search.GlobalSearchScope\nimport com.intellij.psi.search.searches.AnnotatedMembersSearch\nimport java.awt.event.MouseEvent\n\n/**\n * Mark navigation target.\n *\n * @author zhilong <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 2018/12/13 12:30 PM\n */\nclass NavigationLineMarker : LineMarkerProviderDescriptor(), GutterIconNavigationHandler<PsiElement> {\n    override fun getName(): String? {\n        return \"ARouter Location\"\n    }\n\n    override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {\n        return if (isNavigationCall(element)) {\n            LineMarkerInfo<PsiElement>(element, element.textRange, navigationOnIcon,\n                    Pass.UPDATE_ALL, null, this,\n                    GutterIconRenderer.Alignment.LEFT)\n        } else {\n            null\n        }\n    }\n\n    override fun navigate(e: MouseEvent?, psiElement: PsiElement?) {\n        if (psiElement is PsiMethodCallExpression) {\n            val psiExpressionList = (psiElement as PsiMethodCallExpressionImpl).argumentList\n            if (psiExpressionList.expressions.size == 1) {\n                // Support `build(path)` only now.\n\n                val targetPath = psiExpressionList.expressions[0].text.replace(\"\\\"\", \"\")\n                val fullScope = GlobalSearchScope.allScope(psiElement.project)\n                val routeAnnotationWrapper = AnnotatedMembersSearch.search(getAnnotationWrapper(psiElement, fullScope)\n                        ?: return, fullScope).findAll()\n                val target = routeAnnotationWrapper.find {\n                    it.modifierList?.annotations?.map { it.findAttributeValue(\"path\")?.text?.replace(\"\\\"\", \"\") }?.contains(targetPath)\n                            ?: false\n                }\n\n                if (null != target) {\n                    // Redirect to target.\n                    NavigationItem::class.java.cast(target).navigate(true)\n                    return\n                }\n            }\n        }\n\n        notifyNotFound()\n    }\n\n    private fun notifyNotFound() {\n        Notifications.Bus.notify(Notification(NOTIFY_SERVICE_NAME, NOTIFY_TITLE, NOTIFY_NO_TARGET_TIPS, NotificationType.WARNING))\n    }\n\n    private fun getAnnotationWrapper(psiElement: PsiElement?, scope: GlobalSearchScope): PsiClass? {\n        if (null == routeAnnotationWrapper) {\n            routeAnnotationWrapper = JavaPsiFacade.getInstance(psiElement?.project).findClass(ROUTE_ANNOTATION_NAME, scope)\n        }\n\n        return routeAnnotationWrapper\n    }\n\n    override fun collectSlowLineMarkers(elements: MutableList<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {}\n\n    /**\n     * Judge whether the code used for navigation.\n     */\n    private fun isNavigationCall(psiElement: PsiElement): Boolean {\n        if (psiElement is PsiCallExpression) {\n            val method = psiElement.resolveMethod() ?: return false\n            val parent = method.parent\n\n            if (method.name == \"build\" && parent is PsiClass) {\n                if (isClassOfARouter(parent)) {\n                    return true\n                }\n            }\n        }\n        return false\n    }\n\n    /**\n     * Judge whether the caller was ARouter\n     */\n    private fun isClassOfARouter(psiClass: PsiClass): Boolean {\n        // It was ARouter\n        if (psiClass.name.equals(SDK_NAME)) {\n            return true\n        }\n\n        // It super class was ARouter\n        psiClass.supers.find { it.name == SDK_NAME } ?: return false\n\n        return true\n    }\n\n    companion object {\n        const val ROUTE_ANNOTATION_NAME = \"com.alibaba.android.arouter.facade.annotation.Route\"\n        const val SDK_NAME = \"ARouter\"\n\n        // Notify\n        const val NOTIFY_SERVICE_NAME = \"ARouter Plugin Tips\"\n        const val NOTIFY_TITLE = \"Road Sign\"\n        const val NOTIFY_NO_TARGET_TIPS = \"No destination found or unsupported type.\"\n\n        val navigationOnIcon = IconLoader.getIcon(\"/icon/outline_my_location_black_18dp.png\")\n    }\n\n    // I'm 100% sure this point can not made memory leak.\n    private var routeAnnotationWrapper: PsiClass? = null\n}"
  },
  {
    "path": "arouter-idea-plugin/src/main/resources/META-INF/plugin.xml",
    "content": "<idea-plugin>\n    <id>arouter-roadsign</id>\n    <name>ARouter Helper</name>\n    <version>1.0.0</version>\n    <vendor email=\"zhilong.liu@aliyun.com\" url=\"https://www.alibaba.com\">Alibaba</vendor>\n    <idea-version since-build=\"162.0\"/>\n\n    <description><![CDATA[\n    Support to track navigation target.\n    ]]></description>\n\n    <extensions defaultExtensionNs=\"com.intellij\">\n        <codeInsight.lineMarkerProvider language=\"JAVA\"\n                                        implementationClass=\"com.alibaba.android.arouter.idea.extensions.NavigationLineMarker\"/>\n    </extensions>\n</idea-plugin>"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.4.10'\n    ext.arouter_register_version = '1.0.2'\n\n    repositories {\n        mavenCentral()\n        google()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.1.1'\n        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'\n        classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath \"com.alibaba:arouter-register:$arouter_register_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        mavenLocal()\n        mavenCentral()\n        google()\n    }\n}"
  },
  {
    "path": "gradle/publish.gradle",
    "content": "/*\n * Copyright 2013 Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'maven'\napply plugin: 'signing'\n\nversion = VERSION_NAME\ngroup = GROUP\n\ndef isReleaseBuild() {\n    return VERSION_NAME.contains(\"SNAPSHOT\") == false\n}\n\ndef getReleaseRepositoryUrl() {\n    return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL\n            : \"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/\"\n}\n\ndef getSnapshotRepositoryUrl() {\n    return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL\n            : \"https://s01.oss.sonatype.org/content/repositories/snapshots/\"\n}\n\ndef getRepositoryUsername() {\n    return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : \"\"\n}\n\ndef getRepositoryPassword() {\n    return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : \"\"\n}\n\ndef configurePom(pom) {\n    pom.groupId = GROUP\n    pom.artifactId = POM_ARTIFACT_ID\n    pom.version = VERSION_NAME\n\n    pom.project {\n        name POM_NAME\n        packaging POM_PACKAGING\n        description POM_DESCRIPTION\n        url POM_URL\n\n        scm {\n            url POM_SCM_URL\n            connection POM_SCM_CONNECTION\n            developerConnection POM_SCM_DEV_CONNECTION\n        }\n\n        licenses {\n            license {\n                name POM_LICENCE_NAME\n                url POM_LICENCE_URL\n                distribution POM_LICENCE_DIST\n            }\n        }\n\n        developers {\n            developer {\n                id POM_DEVELOPER_ID\n                name POM_DEVELOPER_NAME\n            }\n        }\n    }\n}\n\nafterEvaluate { project ->\n    uploadArchives {\n        repositories {\n            mavenDeployer {\n                beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }\n\n                repository(url: getReleaseRepositoryUrl()) {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n                snapshotRepository(url: getSnapshotRepositoryUrl()) {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n\n                configurePom(pom)\n            }\n        }\n    }\n\n    tasks.create(\"installLocally\", Upload) {\n        configuration = configurations.archives\n\n        repositories {\n            mavenDeployer {\n                repository(url: \"file://${rootProject.buildDir}/localMaven\")\n\n                configurePom(pom)\n            }\n        }\n    }\n\n    signing {\n        required { isReleaseBuild() && gradle.taskGraph.hasTask(\"uploadArchives\") }\n        sign configurations.archives\n    }\n\n    if (project.getPlugins().hasPlugin('com.android.application') ||\n            project.getPlugins().hasPlugin('com.android.library')) {\n        task install(type: Upload, dependsOn: assemble) {\n            repositories.mavenInstaller {\n                configuration = configurations.archives\n\n                configurePom(pom)\n            }\n        }\n\n        task androidJavadocs(type: Javadoc) {\n            source = android.sourceSets.main.java.source\n            classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n        }\n\n        task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {\n            classifier = 'javadoc'\n            from androidJavadocs.destinationDir\n        }\n\n        task androidSourcesJar(type: Jar) {\n            classifier = 'sources'\n            from android.sourceSets.main.java.source\n        }\n    } else {\n        install {\n            repositories.mavenInstaller {\n                configurePom(pom)\n            }\n        }\n\n        task sourcesJar(type: Jar, dependsOn:classes) {\n            classifier = 'sources'\n            from sourceSets.main.allSource\n        }\n\n        task javadocJar(type: Jar, dependsOn:javadoc) {\n            classifier = 'javadoc'\n            from javadoc.destinationDir\n        }\n    }\n\n    if (JavaVersion.current().isJava8Compatible()) {\n        allprojects {\n            tasks.withType(Javadoc) {\n                options.addStringOption('Xdoclint:none', '-quiet')\n            }\n        }\n    }\n\n    artifacts {\n        if (project.getPlugins().hasPlugin('com.android.application') ||\n                project.getPlugins().hasPlugin('com.android.library')) {\n            archives androidSourcesJar\n            archives androidJavadocsJar\n        } else {\n            archives sourcesJar\n            archives javadocJar\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jun 12 21:40:29 CST 2020\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.5-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "## Project-wide Gradle settings.\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.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\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#Fri Oct 09 19:03:24 CST 2015\n\n# JVM\norg.gradle.daemon=true\n#org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005\n\nCOMPILE_SDK_VERSION=29\nBUILDTOOLS_VERSION=29.0.2\nSUPPORT_LIB_VERSION=28.0.0\nMIN_SDK_VERSION=14\nTARGET_SDK_VERSION=28\n\nGROUP=com.alibaba\n\nPOM_URL=https://github.com/Alibaba/ARouter/\nPOM_SCM_URL=https://github.com/Alibaba/ARouter/\nPOM_SCM_CONNECTION=scm:git:git://github.com/Alibaba/ARouter.git\nPOM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/Alibaba/ARouter.git\n\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\n\nPOM_DEVELOPER_ID=zhi1ong\nPOM_DEVELOPER_NAME=ZhiLong Liu\n\narouter_idea_plugin_version=1.0.0\nsupport_idea_version=2016.2.5\narouter_idea_plugin_name=ARouter Road Sign"
  },
  {
    "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\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\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%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "module-java/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\ndependencies {\n    implementation project(':arouter-annotation')\n    implementation project(':arouter-api')\n    annotationProcessor project(':arouter-compiler')\n\n    implementation project(':module-java-export')\n\n    implementation \"com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}\"\n    implementation 'com.alibaba:fastjson:1.2.48'\n}\nandroid {\n    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)\n    buildToolsVersion BUILDTOOLS_VERSION\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: \"enable\"]\n            }\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    buildTypes {\n        release {\n            debuggable false\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n\n        lintOptions { abortOnError false }\n    }\n}"
  },
  {
    "path": "module-java/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.alibaba.android.arouter.demo.module1\">\n\n    <!-- 读写存储的权限 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n    <application>\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.TestModuleActivity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.TestModule2Activity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.TestWebview\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.testactivity.Test2Activity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.testactivity.Test3Activity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.testactivity.Test4Activity\" />\n        <activity android:name=\"com.alibaba.android.arouter.demo.module1.testactivity.TestDynamicActivity\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/BlankFragment.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.alibaba.android.arouter.demo.service.model.TestObj;\nimport com.alibaba.android.arouter.demo.service.model.TestParcelable;\nimport com.alibaba.android.arouter.demo.service.model.TestSerializable;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * A simple {@link Fragment} subclass.\n */\n@Route(path = \"/test/fragment\")\npublic class BlankFragment extends Fragment {\n\n    @Autowired\n    String name;\n\n    @Autowired(required = true)\n    TestObj obj;\n\n    @Autowired\n    int age = 10;\n\n    @Autowired\n    int height = 175;\n\n    @Autowired(name = \"boy\", required = true)\n    boolean girl;\n\n    @Autowired\n    char ch = 'A';\n\n    @Autowired\n    float fl = 12.00f;\n\n    @Autowired\n    double dou = 12.01d;\n\n    @Autowired\n    TestSerializable ser;\n\n    @Autowired\n    TestParcelable pac;\n\n    @Autowired\n    List<TestObj> objList;\n\n    @Autowired\n    Map<String, List<TestObj>> map;\n\n    public BlankFragment() {\n        // Required empty public constructor\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,\n                             Bundle savedInstanceState) {\n        TextView textView = new TextView(getActivity());\n        return textView;\n    }\n\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/MainLooper.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\npublic class MainLooper extends Handler {\n    private static MainLooper instance = new MainLooper(Looper.getMainLooper());\n\n    protected MainLooper(Looper looper) {\n        super(looper);\n    }\n\n    public static MainLooper getInstance() {\n        return instance;\n    }\n\n    public static void runOnUiThread(Runnable runnable) {\n        if(Looper.getMainLooper().equals(Looper.myLooper())) {\n            runnable.run();\n        } else {\n            instance.post(runnable);\n        }\n\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestInterceptor90.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.annotation.Interceptor;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\nimport com.alibaba.android.arouter.facade.template.IInterceptor;\n\n/**\n * TODO feature\n *\n * @author Alex <a href=\"mailto:zhilong.liu@aliyun.com\">Contact me.</a>\n * @version 1.0\n * @since 16/9/9 14:34\n */\n@Interceptor(priority = 90)\npublic class TestInterceptor90 implements IInterceptor {\n    /**\n     * The operation of this interceptor.\n     *\n     * @param postcard meta\n     * @param callback cb\n     */\n    @Override\n    public void process(Postcard postcard, InterceptorCallback callback) {\n        callback.onContinue(postcard);\n    }\n\n    /**\n     * Do your init work in this method, it well be call when processor has been load.\n     *\n     * @param context ctx\n     */\n    @Override\n    public void init(Context context) {\n        Log.e(\"test\", \"位于moudle1中的拦截器初始化了\");\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestModule2Activity.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/module/2\", group = \"m2\")\npublic class TestModule2Activity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test_module2);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestModuleActivity.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\nimport android.app.Activity;\nimport android.os.Bundle;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/module/1\")\npublic class TestModuleActivity extends Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test_module);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestWebview.java",
    "content": "package com.alibaba.android.arouter.demo.module1;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.webkit.WebView;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/test/webview\")\npublic class TestWebview extends Activity {\n\n    WebView webview;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test_webview);\n\n\n        webview = (WebView) findViewById(R.id.webview);\n        webview.loadUrl(getIntent().getStringExtra(\"url\"));\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/BaseActivity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.support.v7.app.AppCompatActivity;\n\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\n\n/**\n * Base Activity (Used for test inject)\n */\npublic class BaseActivity extends AppCompatActivity {\n    @Autowired(desc = \"姓名\")\n    String name = \"jack\";\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test1Activity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.os.Bundle;\nimport android.widget.TextView;\n\nimport com.alibaba.android.arouter.demo.module1.R;\nimport com.alibaba.android.arouter.demo.service.HelloService;\nimport com.alibaba.android.arouter.demo.service.model.TestObj;\nimport com.alibaba.android.arouter.demo.service.model.TestParcelable;\nimport com.alibaba.android.arouter.demo.service.model.TestSerializable;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.launcher.ARouter;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * https://m.aliyun.com/test/activity1?name=老王&age=23&boy=true&high=180\n */\n@Route(path = \"/test/activity1\", name = \"测试用 Activity\")\npublic class Test1Activity extends BaseActivity {\n    @Autowired\n    int age = 10;\n\n    @Autowired\n    int height = 175;\n\n    @Autowired(name = \"boy\", required = true)\n    boolean girl;\n\n    @Autowired\n    char ch = 'A';\n\n    @Autowired\n    float fl = 12.00f;\n\n    @Autowired\n    double dou = 12.01d;\n\n    @Autowired\n    TestSerializable ser;\n\n    @Autowired\n    TestParcelable pac;\n\n    @Autowired\n    TestObj obj;\n\n    @Autowired\n    List<TestObj> objList;\n\n    @Autowired\n    Map<String, List<TestObj>> map;\n\n    private long high;\n\n    @Autowired\n    String url;\n\n    @Autowired\n    HelloService helloService;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test1);\n\n        ARouter.getInstance().inject(this);\n\n        // No more getter ...\n        // name = getIntent().getStringExtra(\"name\");\n        // age = getIntent().getIntExtra(\"age\", 0);\n        // girl = getIntent().getBooleanExtra(\"girl\", false);\n        // high = getIntent().getLongExtra(\"high\", 0);\n        // url = getIntent().getStringExtra(\"url\");\n\n        String params = String.format(\n                \"name=%s,\\n age=%s, \\n height=%s,\\n girl=%s,\\n high=%s,\\n url=%s,\\n ser=%s,\\n pac=%s,\\n obj=%s \\n ch=%s \\n fl = %s, \\n dou = %s, \\n objList=%s, \\n map=%s\",\n                name,\n                age,\n                height,\n                girl,\n                high,\n                url,\n                ser,\n                pac,\n                obj,\n                ch,\n                fl,\n                dou,\n                objList,\n                map\n        );\n        helloService.sayHello(\"Hello moto.\");\n\n        ((TextView) findViewById(R.id.test)).setText(\"I am \" + Test1Activity.class.getName());\n        ((TextView) findViewById(R.id.test2)).setText(params);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test2Activity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.TextUtils;\nimport android.widget.Toast;\n\nimport com.alibaba.android.arouter.demo.module1.R;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/test/activity2\")\npublic class Test2Activity extends AppCompatActivity {\n\n    @Autowired\n    String key1;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test2);\n\n        String value = getIntent().getStringExtra(\"key1\");\n        if (!TextUtils.isEmpty(value)) {\n            Toast.makeText(this, \"exist param :\" + value, Toast.LENGTH_LONG).show();\n        }\n\n        setResult(999);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test3Activity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.widget.TextView;\n\nimport com.alibaba.android.arouter.demo.module1.R;\nimport com.alibaba.android.arouter.facade.annotation.Autowired;\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.launcher.ARouter;\n\n/**\n * 自动注入的测试用例\n */\n@Route(path = \"/test/activity3\")\npublic class Test3Activity extends AppCompatActivity {\n\n    @Autowired\n    String name;\n\n    @Autowired\n    int age;\n\n    @Autowired(name = \"boy\")\n    boolean girl;\n\n    // 这个字段没有注解，是不会自动注入的\n    private long high;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test1);\n\n        ARouter.getInstance().inject(this);\n\n        String params = String.format(\"name=%s, age=%s, girl=%s, high=%s\", name, age, girl, high);\n\n        ((TextView)findViewById(R.id.test)).setText(\"I am \" + Test3Activity.class.getName());\n        ((TextView)findViewById(R.id.test2)).setText(params);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test4Activity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.TextUtils;\nimport android.widget.TextView;\n\nimport com.alibaba.android.arouter.demo.module1.R;\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/test/activity4\")\npublic class Test4Activity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test1);\n\n        ((TextView)findViewById(R.id.test)).setText(\"I am \" + Test4Activity.class.getName());\n        String extra = getIntent().getStringExtra(\"extra\");\n        if (!TextUtils.isEmpty(extra)) {\n            ((TextView)findViewById(R.id.test2)).setText(extra);\n        }\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/TestDynamicActivity.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testactivity;\n\nimport android.os.Bundle;\n\n// 用于测试不标注 Route 的情况下，动态增加路由\n//@Route(path=\"/dynamic/activity\")\npublic class TestDynamicActivity extends Test1Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testinterceptor/Test1Interceptor.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testinterceptor;\n\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.util.Log;\n\nimport com.alibaba.android.arouter.demo.module1.MainLooper;\nimport com.alibaba.android.arouter.facade.Postcard;\nimport com.alibaba.android.arouter.facade.annotation.Interceptor;\nimport com.alibaba.android.arouter.facade.callback.InterceptorCallback;\nimport com.alibaba.android.arouter.facade.template.IInterceptor;\n\n/**\n * 一个拦截器的例子\n *\n * @author Alex <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/1/3 11:20\n */\n@Interceptor(priority = 7)\npublic class Test1Interceptor implements IInterceptor {\n    /**\n     * The operation of this interceptor.\n     *\n     * @param postcard meta\n     * @param callback cb\n     */\n    @Override\n    public void process(final Postcard postcard, final InterceptorCallback callback) {\n        if (\"/test/activity4\".equals(postcard.getPath())) {\n\n            // 这里的弹窗仅做举例，代码写法不具有可参考价值\n            final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());\n            ab.setCancelable(false);\n            ab.setTitle(\"温馨提醒\");\n            ab.setMessage(\"想要跳转到Test4Activity么？(触发了\\\"/inter/test1\\\"拦截器，拦截了本次跳转)\");\n            ab.setNegativeButton(\"继续\", new DialogInterface.OnClickListener() {\n                @Override\n                public void onClick(DialogInterface dialog, int which) {\n                    callback.onContinue(postcard);\n                }\n            });\n            ab.setNeutralButton(\"算了\", new DialogInterface.OnClickListener() {\n                @Override\n                public void onClick(DialogInterface dialog, int which) {\n                    callback.onInterrupt(null);\n                }\n            });\n            ab.setPositiveButton(\"加点料\", new DialogInterface.OnClickListener() {\n                @Override\n                public void onClick(DialogInterface dialog, int which) {\n                    postcard.withString(\"extra\", \"我是在拦截器中附加的参数\");\n                    callback.onContinue(postcard);\n                }\n            });\n\n            MainLooper.runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    ab.create().show();\n                }\n            });\n        } else {\n            callback.onContinue(postcard);\n        }\n    }\n\n    /**\n     * Do your init work in this method, it well be call when processor has been load.\n     *\n     * @param context ctx\n     */\n    @Override\n    public void init(Context context) {\n        Log.e(\"testService\", Test1Interceptor.class.getName() + \" has init.\");\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/HelloServiceImpl.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testservice;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.alibaba.android.arouter.demo.service.HelloService;\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n/**\n * TODO feature\n *\n * @author Alex <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/1/3 10:26\n */\n@Route(path = \"/yourservicegroupname/hello\")\npublic class HelloServiceImpl implements HelloService {\n    Context mContext;\n\n    @Override\n    public void sayHello(String name) {\n        Toast.makeText(mContext, \"Hello \" + name, Toast.LENGTH_SHORT).show();\n    }\n\n    /**\n     * Do your init work in this method, it well be call when processor has been load.\n     *\n     * @param context ctx\n     */\n    @Override\n    public void init(Context context) {\n        mContext = context;\n        Log.e(\"testService\", HelloService.class.getName() + \" has init.\");\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/JsonServiceImpl.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testservice;\n\nimport android.content.Context;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.service.SerializationService;\nimport com.alibaba.fastjson.JSON;\n\nimport java.lang.reflect.Type;\n\n/**\n * Used for json converter\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/4/10 下午2:10\n */\n@Route(path = \"/yourservicegroupname/json\")\npublic class JsonServiceImpl implements SerializationService {\n    @Override\n    public void init(Context context) {\n\n    }\n\n    @Override\n    public <T> T json2Object(String text, Class<T> clazz) {\n        return JSON.parseObject(text, clazz);\n    }\n\n    @Override\n    public String object2Json(Object instance) {\n        return JSON.toJSONString(instance);\n    }\n\n    @Override\n    public <T> T parseObject(String input, Type clazz) {\n        return JSON.parseObject(input, clazz);\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/SingleService.java",
    "content": "package com.alibaba.android.arouter.demo.module1.testservice;\n\nimport android.content.Context;\nimport android.widget.Toast;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * 测试单类注入\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/4/24 下午9:04\n */\n@Route(path = \"/yourservicegroupname/single\")\npublic class SingleService implements IProvider {\n\n    Context mContext;\n\n    public void sayHello(String name) {\n        Toast.makeText(mContext, \"Hello \" + name, Toast.LENGTH_SHORT).show();\n    }\n\n    @Override\n    public void init(Context context) {\n        mContext = context;\n    }\n}\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_test1\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".module1.testactivity.Test1Activity\">\n\n    <TextView\n        android:id=\"@+id/test\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"I am test1\"\n        android:textSize=\"18sp\"/>\n\n\n    <TextView\n        android:id=\"@+id/test2\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/test\"\n        android:layout_marginTop=\"20dp\"\n        android:textSize=\"18sp\"/>\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_test2\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".module1.testactivity.Test2Activity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"I am test2\"\n        android:textSize=\"18sp\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test4.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_test4\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".module1.testactivity.Test4Activity\">\n\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test_module.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.alibaba.android.arouter.demo.service.com.alibaba.android.arouter.demo.module1.TestModuleActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"我是测试module1\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test_module2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.alibaba.android.arouter.demo.service.com.alibaba.android.arouter.demo.module1.TestModuleActivity\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"我是测试module2\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/layout/activity_test_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_test_webview\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.alibaba.android.arouter.demo.activity.TestWebview\">\n\n    <WebView\n        android:id=\"@+id/webview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"/>\n</RelativeLayout>\n"
  },
  {
    "path": "module-java/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "module-java/src/main/res/values/strings.xml",
    "content": "<resources></resources>\n"
  },
  {
    "path": "module-java-export/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\ndependencies {\n    implementation project(':arouter-annotation')\n    implementation project(':arouter-api')\n    implementation \"com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}\"\n    annotationProcessor project(':arouter-compiler')\n\n    implementation 'com.alibaba:fastjson:1.2.48'\n}\nandroid {\n    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)\n    buildToolsVersion BUILDTOOLS_VERSION\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)\n\n        javaCompileOptions {\n            annotationProcessorOptions {\n                arguments = [ AROUTER_MODULE_NAME : project.getName() ]\n            }\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    buildTypes {\n        release {\n            debuggable false\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n\n        lintOptions { abortOnError false }\n    }\n}"
  },
  {
    "path": "module-java-export/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.alibaba.android.arouter.demo.service\">\n</manifest>"
  },
  {
    "path": "module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/Entrance.java",
    "content": "package com.alibaba.android.arouter.demo.service;\n\nimport android.content.Context;\n\nimport com.alibaba.android.arouter.launcher.ARouter;\n\npublic class Entrance {\n    /**\n     * 跳转到 Test1 Activity,\n     *\n     * @param name    姓名\n     * @param age     年龄\n     * @param context ctx\n     */\n    public static void redirect2Test1Activity(String name, int age, Context context) {\n        ARouter.getInstance().build(\"/test/activity1\")\n                .withString(\"name\", name)\n                .withInt(\"age\", age)\n                .navigation(context);\n    }\n}\n"
  },
  {
    "path": "module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/HelloService.java",
    "content": "package com.alibaba.android.arouter.demo.service;\n\nimport com.alibaba.android.arouter.facade.template.IProvider;\n\n/**\n * 通过 service module 提供给使用方依赖，使用方可以不依赖具体实现，只需要保证最终打包在 app 中即可\n *\n * @author Alex <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/1/3 10:26\n */\npublic interface HelloService extends IProvider {\n    void sayHello(String name);\n}\n"
  },
  {
    "path": "module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestObj.java",
    "content": "package com.alibaba.android.arouter.demo.service.model;\n\n/**\n * TODO:Feature\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/3/16 下午4:42\n */\npublic class TestObj {\n    public String name;\n    public int id;\n\n    public TestObj() {\n    }\n\n    public TestObj(String name, int id) {\n        this.name = name;\n        this.id = id;\n    }\n}\n"
  },
  {
    "path": "module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestParcelable.java",
    "content": "package com.alibaba.android.arouter.demo.service.model;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * TODO:Feature\n *\n * @author zhilong <a href=\"mailto:zhilong.lzl@alibaba-inc.com\">Contact me.</a>\n * @version 1.0\n * @since 2017/3/16 下午4:42\n */\npublic class TestParcelable implements Parcelable {\n    public String name;\n    public int id;\n\n    public TestParcelable() {\n    }\n\n    public TestParcelable(String name, int id) {\n        this.name = name;\n        this.id = id;\n    }\n\n    protected TestParcelable(Parcel in) {\n        name = in.readString();\n        id = in.readInt();\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeString(name);\n        dest.writeInt(id);\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    public static final Creator<TestParcelable> CREATOR = new Creator<TestParcelable>() {\n        @Override\n        public TestParcelable createFromParcel(Parcel in) {\n            return new TestParcelable(in);\n        }\n\n        @Override\n        public TestParcelable[] newArray(int size) {\n            return new TestParcelable[size];\n        }\n    };\n}\n"
  },
  {
    "path": "module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestSerializable.java",
    "content": "package com.alibaba.android.arouter.demo.service.model;\n\nimport java.io.Serializable;\n\n/**\n * Created by @author joker on 2018/7/10.\n */\npublic class TestSerializable implements Serializable {\n    public String name;\n    public int id;\n\n    public TestSerializable() {\n    }\n\n    public TestSerializable(String name, int id) {\n        this.name = name;\n        this.id = id;\n    }\n}\n"
  },
  {
    "path": "module-kotlin/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\napply plugin: 'kotlin-android-extensions'\n\nkapt {\n    arguments {\n        arg(\"AROUTER_MODULE_NAME\", project.getName())\n    }\n}\n\ndependencies {\n    implementation project(':arouter-api')\n    implementation project(':arouter-annotation')\n    kapt project(':arouter-compiler')\n    implementation \"com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}\"\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation 'com.android.support.constraint:constraint-layout:1.1.3'\n}\nandroid {\n    compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)\n    buildToolsVersion BUILDTOOLS_VERSION\n\n    defaultConfig {\n        minSdkVersion Integer.parseInt(MIN_SDK_VERSION)\n        targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    buildTypes {\n        release {\n            debuggable false\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n\n        lintOptions { abortOnError false }\n    }\n}"
  },
  {
    "path": "module-kotlin/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.alibaba.android.arouter.demo.kotlin\">\n\n    <!-- 读写存储的权限 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n\n    <application>\n        <activity android:name=\".KotlinTestActivity\"/>\n        <activity android:name=\".TestNormalActivity\">\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "module-kotlin/src/main/java/com/alibaba/android/arouter/demo/kotlin/KotlinTestActivity.kt",
    "content": "package com.alibaba.android.arouter.demo.kotlin\n\nimport android.app.Activity\nimport android.os.Bundle\nimport com.alibaba.android.arouter.facade.annotation.Autowired\nimport com.alibaba.android.arouter.facade.annotation.Route\nimport com.alibaba.android.arouter.launcher.ARouter\nimport kotlinx.android.synthetic.main.activity_kotlin_test.*\n\n@Route(path = \"/kotlin/test\")\nclass KotlinTestActivity : Activity() {\n\n    @Autowired\n    @JvmField var name: String? = null\n    @Autowired\n    @JvmField var age: Int? = 0\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        ARouter.getInstance().inject(this)  // Start auto inject.\n\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_kotlin_test)\n\n        content.text = \"name = $name, age = $age\"\n    }\n}\n"
  },
  {
    "path": "module-kotlin/src/main/java/com/alibaba/android/arouter/demo/kotlin/TestNormalActivity.java",
    "content": "package com.alibaba.android.arouter.demo.kotlin;\n\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\n\nimport com.alibaba.android.arouter.facade.annotation.Route;\n\n@Route(path = \"/kotlin/java\")\npublic class TestNormalActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_test_normal);\n    }\n}\n"
  },
  {
    "path": "module-kotlin/src/main/res/layout/activity_kotlin_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".KotlinTestActivity\">\n\n    <TextView\n        android:id=\"@+id/content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:text=\"Im kotlin activity.\"/>\n</LinearLayout>\n"
  },
  {
    "path": "module-kotlin/src/main/res/layout/activity_test_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.constraint.ConstraintLayout\n    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    tools:context=\"com.alibaba.android.arouter.demo.kotlin.TestNormalActivity\">\n\n</android.support.constraint.ConstraintLayout>\n"
  },
  {
    "path": "module-kotlin/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "module-kotlin/src/main/res/values/strings.xml",
    "content": "<resources></resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\ninclude ':arouter-api'\ninclude ':arouter-compiler'\ninclude ':arouter-annotation'\ninclude ':module-java'\ninclude ':module-java-export'\ninclude ':module-kotlin'\ninclude ':arouter-gradle-plugin'\n//include ':arouter-idea-plugin'\n"
  }
]