├── .gitignore ├── LICENSE.txt ├── README.md ├── _config.yml ├── annotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── linked │ └── annotion │ ├── Action.java │ ├── Module.java │ ├── Modules.java │ └── Provider.java ├── build.gradle ├── compile ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── compiler │ └── ActionPrecessor.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── macore ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── spinytech │ │ └── macore │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── aidl │ │ └── com │ │ │ └── spinytech │ │ │ └── macore │ │ │ └── router │ │ │ ├── ILocalRouterAIDL.aidl │ │ │ ├── IWideRouterAIDL.aidl │ │ │ ├── MaActionResult.aidl │ │ │ └── RouterRequest.aidl │ ├── java │ │ └── com │ │ │ └── spinytech │ │ │ └── macore │ │ │ ├── ErrorAction.java │ │ │ ├── MaAction.java │ │ │ ├── MaApplication.java │ │ │ ├── MaProvider.java │ │ │ ├── Song.java │ │ │ ├── multiprocess │ │ │ ├── BaseApplicationLogic.java │ │ │ └── PriorityLogicWrapper.java │ │ │ ├── router │ │ │ ├── ConnectServiceWrapper.java │ │ │ ├── LocalRouter.java │ │ │ ├── LocalRouterConnectService.java │ │ │ ├── MaActionResult.java │ │ │ ├── RouterRequest.java │ │ │ ├── RouterRequestUtil.java │ │ │ ├── WideRouter.java │ │ │ ├── WideRouterApplicationLogic.java │ │ │ └── WideRouterConnectService.java │ │ │ └── tools │ │ │ ├── Logger.java │ │ │ ├── ProcessUtil.java │ │ │ └── RouterMessageUtil.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── spinytech │ └── macore │ └── ExampleUnitTest.java ├── maindemo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── spinytech │ │ └── maindemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── spinytech │ │ │ └── maindemo │ │ │ ├── AsyncAction.java │ │ │ ├── MainActivity.java │ │ │ ├── MainApplicationLogic.java │ │ │ ├── MainProvider.java │ │ │ ├── MainRouterConnectService.java │ │ │ ├── MyApplication.java │ │ │ ├── SyncAction.java │ │ │ └── ThreadPool.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── spinytech │ └── maindemo │ └── ExampleUnitTest.java ├── musicdemo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── spinytech │ │ └── musicdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── spinytech │ │ │ └── musicdemo │ │ │ ├── Music.java │ │ │ ├── MusicApplicationLogic.java │ │ │ ├── MusicProvider.java │ │ │ ├── MusicRouterConnectService.java │ │ │ ├── MusicService.java │ │ │ ├── PlayAction.java │ │ │ ├── ShutdownAction.java │ │ │ └── StopAction.java │ └── res │ │ ├── raw │ │ └── music.mp3 │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── spinytech │ └── musicdemo │ └── ExampleUnitTest.java ├── picdemo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── spinytech │ │ └── picdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── messi.png │ ├── java │ │ └── com │ │ │ └── spinytech │ │ │ └── picdemo │ │ │ ├── Date.java │ │ │ ├── PicAction.java │ │ │ ├── PicActivity.java │ │ │ ├── PicApplicationLogic.java │ │ │ ├── PicProvider.java │ │ │ └── PicRouterConnectService.java │ └── res │ │ ├── layout │ │ └── activity_pic.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── spinytech │ └── picdemo │ └── ExampleUnitTest.java ├── settings.gradle ├── stub ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── provider │ └── ProviderInit.java └── webdemo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── com │ └── spinytech │ └── webdemo │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── assets │ └── page.html ├── java │ └── com │ │ └── spinytech │ │ └── webdemo │ │ ├── WebAction.java │ │ ├── WebActivity.java │ │ ├── WebApplicationLogic.java │ │ └── WebProvider.java └── res │ ├── layout │ └── activity_web.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ └── strings.xml └── test └── java └── com └── spinytech └── webdemo └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .DS_Store 5 | /build 6 | /captures 7 | .externalNativeBuild 8 | /.idea -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 说明 2 | 从[SpinyTech/ModularizationArchitecture](https://github.com/SpinyTech/ModularizationArchitecture)fork的代码 3 | 4 | 关于架构说明可以参考 5 | [Android架构思考(模块化、多进程)](http://blog.spinytech.com/2016/12/28/android_modularization/) 6 | 7 | 关于这个fork版本增加了一些新的特性,方便使用,文档地址:[Android组件化之通信(多模块,多进程)](http://www.jianshu.com/p/1fc5f8a2d703) 8 | 9 | ## 此fork版本的特性 10 | ### 1 进程间通信数据格式的修改 11 | 请求数据```RouterResquest``` 和返回数据```MaActionResult```分别实现了Parcelable接口,并且分别提供了两个可以自定义的成员变量```requestObject```和```result```,用户在建立请求数据```RouterResquest``` 和返回数据```MaActionResult```可以把自定义的数据传递进去,需要注意的是传递的自定义类型也要实现Parcelable接口。 12 | ``` 13 | //自定义数据 14 | public class Song implements Parcelable { 15 | public String name; 16 | 17 | public Song(String name) { 18 | this.name = name; 19 | } 20 | 21 | protected Song(Parcel in) { 22 | name = in.readString(); 23 | } 24 | 25 | @Override 26 | public void writeToParcel(Parcel dest, int flags) { 27 | dest.writeString(name); 28 | } 29 | 30 | @Override 31 | public int describeContents() { 32 | return 0; 33 | } 34 | 35 | public static final Creator CREATOR = new Creator() { 36 | @Override 37 | public Song createFromParcel(Parcel in) { 38 | return new Song(in); 39 | } 40 | 41 | @Override 42 | public Song[] newArray(int size) { 43 | return new Song[size]; 44 | } 45 | }; 46 | } 47 | ``` 48 | 49 | ``` 50 | //RouterResquest中设置了自定义类型Song 51 | RouterRequestUtil.obtain(MainActivity.this) 52 | .domain("com.spinytech.maindemo:music") 53 | .provider("music") 54 | .action("play") 55 | .reqeustObject(new Song("see you")) 56 | ) 57 | //MaActionResult中设置自定义类型Song 58 | MaActionResult result = new MaActionResult.Builder() 59 | .code(MaActionResult.CODE_SUCCESS) 60 | .msg("play success") 61 | .result(new Song("lili")) 62 | .build(); 63 | ``` 64 | ### 2 Provider、Action自动生成 65 | ``` 66 | // 注解Provider,程序运行后将自动注册MusicProvider到com.spinytech.maindemo:music到Router 67 | @Provider(processName = "com.spinytech.maindemo:music") 68 | public class MusicProvider extends MaProvider{ 69 | @Override 70 | protected String getName() { 71 | return "music"; 72 | } 73 | } 74 | // 注解Action,程序运行后将自动注册PlayAction到Provider 75 | @Action(processName = "com.spinytech.maindemo:music", providerName = "music") 76 | public class PlayAction implements MaAction 77 | ``` 78 | ### 3 Rxjava 的引入 79 | 引入Rxjava之后,修改LocalRoute的route方法,使之返回```Observable```,在调用时可以非常方便地使用Rxjava切换线程: 80 | ``` 81 | LocalRouter.getInstance(MaApplication.getMaApplication()) 82 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 83 | .domain("com.spinytech.maindemo:pic") 84 | .provider("pic") 85 | .action("pic") 86 | .data("is_big", "0")) 87 | .observeOn(AndroidSchedulers.mainThread()) 88 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 89 | .subscribe(new Consumer() { 90 | @Override 91 | public void accept(MaActionResult maActionResult) throws Exception { 92 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 93 | } 94 | }, new Consumer() { 95 | @Override 96 | public void accept(Throwable throwable) throws Exception { 97 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 98 | } 99 | }); 100 | ``` 101 | 102 | 103 | ## 使用教程 104 | 项目地址:[https://github.com/wutongke/ModularizationArchitecture](https://github.com/wutongke/ModularizationArchitecture) 105 | ### 1 在项目中集成 106 | 1.1 在project的build.gradle中设置dependencies块中支持apt: 107 | ``` 108 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 109 | ``` 110 | 1.2 所有Module中配置apt插件: 111 | ``` 112 | apply plugin: 'com.neenbedankt.android-apt' 113 | ``` 114 | dependencies块中设置: 115 | ``` 116 | apt 'com.github.wutongke.modularization:compile:1.1.1' 117 | compile 'com.github.wutongke.modularization:macore:1.1.1' 118 | ``` 119 | ### 2 创建自定义Application 120 | 2.1 实际Application 121 | 我们知道一个app中只有一个Application,所以在主Module中定义Application,然后在其它模块中根据需要实现逻辑Application即可,然后启动时注册逻辑Application,即可管理其生命周期: 122 | ``` 123 | public class MyApplication extends MaApplication { 124 | 125 | //多进程中注册各个进程的Router,可以参考第3小节的原理图 126 | @Override 127 | public void initializeAllProcessRouter() { 128 | WideRouter.registerLocalRouter("com.spinytech.maindemo",MainRouterConnectService.class); 129 | WideRouter.registerLocalRouter("com.spinytech.maindemo:music",MusicRouterConnectService.class); 130 | WideRouter.registerLocalRouter("com.spinytech.maindemo:pic",PicRouterConnectService.class); 131 | } 132 | 133 | //注册各个模块中的逻辑Application,每个模块中可以注册多个逻辑 134 | //Applicatoin,设置优先级,可以调整模块中多个逻辑Application的 135 | //调用顺序 136 | @Override 137 | protected void initializeLogic() { 138 | registerApplicationLogic("com.spinytech.maindemo",999, MainApplicationLogic.class); 139 | registerApplicationLogic("com.spinytech.maindemo",998, WebApplicationLogic.class); 140 | registerApplicationLogic("com.spinytech.maindemo:music",999, MusicApplicationLogic.class); 141 | registerApplicationLogic("com.spinytech.maindemo:pic",999, PicApplicationLogic.class); 142 | } 143 | 144 | //设置是否支持多进程 145 | @Override 146 | public boolean needMultipleProcess() { 147 | return true; 148 | } 149 | } 150 | ``` 151 | 当然这个自定义的Application需要注册到manifest文件中。 152 | 使用多进程提供服务的模块需要继承```LocalRouterConnectService```,并且在manifest中注册服务: 153 | ``` 154 | public class MusicRouterConnectService extends LocalRouterConnectService { 155 | @Override 156 | public boolean onUnbind(Intent intent) { 157 | Log.e("MRCS","onUnbind"); 158 | return super.onUnbind(intent); 159 | } 160 | 161 | @Override 162 | public void onDestroy() { 163 | super.onDestroy(); 164 | Log.e("MRCS","onDestroy"); 165 | } 166 | } 167 | ``` 168 | ``` 169 | 171 | ``` 172 | 173 | 174 | 2.2 逻辑Application 175 | 逻辑Application通过继承BaseApplicationLogic,实现相应的方法即可被回调。 176 | ``` 177 | public class BaseApplicationLogic { 178 | protected MaApplication mApplication; 179 | public BaseApplicationLogic() { 180 | } 181 | 182 | public void setApplication(@NonNull MaApplication application) { 183 | mApplication = application; 184 | } 185 | 186 | public void onCreate() { 187 | } 188 | 189 | public void onTerminate() { 190 | } 191 | 192 | public void onLowMemory() { 193 | } 194 | 195 | public void onTrimMemory(int level) { 196 | } 197 | 198 | public void onConfigurationChanged(Configuration newConfig) { 199 | } 200 | } 201 | 202 | //逻辑Application只需要继承BaseApplicationLogic,注册后 203 | //生命周期会被回调 204 | public class MainApplicationLogic extends BaseApplicationLogic { 205 | @Override 206 | public void onCreate() { 207 | super.onCreate(); 208 | } 209 | } 210 | ``` 211 | 212 | ### 3 自定义Provider和Action 213 | 定义Provider 214 | ``` 215 | @Provider(processName = "com.spinytech.maindemo:music") 216 | public class MusicProvider extends MaProvider{ 217 | @Override 218 | protected String getName() { 219 | return "music"; 220 | } 221 | } 222 | ``` 223 | 定义Action 224 | ``` 225 | @Action(processName = "com.spinytech.maindemo:music", providerName = "music") 226 | public class PlayAction implements MaAction { 227 | 228 | @Override 229 | public boolean isAsync(Context context, RouterRequest requestData) { 230 | return false; 231 | } 232 | 233 | @Override 234 | public MaActionResult invoke(final Context context, final RouterRequest requestData) { 235 | Intent intent = new Intent(context, MusicService.class); 236 | intent.putExtra("command", "play"); 237 | context.startService(intent); 238 | MaActionResult result = new MaActionResult.Builder() 239 | .code(MaActionResult.CODE_SUCCESS) 240 | .msg("play success") 241 | .result(new Song("lili")) 242 | .build(); 243 | Handler handler = new Handler(context.getMainLooper()); 244 | handler.post(new Runnable() { 245 | @Override 246 | public void run() { 247 | if (requestData != null && requestData.getRequestObject() != null) { 248 | Toast.makeText(context, "歌曲名字:" + requestData.getRequestObject().name + "(并不知道)", Toast.LENGTH_SHORT).show(); 249 | } 250 | } 251 | }); 252 | Logger.d("com.spinytech", requestData.getRequestObject().name); 253 | return result; 254 | } 255 | 256 | @Override 257 | public String getName() { 258 | return "play"; 259 | } 260 | } 261 | ``` 262 | 可以看到定义Provider和Action时分别使用了```@Provider``` 和```@Action``` 注解,这样可以在程序编译时完成自动的注册,不需要手动注册到Router了。 263 | 264 | 其中 ```@Provider```需要设置进程名字,```@Action``` 需要设置进程名字和注册到的Provider名字: 265 | ``` 266 | @Provider(processName = "com.spinytech.maindemo:music") 267 | @Action(processName = "com.spinytech.maindemo:music", providerName = "music") 268 | ``` 269 | ### 4 调用Action 270 | 4.1 建立Action调用 271 | 首先需求建立一个请求```RouterRequest```,说明要请求的内容: 272 | ``` 273 | RouterRequestUtil.obtain(MainActivity.this) 274 | .domain("com.spinytech.maindemo:music") 275 | .provider("music") 276 | .action("play") 277 | .reqeustObject(new Song("see you")) 278 | ``` 279 | 可以通过RouterRequestUtil的obtain方法快速建立请求,上例中请求的Action位于"com.spinytech.maindemo:music"进程,Provider是"music",Action是"play",并且传递了相应的参数new Song("see you")。 280 | 281 | 然后使用Rxjava的方式请求Action: 282 | ``` 283 | LocalRouter.getInstance(MaApplication.getMaApplication()) 284 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 285 | .domain("com.spinytech.maindemo:music") 286 | .provider("music") 287 | .action("play") 288 | .reqeustObject(new Song("see you")) 289 | ) 290 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 291 | .observeOn(AndroidSchedulers.mainThread()) 292 | .subscribe(new Consumer() { 293 | @Override 294 | public void accept(MaActionResult maActionResult) throws Exception { 295 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 296 | } 297 | }, new Consumer() { 298 | @Override 299 | public void accept(Throwable throwable) throws Exception { 300 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 301 | } 302 | }); 303 | ``` 304 | 305 | 4.2 处理请求 306 | 在music模块中处理刚刚发出的请求,6.3中定义的Provider和Action其实就是处理6.4.1中的请求的,并且返回了```MaActionResult```: 307 | ``` 308 | MaActionResult result = new MaActionResult.Builder() 309 | .code(MaActionResult.CODE_SUCCESS) 310 | .msg("play success") 311 | .result(new Song("lili")) 312 | .build(); 313 | ``` 314 | 315 | ## 其它 316 | 注意:在demo的gradle.properties中可以配置Local属性,从而根据需要设置使用本地的library,还是远端的library,更改Local后注意sync。 317 | ## License 318 | 319 | 320 | Licensed under the Apache License, Version 2.0 (the "License"); 321 | you may not use this file except in compliance with the License. 322 | You may obtain a copy of the License at 323 | 324 | http://www.apache.org/licenses/LICENSE-2.0 325 | 326 | Unless required by applicable law or agreed to in writing, software 327 | distributed under the License is distributed on an "AS IS" BASIS, 328 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 329 | See the License for the specific language governing permissions and 330 | limitations under the License. 331 | 332 | 333 | 334 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | sourceCompatibility = "1.7" 8 | targetCompatibility = "1.7" 9 | 10 | 11 | //for upload 12 | ext { 13 | bintrayRepo = 'maven' 14 | bintrayName = 'modularization-architecture-annotation' 15 | 16 | publishedGroupId = 'com.github.wutongke.modularization' 17 | libraryName = 'Annotation' 18 | artifact = 'annotation' 19 | 20 | libraryDescription = 'Modularization architecture' 21 | 22 | siteUrl = 'https://github.com/wutongke/ModularizationArchitecture' 23 | gitUrl = 'https://github.com/wutongke/ModularizationArchitecture.git' 24 | 25 | libraryVersion = '1.1.1' 26 | 27 | developerId = 'wutongke' 28 | developerName = 'wutongke' 29 | developerEmail = 'ximu470791413@163.com' 30 | 31 | licenseName = 'The Apache Software License, Version 2.0' 32 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 33 | allLicenses = ["Apache-2.0"] 34 | } 35 | 36 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 37 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /annotation/src/main/java/com/linked/annotion/Action.java: -------------------------------------------------------------------------------- 1 | package com.linked.annotion; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Created by erfli on 2/15/17. 8 | */ 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Action { 11 | String processName(); 12 | String providerName(); 13 | } 14 | -------------------------------------------------------------------------------- /annotation/src/main/java/com/linked/annotion/Module.java: -------------------------------------------------------------------------------- 1 | package com.linked.annotion; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Created by erfli on 2/15/17. 8 | */ 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Module { 11 | String name(); 12 | } 13 | -------------------------------------------------------------------------------- /annotation/src/main/java/com/linked/annotion/Modules.java: -------------------------------------------------------------------------------- 1 | package com.linked.annotion; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Created by erfli on 2/15/17. 8 | */ 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Modules { 11 | String[] modules(); 12 | } 13 | -------------------------------------------------------------------------------- /annotation/src/main/java/com/linked/annotion/Provider.java: -------------------------------------------------------------------------------- 1 | package com.linked.annotion; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * Created by erfli on 2/15/17. 8 | */ 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface Provider { 11 | String processName(); 12 | } 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /compile/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /compile/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(include: ['*.jar'], dir: 'libs') 5 | compile 'com.google.auto.service:auto-service:1.0-rc2' 6 | compile 'com.squareup:javapoet:1.6.1' 7 | compile project(':annotation') 8 | } 9 | 10 | sourceCompatibility = "1.7" 11 | targetCompatibility = "1.7" 12 | 13 | ext { 14 | bintrayRepo = 'maven' 15 | bintrayName = 'modularization-compiler' 16 | 17 | publishedGroupId = 'com.github.wutongke.modularization' 18 | libraryName = 'Compile' 19 | artifact = 'compile' 20 | 21 | libraryDescription = 'Modularization architecture' 22 | 23 | siteUrl = 'https://github.com/wutongke/ModularizationArchitecture' 24 | gitUrl = 'https://github.com/wutongke/ModularizationArchitecture.git' 25 | 26 | libraryVersion = '1.1.1' 27 | 28 | developerId = 'wutongke' 29 | developerName = 'wutongke' 30 | developerEmail = 'ximu470791413@163.com' 31 | 32 | licenseName = 'The Apache Software License, Version 2.0' 33 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 34 | allLicenses = ["Apache-2.0"] 35 | } 36 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 37 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /compile/src/main/java/com/compiler/ActionPrecessor.java: -------------------------------------------------------------------------------- 1 | package com.compiler; 2 | 3 | import com.google.auto.service.AutoService; 4 | import com.linked.annotion.Action; 5 | import com.linked.annotion.Module; 6 | import com.linked.annotion.Modules; 7 | import com.linked.annotion.Provider; 8 | import com.squareup.javapoet.ClassName; 9 | import com.squareup.javapoet.JavaFile; 10 | import com.squareup.javapoet.MethodSpec; 11 | import com.squareup.javapoet.TypeName; 12 | import com.squareup.javapoet.TypeSpec; 13 | 14 | import java.util.HashMap; 15 | import java.util.HashSet; 16 | import java.util.Set; 17 | 18 | import javax.annotation.processing.AbstractProcessor; 19 | import javax.annotation.processing.Filer; 20 | import javax.annotation.processing.Messager; 21 | import javax.annotation.processing.ProcessingEnvironment; 22 | import javax.annotation.processing.Processor; 23 | import javax.annotation.processing.RoundEnvironment; 24 | import javax.lang.model.SourceVersion; 25 | import javax.lang.model.element.Element; 26 | import javax.lang.model.element.ElementKind; 27 | import javax.lang.model.element.Modifier; 28 | import javax.lang.model.element.TypeElement; 29 | import javax.tools.Diagnostic; 30 | 31 | @AutoService(Processor.class) 32 | public class ActionPrecessor extends AbstractProcessor { 33 | private Filer filer; 34 | private Messager messager; 35 | 36 | @Override 37 | public Set getSupportedAnnotationTypes() { 38 | Set supportTypes = new HashSet<>(); 39 | supportTypes.add(Action.class.getCanonicalName()); 40 | supportTypes.add(Provider.class.getCanonicalName()); 41 | return supportTypes; 42 | } 43 | 44 | @Override 45 | public synchronized void init(ProcessingEnvironment processingEnv) { 46 | super.init(processingEnv); 47 | filer = processingEnv.getFiler(); 48 | messager = processingEnv.getMessager(); 49 | } 50 | 51 | @Override 52 | public SourceVersion getSupportedSourceVersion() { 53 | return SourceVersion.latestSupported(); 54 | } 55 | 56 | @Override 57 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 58 | if (annotations.isEmpty()) { 59 | return false; 60 | } else { 61 | Set provideList = roundEnv.getElementsAnnotatedWith(Provider.class); 62 | Set actionList = roundEnv.getElementsAnnotatedWith(Action.class); 63 | if (provideList.isEmpty() || actionList.isEmpty()) { 64 | return false; 65 | } else { 66 | Set modulesList = roundEnv.getElementsAnnotatedWith(Modules.class); 67 | Set moduleList = roundEnv.getElementsAnnotatedWith(Module.class); 68 | String aptModuleName = "ProviderMappingInit"; 69 | if (!modulesList.isEmpty()) { 70 | Element element = modulesList.iterator().next(); 71 | Modules modules = element.getAnnotation(Modules.class); 72 | generateModulesProviderMappingInit(modules.modules()); 73 | } else if (moduleList.isEmpty()) { 74 | generateDefaultProviderMappingInit(); 75 | } else if (moduleList.size() > 1) { 76 | throw new IllegalArgumentException("one Modules annotation is enough"); 77 | } 78 | 79 | if (!moduleList.isEmpty()) { 80 | Element element = moduleList.iterator().next(); 81 | Module modules = element.getAnnotation(Module.class); 82 | aptModuleName += "_" + modules.name(); 83 | } 84 | generateProviderMapping(aptModuleName, roundEnv); 85 | return true; 86 | } 87 | } 88 | } 89 | 90 | private void generateDefaultProviderMappingInit() { 91 | debug("generateDefaultProviderMappingInit"); 92 | MethodSpec.Builder initBuilder = MethodSpec.methodBuilder("init") 93 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 94 | .returns(TypeName.VOID) 95 | .addParameter(HashMap.class, "providerMap") 96 | .addParameter(HashMap.class, "actionMap"); 97 | initBuilder.addStatement("ProviderMappingInit.init(providerMap, actionMap)"); 98 | TypeSpec providerInit = TypeSpec.classBuilder("ProviderInit") 99 | .addModifiers(Modifier.PUBLIC) 100 | .addMethod(initBuilder.build()) 101 | .build(); 102 | try { 103 | JavaFile.builder("com.provider", providerInit) 104 | .build() 105 | .writeTo(filer); 106 | } catch (Throwable e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | 111 | private void generateModulesProviderMappingInit(String[] modules) { 112 | MethodSpec.Builder initBuilder = MethodSpec.methodBuilder("init") 113 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 114 | .returns(TypeName.VOID) 115 | .addParameter(HashMap.class, "providerMap") 116 | .addParameter(HashMap.class, "actionMap") 117 | .addStatement("ProviderMappingInit.init(providerMap, actionMap)"); 118 | for (String moduleName : modules) { 119 | initBuilder.addStatement("ProviderMappingInit_" + moduleName + ".init(providerMap, actionMap)"); 120 | } 121 | TypeSpec providerInit = TypeSpec.classBuilder("ProviderInit") 122 | .addModifiers(Modifier.PUBLIC) 123 | .addMethod(initBuilder.build()) 124 | .build(); 125 | try { 126 | JavaFile.builder("com.provider", providerInit) 127 | .build() 128 | .writeTo(filer); 129 | } catch (Throwable e) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | 134 | private void generateProviderMapping(String fileName, RoundEnvironment roundEnv) { 135 | Set provideList = roundEnv.getElementsAnnotatedWith(Provider.class); 136 | Set actionList = roundEnv.getElementsAnnotatedWith(Action.class); 137 | MethodSpec.Builder initBuilder = MethodSpec.methodBuilder("init") 138 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 139 | .returns(TypeName.VOID) 140 | .addParameter(HashMap.class, "providerMap") 141 | .addParameter(HashMap.class, "actionMap"); 142 | ClassName arrayList = ClassName.get("java.util", "ArrayList"); 143 | for (Element element : provideList) { 144 | if (element.getKind() == ElementKind.CLASS) { 145 | ClassName providerClassName = ClassName.get((TypeElement) element); 146 | Provider provider = element.getAnnotation(Provider.class); 147 | initBuilder.addStatement("$T $N = new $T()", providerClassName, providerClassName.simpleName().toLowerCase(), providerClassName); 148 | initBuilder.addCode("if( providerMap.get($S) == null ){\n", provider.processName()); 149 | initBuilder.addCode("providerMap.put($S, new $T());\n}\n", provider.processName(), arrayList); 150 | initBuilder.addCode("(($T)providerMap.get($S)).add($N);\n", arrayList, provider.processName(), providerClassName.simpleName().toLowerCase()); 151 | } 152 | } 153 | 154 | for (Element element : actionList) { 155 | if (element.getKind() == ElementKind.CLASS) { 156 | Action action = element.getAnnotation(Action.class); 157 | ClassName actionClassName = ClassName.get((TypeElement) element); 158 | initBuilder.addStatement("$T $N = new $T()", actionClassName, actionClassName.simpleName().toLowerCase(), actionClassName); 159 | String key = action.processName() + "_" + action.providerName(); 160 | initBuilder.addCode("if(actionMap.get($S) == null ){\n", key); 161 | initBuilder.addCode("actionMap.put($S, new $T());\n}\n", key, arrayList); 162 | initBuilder.addCode("(($T)actionMap.get($S)).add($N);\n", arrayList, key, actionClassName.simpleName().toLowerCase()); 163 | } 164 | } 165 | TypeSpec providerInit = TypeSpec.classBuilder(fileName) 166 | .addModifiers(Modifier.PUBLIC) 167 | .addMethod(initBuilder.build()) 168 | .build(); 169 | try { 170 | JavaFile.builder("com.provider", providerInit) 171 | .build() 172 | .writeTo(filer); 173 | } catch (Throwable e) { 174 | e.printStackTrace(); 175 | } 176 | } 177 | 178 | private void debug(String msg) { 179 | messager.printMessage(Diagnostic.Kind.NOTE, msg); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | Local = true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wutongke/ModularizationArchitecture/da5f88807b370fe4702f1c03a21d7694889705f1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /macore/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /macore/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.0" 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion 25 10 | versionCode 2017011300 11 | versionName "v0.1.2" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | compile fileTree(include: ['*.jar'], dir: 'libs') 26 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 27 | exclude group: 'com.android.support', module: 'support-annotations' 28 | }) 29 | testCompile 'junit:junit:4.12' 30 | compile 'com.android.support:appcompat-v7:25.+' 31 | provided project(':stub') 32 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 33 | compile 'io.reactivex.rxjava2:rxjava:2.0.1' 34 | if (Local.toBoolean()){ 35 | compile project(':annotation') 36 | }else{ 37 | compile 'com.github.wutongke.modularization:annotation:1.0.0' 38 | } 39 | } 40 | 41 | ext { 42 | bintrayRepo = 'maven' 43 | bintrayName = 'modularization-architecture' 44 | 45 | publishedGroupId = 'com.github.wutongke.modularization' 46 | libraryName = 'Modularization' 47 | artifact = 'macore' 48 | 49 | libraryDescription = 'Modularization architecture' 50 | 51 | siteUrl = 'https://github.com/wutongke/ModularizationArchitecture' 52 | gitUrl = 'https://github.com/wutongke/ModularizationArchitecture.git' 53 | 54 | libraryVersion = '1.1.1' 55 | 56 | developerId = 'wutongke' 57 | developerName = 'wutongke' 58 | developerEmail = 'ximu470791413@163.com' 59 | 60 | licenseName = 'The Apache Software License, Version 2.0' 61 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 62 | allLicenses = ["Apache-2.0"] 63 | } 64 | 65 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 66 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' 67 | -------------------------------------------------------------------------------- /macore/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wanglei/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /macore/src/androidTest/java/com/spinytech/macore/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.spinytech.macore.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /macore/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /macore/src/main/aidl/com/spinytech/macore/router/ILocalRouterAIDL.aidl: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | import com.spinytech.macore.router.MaActionResult; 3 | import com.spinytech.macore.router.RouterRequest; 4 | 5 | interface ILocalRouterAIDL { 6 | boolean checkResponseAsync(in RouterRequest routerRequset); 7 | MaActionResult route(in RouterRequest routerRequest); 8 | boolean stopWideRouter(); 9 | void connectWideRouter(); 10 | } 11 | -------------------------------------------------------------------------------- /macore/src/main/aidl/com/spinytech/macore/router/IWideRouterAIDL.aidl: -------------------------------------------------------------------------------- 1 | // IRouterAIDL.aidl 2 | package com.spinytech.macore.router; 3 | import com.spinytech.macore.router.MaActionResult; 4 | import com.spinytech.macore.router.RouterRequest; 5 | // Declare any non-default types here with import statements 6 | 7 | interface IWideRouterAIDL { 8 | boolean checkResponseAsync(String domain,in RouterRequest routerRequset); 9 | MaActionResult route(String domain,in RouterRequest routerRequest); 10 | boolean stopRouter(String domain); 11 | } 12 | -------------------------------------------------------------------------------- /macore/src/main/aidl/com/spinytech/macore/router/MaActionResult.aidl: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | // Declare any non-default types here with import statements 3 | parcelable MaActionResult; 4 | -------------------------------------------------------------------------------- /macore/src/main/aidl/com/spinytech/macore/router/RouterRequest.aidl: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | // Declare any non-default types here with import statements 3 | parcelable RouterRequest; 4 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/ErrorAction.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import android.content.Context; 4 | 5 | import com.spinytech.macore.router.MaActionResult; 6 | import com.spinytech.macore.router.RouterRequest; 7 | 8 | /** 9 | * Created by wanglei on 2016/12/28. 10 | */ 11 | 12 | public class ErrorAction implements MaAction { 13 | 14 | private static final String DEFAULT_MESSAGE = "Something was really wrong. Ha ha!"; 15 | private int mCode; 16 | private String mMessage; 17 | private boolean mAsync; 18 | public ErrorAction() { 19 | mCode = MaActionResult.CODE_ERROR; 20 | mMessage = DEFAULT_MESSAGE; 21 | mAsync = false; 22 | } 23 | 24 | public ErrorAction(boolean isAsync,int code, String message) { 25 | this.mCode = code; 26 | this.mMessage = message; 27 | this.mAsync = isAsync; 28 | } 29 | 30 | @Override 31 | public boolean isAsync(Context context, RouterRequest requestData) { 32 | return mAsync; 33 | } 34 | 35 | @Override 36 | public MaActionResult invoke(Context context, RouterRequest requestData) { 37 | MaActionResult result = new MaActionResult.Builder() 38 | .code(mCode) 39 | .msg(mMessage) 40 | .data(null) 41 | .result(null) 42 | .build(); 43 | return result; 44 | } 45 | 46 | @Override 47 | public String getName() { 48 | return "error"; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/MaAction.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import android.content.Context; 4 | 5 | import com.spinytech.macore.router.MaActionResult; 6 | import com.spinytech.macore.router.RouterRequest; 7 | 8 | /** 9 | * Created by wanglei on 2016/11/29. 10 | */ 11 | 12 | public interface MaAction { 13 | boolean isAsync(Context context, RouterRequest routerRequest); 14 | 15 | MaActionResult invoke(Context context, RouterRequest routerRequest); 16 | 17 | String getName(); 18 | } 19 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/MaApplication.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import android.app.Application; 4 | import android.content.Intent; 5 | import android.content.res.Configuration; 6 | import android.support.annotation.CallSuper; 7 | import android.support.annotation.NonNull; 8 | import android.util.Log; 9 | 10 | import com.provider.ProviderInit; 11 | import com.spinytech.macore.multiprocess.BaseApplicationLogic; 12 | import com.spinytech.macore.multiprocess.PriorityLogicWrapper; 13 | import com.spinytech.macore.router.LocalRouter; 14 | import com.spinytech.macore.router.WideRouter; 15 | import com.spinytech.macore.router.WideRouterApplicationLogic; 16 | import com.spinytech.macore.router.WideRouterConnectService; 17 | import com.spinytech.macore.tools.Logger; 18 | import com.spinytech.macore.tools.ProcessUtil; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | 24 | /** 25 | * Created by wanglei on 2016/11/25. 26 | */ 27 | 28 | public abstract class MaApplication extends Application { 29 | private static final String TAG = "MaApplication"; 30 | private static MaApplication sInstance; 31 | private ArrayList mLogicList; 32 | private HashMap> mLogicClassMap; 33 | private HashMap> mProviderMap; 34 | private HashMap> mActionMap; 35 | 36 | @CallSuper 37 | @Override 38 | public void onCreate() { 39 | super.onCreate(); 40 | sInstance = this; 41 | Logger.d(TAG, "Application onCreate start: " + System.currentTimeMillis()); 42 | init(); 43 | startWideRouter(); 44 | initializeLogic(); 45 | dispatchLogic(); 46 | instantiateLogic(); 47 | 48 | // Traverse the application logic. 49 | if (null != mLogicList && mLogicList.size() > 0) { 50 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 51 | if (null != priorityLogicWrapper && null != priorityLogicWrapper.instance) { 52 | priorityLogicWrapper.instance.onCreate(); 53 | String precessName = ProcessUtil.getProcessName(this, ProcessUtil.getMyProcessId()); 54 | Logger.d(TAG, precessName); 55 | if(mProviderMap.get(precessName)!= null){ 56 | for (MaProvider maProvider : mProviderMap.get(precessName)) { 57 | LocalRouter.getInstance(this).registerProvider(maProvider.getName(), maProvider); 58 | for (MaAction maAction : mActionMap.get(precessName + "_" + maProvider.getName())) { 59 | maProvider.registerAction(maAction.getName(), maAction); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | Logger.d(TAG, "Application onCreate end: " + System.currentTimeMillis()); 67 | } 68 | 69 | private void init() { 70 | LocalRouter.getInstance(this); 71 | mLogicClassMap = new HashMap<>(); 72 | mProviderMap = new HashMap<>(); 73 | mActionMap = new HashMap<>(); 74 | ProviderInit.init(mProviderMap, mActionMap); 75 | } 76 | 77 | protected void startWideRouter() { 78 | if (needMultipleProcess()) { 79 | registerApplicationLogic(WideRouter.PROCESS_NAME, 1000, WideRouterApplicationLogic.class); 80 | Intent intent = new Intent(this, WideRouterConnectService.class); 81 | startService(intent); 82 | } 83 | } 84 | 85 | public abstract void initializeAllProcessRouter(); 86 | 87 | protected abstract void initializeLogic(); 88 | 89 | public abstract boolean needMultipleProcess(); 90 | 91 | protected boolean registerApplicationLogic(String processName, int priority, @NonNull Class logicClass) { 92 | boolean result = false; 93 | if (null != mLogicClassMap) { 94 | ArrayList tempList = mLogicClassMap.get(processName); 95 | if (null == tempList) { 96 | tempList = new ArrayList<>(); 97 | mLogicClassMap.put(processName, tempList); 98 | } 99 | if (tempList.size() > 0) { 100 | for (PriorityLogicWrapper priorityLogicWrapper : tempList) { 101 | if (logicClass.getName().equals(priorityLogicWrapper.logicClass.getName())) { 102 | throw new RuntimeException(logicClass.getName() + " has registered."); 103 | } 104 | } 105 | } 106 | PriorityLogicWrapper priorityLogicWrapper = new PriorityLogicWrapper(priority, logicClass); 107 | tempList.add(priorityLogicWrapper); 108 | } 109 | return result; 110 | } 111 | 112 | private void dispatchLogic() { 113 | if (null != mLogicClassMap) { 114 | mLogicList = mLogicClassMap.get(ProcessUtil.getProcessName(this, ProcessUtil.getMyProcessId())); 115 | } 116 | } 117 | 118 | private void instantiateLogic() { 119 | if (null != mLogicList && mLogicList.size() > 0) { 120 | if (null != mLogicList && mLogicList.size() > 0) { 121 | Collections.sort(mLogicList); 122 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 123 | if (null != priorityLogicWrapper) { 124 | try { 125 | priorityLogicWrapper.instance = priorityLogicWrapper.logicClass.newInstance(); 126 | } catch (InstantiationException e) { 127 | e.printStackTrace(); 128 | } catch (IllegalAccessException e) { 129 | e.printStackTrace(); 130 | } 131 | if (null != priorityLogicWrapper.instance) { 132 | priorityLogicWrapper.instance.setApplication(this); 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | 140 | @Override 141 | public void onTerminate() { 142 | super.onTerminate(); 143 | if (null != mLogicList && mLogicList.size() > 0) { 144 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 145 | if (null != priorityLogicWrapper && null != priorityLogicWrapper.instance) { 146 | priorityLogicWrapper.instance.onTerminate(); 147 | } 148 | } 149 | } 150 | } 151 | 152 | @Override 153 | public void onLowMemory() { 154 | super.onLowMemory(); 155 | if (null != mLogicList && mLogicList.size() > 0) { 156 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 157 | if (null != priorityLogicWrapper && null != priorityLogicWrapper.instance) { 158 | priorityLogicWrapper.instance.onLowMemory(); 159 | } 160 | } 161 | } 162 | } 163 | 164 | @Override 165 | public void onTrimMemory(int level) { 166 | super.onTrimMemory(level); 167 | if (null != mLogicList && mLogicList.size() > 0) { 168 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 169 | if (null != priorityLogicWrapper && null != priorityLogicWrapper.instance) { 170 | priorityLogicWrapper.instance.onTrimMemory(level); 171 | } 172 | } 173 | } 174 | } 175 | 176 | @Override 177 | public void onConfigurationChanged(Configuration newConfig) { 178 | super.onConfigurationChanged(newConfig); 179 | if (null != mLogicList && mLogicList.size() > 0) { 180 | for (PriorityLogicWrapper priorityLogicWrapper : mLogicList) { 181 | if (null != priorityLogicWrapper && null != priorityLogicWrapper.instance) { 182 | priorityLogicWrapper.instance.onConfigurationChanged(newConfig); 183 | } 184 | } 185 | } 186 | } 187 | 188 | public static MaApplication getMaApplication() { 189 | return sInstance; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/MaProvider.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * Created by wanglei on 2016/11/29. 7 | */ 8 | 9 | public abstract class MaProvider{ 10 | //TODO this field is used for control the provider on and off 11 | private boolean mValid = true; 12 | private HashMap mActions; 13 | public MaProvider(){ 14 | mActions = new HashMap<>(); 15 | } 16 | protected void registerAction(String actionName,MaAction action){ 17 | mActions.put(actionName,action); 18 | } 19 | 20 | public MaAction findAction(String actionName){ 21 | return mActions.get(actionName); 22 | } 23 | 24 | public boolean isValid(){ 25 | return mValid; 26 | } 27 | 28 | protected abstract String getName(); 29 | } 30 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/Song.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by erfli on 2/14/17. 8 | */ 9 | 10 | public class Song implements Parcelable { 11 | public String name; 12 | 13 | public Song(String name) { 14 | this.name = name; 15 | } 16 | 17 | protected Song(Parcel in) { 18 | name = in.readString(); 19 | } 20 | 21 | @Override 22 | public void writeToParcel(Parcel dest, int flags) { 23 | dest.writeString(name); 24 | } 25 | 26 | @Override 27 | public int describeContents() { 28 | return 0; 29 | } 30 | 31 | public static final Creator CREATOR = new Creator() { 32 | @Override 33 | public Song createFromParcel(Parcel in) { 34 | return new Song(in); 35 | } 36 | 37 | @Override 38 | public Song[] newArray(int size) { 39 | return new Song[size]; 40 | } 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/multiprocess/BaseApplicationLogic.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.multiprocess; 2 | 3 | import android.content.res.Configuration; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.spinytech.macore.MaApplication; 7 | 8 | /** 9 | * Created by wanglei on 2016/11/25. 10 | */ 11 | 12 | public class BaseApplicationLogic { 13 | protected MaApplication mApplication; 14 | public BaseApplicationLogic() { 15 | } 16 | 17 | public void setApplication(@NonNull MaApplication application) { 18 | mApplication = application; 19 | } 20 | 21 | public void onCreate() { 22 | } 23 | 24 | public void onTerminate() { 25 | } 26 | 27 | public void onLowMemory() { 28 | } 29 | 30 | public void onTrimMemory(int level) { 31 | } 32 | 33 | public void onConfigurationChanged(Configuration newConfig) { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/multiprocess/PriorityLogicWrapper.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.multiprocess; 2 | 3 | 4 | /** 5 | * Created by wanglei on 2016/11/25. 6 | */ 7 | 8 | public class PriorityLogicWrapper implements Comparable { 9 | 10 | public int priority = 0; 11 | public Class logicClass = null; 12 | public BaseApplicationLogic instance; 13 | 14 | public PriorityLogicWrapper(int priority, Class logicClass) { 15 | this.priority = priority; 16 | this.logicClass = logicClass; 17 | } 18 | 19 | @Override 20 | public int compareTo(PriorityLogicWrapper o) { 21 | return o.priority - this.priority; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/ConnectServiceWrapper.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | /** 4 | * Created by wanglei on 2016/11/30. 5 | */ 6 | 7 | public class ConnectServiceWrapper { 8 | public Class targetClass = null; 9 | 10 | public ConnectServiceWrapper( Class logicClass) { 11 | this.targetClass = logicClass; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/LocalRouter.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.os.IBinder; 8 | import android.os.RemoteException; 9 | import android.support.annotation.NonNull; 10 | 11 | import com.spinytech.macore.ErrorAction; 12 | import com.spinytech.macore.MaAction; 13 | import com.spinytech.macore.MaApplication; 14 | import com.spinytech.macore.MaProvider; 15 | import com.spinytech.macore.tools.Logger; 16 | import com.spinytech.macore.tools.ProcessUtil; 17 | 18 | import java.util.HashMap; 19 | import java.util.concurrent.Callable; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | 23 | import io.reactivex.Observable; 24 | 25 | import static android.content.Context.BIND_AUTO_CREATE; 26 | 27 | /** 28 | * The Local Router 29 | */ 30 | 31 | public class LocalRouter { 32 | private static final String TAG = "LocalRouter"; 33 | private String mProcessName = ProcessUtil.UNKNOWN_PROCESS_NAME; 34 | private static LocalRouter sInstance = null; 35 | private HashMap mProviders = null; 36 | private MaApplication mApplication; 37 | private IWideRouterAIDL mWideRouterAIDL; 38 | private static ExecutorService threadPool = null; 39 | private ServiceConnection mServiceConnection = new ServiceConnection() { 40 | @Override 41 | public void onServiceConnected(ComponentName name, IBinder service) { 42 | mWideRouterAIDL = IWideRouterAIDL.Stub.asInterface(service); 43 | } 44 | 45 | @Override 46 | public void onServiceDisconnected(ComponentName name) { 47 | mWideRouterAIDL = null; 48 | } 49 | }; 50 | 51 | private LocalRouter(MaApplication context) { 52 | mApplication = context; 53 | mProcessName = ProcessUtil.getProcessName(context, ProcessUtil.getMyProcessId()); 54 | mProviders = new HashMap<>(); 55 | if (mApplication.needMultipleProcess() && !WideRouter.PROCESS_NAME.equals(mProcessName)) { 56 | connectWideRouter(); 57 | } 58 | } 59 | 60 | public static synchronized LocalRouter getInstance(@NonNull MaApplication context) { 61 | if (sInstance == null) { 62 | sInstance = new LocalRouter(context); 63 | } 64 | return sInstance; 65 | } 66 | 67 | private static ExecutorService getThreadPool() { 68 | if (null == threadPool) { 69 | synchronized (LocalRouter.class) { 70 | threadPool = Executors.newCachedThreadPool(); 71 | } 72 | } 73 | return threadPool; 74 | } 75 | 76 | public void connectWideRouter() { 77 | Intent binderIntent = new Intent(mApplication, WideRouterConnectService.class); 78 | binderIntent.putExtra("domain", mProcessName); 79 | mApplication.bindService(binderIntent, mServiceConnection, BIND_AUTO_CREATE); 80 | } 81 | 82 | public void disconnectWideRouter() { 83 | if (null == mServiceConnection) { 84 | return; 85 | } 86 | mApplication.unbindService(mServiceConnection); 87 | mWideRouterAIDL = null; 88 | } 89 | 90 | public void registerProvider(String providerName, MaProvider provider) { 91 | mProviders.put(providerName, provider); 92 | } 93 | 94 | 95 | public boolean checkWideRouterConnection() { 96 | boolean result = false; 97 | if (mWideRouterAIDL != null) { 98 | result = true; 99 | } 100 | return result; 101 | } 102 | 103 | boolean answerWiderAsync(@NonNull RouterRequest routerRequest) { 104 | if (mProcessName.equals(routerRequest.getDomain()) && checkWideRouterConnection()) { 105 | return findRequestAction(routerRequest).isAsync(mApplication, routerRequest); 106 | } else { 107 | return true; 108 | } 109 | } 110 | 111 | private static class RouteResultWrap{ 112 | MaActionResult maActionResult; 113 | Observable maActionResultObservable; 114 | } 115 | 116 | 117 | public MaActionResult route(Context context, @NonNull RouterRequest routerRequest) throws Exception { 118 | RouteResultWrap routeResultWrap = new RouteResultWrap(); 119 | rxRoute(context, routerRequest, routeResultWrap, RouteResultType.MA_ACTION_RESULT); 120 | return routeResultWrap.maActionResult; 121 | } 122 | 123 | public Observable rxRoute(Context context, @NonNull RouterRequest routerRequest) throws Exception { 124 | RouteResultWrap routeResultWrap= new RouteResultWrap(); 125 | rxRoute(context, routerRequest, routeResultWrap, RouteResultType.OBSERVABLE); 126 | return routeResultWrap.maActionResultObservable; 127 | } 128 | 129 | private void rxRoute(Context context, @NonNull RouterRequest routerRequest, RouteResultWrap routeResultWrap, RouteResultType type) throws Exception { 130 | Logger.d(TAG, "Process:" + mProcessName + "\nLocal rxRoute start: " + System.currentTimeMillis()); 131 | // Local request 132 | if (mProcessName.equals(routerRequest.getDomain())) { 133 | Logger.d(TAG, "Process:" + mProcessName + "\nLocal find action start: " + System.currentTimeMillis()); 134 | MaAction targetAction = findRequestAction(routerRequest); 135 | routerRequest.isIdle.set(true); 136 | Logger.d(TAG, "Process:" + mProcessName + "\nLocal find action end: " + System.currentTimeMillis()); 137 | // Sync result, return the result immediately. 138 | if (!targetAction.isAsync(context, routerRequest)) { 139 | routeResultWrap.maActionResult = targetAction.invoke(context, routerRequest); 140 | Logger.d(TAG, "Process:" + mProcessName + "\nLocal sync end: " + System.currentTimeMillis()); 141 | if (type == RouteResultType.OBSERVABLE) { 142 | routeResultWrap.maActionResultObservable = Observable.just(routeResultWrap.maActionResult); 143 | return; 144 | } else { 145 | return; 146 | } 147 | } 148 | // Async result, use the thread pool to execute the task. 149 | else { 150 | LocalTask task = new LocalTask(routerRequest, context, targetAction); 151 | if (type == RouteResultType.OBSERVABLE) { 152 | routeResultWrap.maActionResultObservable = Observable.fromFuture(getThreadPool().submit(task)); 153 | } else { 154 | routeResultWrap.maActionResult = getThreadPool().submit(task).get(); 155 | } 156 | return; 157 | } 158 | } else if (!mApplication.needMultipleProcess()) { 159 | throw new RuntimeException("Please make sure the returned value of needMultipleProcess in MaApplication is true, so that you can invoke other process action."); 160 | } 161 | // IPC request 162 | else { 163 | String domain = routerRequest.getDomain(); 164 | routerRequest.isIdle.set(true); 165 | boolean mIsAsync = false; 166 | if (checkWideRouterConnection()) { 167 | Logger.d(TAG, "Process:" + mProcessName + "\nWide async check start: " + System.currentTimeMillis()); 168 | //If you don'requestObject need wide async check, use "maActionResult.mIsAsync = false;" replace the next line to improve performance. 169 | mIsAsync = mWideRouterAIDL.checkResponseAsync(domain, routerRequest); 170 | Logger.d(TAG, "Process:" + mProcessName + "\nWide async check end: " + System.currentTimeMillis()); 171 | if (!mIsAsync) { 172 | Logger.d(TAG, "Process:" + mProcessName + "\nWide sync end: " + System.currentTimeMillis()); 173 | if (type == RouteResultType.OBSERVABLE) { 174 | routeResultWrap.maActionResultObservable = Observable.just(mWideRouterAIDL.route(domain, routerRequest)); 175 | } else { 176 | routeResultWrap.maActionResult = mWideRouterAIDL.route(domain, routerRequest); 177 | } 178 | return; 179 | } 180 | // Async result, use the thread pool to execute the task. 181 | else { 182 | WideTask task = new WideTask(domain, routerRequest); 183 | if (type == RouteResultType.OBSERVABLE) { 184 | routeResultWrap.maActionResultObservable = Observable.fromFuture(getThreadPool().submit(task)); 185 | } else { 186 | routeResultWrap.maActionResult = getThreadPool().submit(task).get(); 187 | } 188 | return; 189 | } 190 | } 191 | // Has not connected with the wide router. 192 | else { 193 | ConnectWideTask task = new ConnectWideTask(domain, routerRequest); 194 | if (type == RouteResultType.OBSERVABLE) { 195 | routeResultWrap.maActionResultObservable = Observable.fromFuture(getThreadPool().submit(task)); 196 | } else { 197 | routeResultWrap.maActionResult = getThreadPool().submit(task).get(); 198 | } 199 | return; 200 | } 201 | } 202 | } 203 | 204 | public boolean stopSelf(Class clazz) { 205 | if (checkWideRouterConnection()) { 206 | try { 207 | return mWideRouterAIDL.stopRouter(mProcessName); 208 | } catch (RemoteException e) { 209 | e.printStackTrace(); 210 | return false; 211 | } 212 | } else { 213 | mApplication.stopService(new Intent(mApplication, clazz)); 214 | return true; 215 | } 216 | } 217 | 218 | public void stopWideRouter() { 219 | if (checkWideRouterConnection()) { 220 | try { 221 | mWideRouterAIDL.stopRouter(WideRouter.PROCESS_NAME); 222 | } catch (RemoteException e) { 223 | e.printStackTrace(); 224 | } 225 | } else { 226 | Logger.e(TAG, "This local router hasn'requestObject connected the wide router."); 227 | } 228 | } 229 | 230 | private MaAction findRequestAction(RouterRequest routerRequest) { 231 | MaProvider targetProvider = mProviders.get(routerRequest.getProvider()); 232 | ErrorAction defaultNotFoundAction = new ErrorAction(false, MaActionResult.CODE_NOT_FOUND, "Not found the action."); 233 | if (null == targetProvider) { 234 | return defaultNotFoundAction; 235 | } else { 236 | MaAction targetAction = targetProvider.findAction(routerRequest.getAction()); 237 | if (null == targetAction) { 238 | return defaultNotFoundAction; 239 | } else { 240 | return targetAction; 241 | } 242 | } 243 | } 244 | 245 | private class LocalTask implements Callable { 246 | private RouterRequest mRequestData; 247 | private Context mContext; 248 | private MaAction mAction; 249 | 250 | public LocalTask(RouterRequest requestData, Context context, MaAction maAction) { 251 | this.mContext = context; 252 | this.mRequestData = requestData; 253 | this.mAction = maAction; 254 | } 255 | 256 | @Override 257 | public MaActionResult call() throws Exception { 258 | MaActionResult result = mAction.invoke(mContext, mRequestData); 259 | Logger.d(TAG, "Process:" + mProcessName + "\nLocal async end: " + System.currentTimeMillis()); 260 | return result; 261 | } 262 | } 263 | 264 | private class WideTask implements Callable { 265 | 266 | private String mDomain; 267 | private RouterRequest routerRequest; 268 | 269 | public WideTask(String domain, RouterRequest routerRequest) { 270 | this.mDomain = domain; 271 | this.routerRequest = routerRequest; 272 | } 273 | 274 | @Override 275 | public MaActionResult call() throws Exception { 276 | Logger.d(TAG, "Process:" + mProcessName + "\nWide async start: " + System.currentTimeMillis()); 277 | MaActionResult result = mWideRouterAIDL.route(mDomain, routerRequest); 278 | Logger.d(TAG, "Process:" + mProcessName + "\nWide async end: " + System.currentTimeMillis()); 279 | return result; 280 | } 281 | } 282 | 283 | private class ConnectWideTask implements Callable { 284 | private String mDomain; 285 | private RouterRequest routerRequest; 286 | 287 | public ConnectWideTask(String domain, RouterRequest routerRequest) { 288 | this.mDomain = domain; 289 | this.routerRequest = routerRequest; 290 | } 291 | 292 | @Override 293 | public MaActionResult call() throws Exception { 294 | Logger.d(TAG, "Process:" + mProcessName + "\nBind wide router start: " + System.currentTimeMillis()); 295 | connectWideRouter(); 296 | int time = 0; 297 | while (true) { 298 | if (null == mWideRouterAIDL) { 299 | try { 300 | Thread.sleep(50); 301 | } catch (InterruptedException e) { 302 | e.printStackTrace(); 303 | } 304 | time++; 305 | } else { 306 | break; 307 | } 308 | if (time >= 600) { 309 | ErrorAction defaultNotFoundAction = new ErrorAction(true, MaActionResult.CODE_CANNOT_BIND_WIDE, "Bind wide router time out. Can not bind wide router."); 310 | return defaultNotFoundAction.invoke(mApplication, new RouterRequest()); 311 | } 312 | } 313 | Logger.d(TAG, "Process:" + mProcessName + "\nBind wide router end: " + System.currentTimeMillis()); 314 | MaActionResult result = mWideRouterAIDL.route(mDomain, routerRequest); 315 | Logger.d(TAG, "Process:" + mProcessName + "\nWide async end: " + System.currentTimeMillis()); 316 | return result; 317 | } 318 | } 319 | 320 | private enum RouteResultType { 321 | OBSERVABLE, 322 | MA_ACTION_RESULT 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/LocalRouterConnectService.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.os.RemoteException; 7 | import android.support.annotation.Nullable; 8 | import android.util.Log; 9 | 10 | import com.spinytech.macore.MaApplication; 11 | 12 | /** 13 | * Created by wanglei on 2016/11/29. 14 | */ 15 | 16 | public class LocalRouterConnectService extends Service { 17 | 18 | @Override 19 | public int onStartCommand(Intent intent, int flags, int startId) { 20 | return super.onStartCommand(intent, flags, startId); 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public IBinder onBind(Intent intent) { 26 | Log.e("MRCS","onBind"); 27 | return stub; 28 | } 29 | 30 | ILocalRouterAIDL.Stub stub = new ILocalRouterAIDL.Stub() { 31 | 32 | @Override 33 | public boolean checkResponseAsync(RouterRequest routerRequest) throws RemoteException { 34 | return LocalRouter.getInstance(MaApplication.getMaApplication()). 35 | answerWiderAsync(routerRequest); 36 | } 37 | 38 | @Override 39 | public MaActionResult route(RouterRequest routerRequest) { 40 | try { 41 | LocalRouter localRouter = LocalRouter.getInstance(MaApplication.getMaApplication()); 42 | RouterRequest routerRequest1 = routerRequest; 43 | return localRouter.route(LocalRouterConnectService.this,routerRequest1); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | return new MaActionResult.Builder().msg(e.getMessage()).build(); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean stopWideRouter() throws RemoteException { 52 | LocalRouter 53 | .getInstance(MaApplication.getMaApplication()) 54 | .disconnectWideRouter(); 55 | return true; 56 | } 57 | 58 | @Override 59 | public void connectWideRouter() throws RemoteException { 60 | LocalRouter 61 | .getInstance(MaApplication.getMaApplication()) 62 | .connectWideRouter(); 63 | } 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/MaActionResult.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | /** 8 | * Created by wanglei on 16/6/14. 9 | */ 10 | public class MaActionResult implements Parcelable { 11 | public static final int CODE_SUCCESS = 0x0000; 12 | public static final int CODE_ERROR = 0x0001; 13 | public static final int CODE_NOT_FOUND = 0X0002; 14 | public static final int CODE_INVALID = 0X0003; 15 | public static final int CODE_ROUTER_NOT_REGISTER = 0X0004; 16 | public static final int CODE_CANNOT_BIND_LOCAL = 0X0005; 17 | public static final int CODE_REMOTE_EXCEPTION = 0X0006; 18 | public static final int CODE_CANNOT_BIND_WIDE = 0X0007; 19 | public static final int CODE_TARGET_IS_WIDE = 0X0008; 20 | public static final int CODE_WIDE_STOPPING = 0X0009; 21 | 22 | private int code; 23 | private String msg; 24 | private String data; 25 | private T result; 26 | 27 | MaActionResult(){ 28 | 29 | } 30 | 31 | private MaActionResult(Builder builder) { 32 | code = builder.code; 33 | msg = builder.msg; 34 | data = builder.data; 35 | result = (T) builder.result; 36 | } 37 | 38 | protected MaActionResult(Parcel in) { 39 | code = in.readInt(); 40 | msg = in.readString(); 41 | data = in.readString(); 42 | result = (T) in.readParcelable(this.getClass().getClassLoader()); 43 | } 44 | 45 | public static final Creator CREATOR = new Creator() { 46 | @Override 47 | public MaActionResult createFromParcel(Parcel in) { 48 | return new MaActionResult(in); 49 | } 50 | 51 | @Override 52 | public MaActionResult[] newArray(int size) { 53 | return new MaActionResult[size]; 54 | } 55 | }; 56 | 57 | public Object getResult() { 58 | return result; 59 | } 60 | 61 | public String getData() { 62 | return data; 63 | } 64 | 65 | public int getCode() { 66 | return code; 67 | } 68 | 69 | public String getMsg() { 70 | return msg; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "MaActionResult{" + 76 | "code=" + code + 77 | ", msg='" + msg + '\'' + 78 | ", data='" + data + '\'' + 79 | ", object=" + result + 80 | '}'; 81 | } 82 | 83 | @Override 84 | public int describeContents() { 85 | return 0; 86 | } 87 | 88 | @Override 89 | public void writeToParcel(Parcel dest, int flags) { 90 | dest.writeInt(code); 91 | dest.writeString(msg); 92 | dest.writeString(data); 93 | dest.writeParcelable((Parcelable) result, flags); 94 | } 95 | 96 | 97 | public static final class Builder { 98 | private int code; 99 | private String msg; 100 | private String data; 101 | private T result; 102 | 103 | public Builder() { 104 | } 105 | 106 | public Builder code(int val) { 107 | code = val; 108 | return this; 109 | } 110 | 111 | public Builder msg(String val) { 112 | msg = val; 113 | return this; 114 | } 115 | 116 | public Builder data(String val) { 117 | data = val; 118 | return this; 119 | } 120 | 121 | public Builder result(T val) { 122 | result = val; 123 | return this; 124 | } 125 | 126 | public MaActionResult build() { 127 | return new MaActionResult(this); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/RouterRequest.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.content.Context; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.concurrent.atomic.AtomicBoolean; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * Created by wanglei on 2016/12/27. 17 | */ 18 | 19 | public class RouterRequest implements Parcelable { 20 | static AtomicInteger sIndex = new AtomicInteger(0); 21 | 22 | String from; 23 | String domain; 24 | String provider; 25 | String action; 26 | HashMap data; 27 | T requestObject; 28 | 29 | public AtomicBoolean isIdle = new AtomicBoolean(true); 30 | 31 | public RouterRequest() { 32 | this.from = RouterRequestUtil.DEFAULT_PROCESS; 33 | this.domain = RouterRequestUtil.DEFAULT_PROCESS; 34 | this.provider = ""; 35 | this.action = ""; 36 | this.data = new HashMap<>(); 37 | } 38 | 39 | 40 | RouterRequest(Context context) { 41 | this.from = RouterRequestUtil.getProcess(context); 42 | this.domain = RouterRequestUtil.getProcess(context); 43 | this.provider = ""; 44 | this.action = ""; 45 | this.data = new HashMap<>(); 46 | } 47 | 48 | protected RouterRequest(Parcel in) { 49 | from = in.readString(); 50 | domain = in.readString(); 51 | provider = in.readString(); 52 | action = in.readString(); 53 | requestObject = (T) in.readParcelable(this.getClass().getClassLoader()); 54 | int mapSize = in.readInt(); 55 | if (mapSize > 0) { 56 | data = new HashMap<>(); 57 | } 58 | for (int i = 0; i < mapSize; i++) { 59 | String key = in.readString(); 60 | String value = in.readString(); 61 | data.put(key, value); 62 | } 63 | } 64 | 65 | public T getRequestObject() { 66 | return requestObject; 67 | } 68 | 69 | public static final Creator CREATOR = new Creator() { 70 | @Override 71 | public RouterRequest createFromParcel(Parcel in) { 72 | return new RouterRequest(in); 73 | } 74 | 75 | @Override 76 | public RouterRequest[] newArray(int size) { 77 | return new RouterRequest[size]; 78 | } 79 | }; 80 | 81 | public String getFrom() { 82 | return from; 83 | } 84 | 85 | public String getDomain() { 86 | return domain; 87 | } 88 | 89 | public String getProvider() { 90 | return provider; 91 | } 92 | 93 | public String getAction() { 94 | return action; 95 | } 96 | 97 | public HashMap getData() { 98 | return data; 99 | } 100 | 101 | public String toJsonString() { 102 | //Here remove Gson to save about 10ms. 103 | //String result = new Gson().toJson(this); 104 | JSONObject jsonObject = new JSONObject(); 105 | try { 106 | jsonObject.put("from", from); 107 | jsonObject.put("domain", domain); 108 | jsonObject.put("provider", provider); 109 | jsonObject.put("action", action); 110 | 111 | try { 112 | JSONObject jsonData = new JSONObject(); 113 | for (Map.Entry entry : data.entrySet()) { 114 | jsonData.put(entry.getKey(), entry.getValue()); 115 | } 116 | jsonObject.put("data", jsonData); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | jsonObject.put("data", "{}"); 120 | } 121 | } catch (JSONException e) { 122 | e.printStackTrace(); 123 | } 124 | 125 | return jsonObject.toString(); 126 | } 127 | 128 | public RouterRequest domain(String domain) { 129 | this.domain = domain; 130 | return this; 131 | } 132 | 133 | 134 | public RouterRequest provider(String provider) { 135 | this.provider = provider; 136 | return this; 137 | } 138 | 139 | 140 | public RouterRequest action(String action) { 141 | this.action = action; 142 | return this; 143 | } 144 | 145 | public RouterRequest reqeustObject(T t) { 146 | this.requestObject = t; 147 | return this; 148 | } 149 | 150 | public RouterRequest data(String key, String data) { 151 | this.data.put(key, data); 152 | return this; 153 | } 154 | 155 | @Override 156 | public int describeContents() { 157 | return 0; 158 | } 159 | 160 | @Override 161 | public void writeToParcel(Parcel dest, int flags) { 162 | dest.writeString(from); 163 | dest.writeString(domain); 164 | dest.writeString(provider); 165 | dest.writeString(action); 166 | dest.writeParcelable((Parcelable) requestObject, flags); 167 | if(data !=null){ 168 | dest.writeInt(data.size()); 169 | for (Map.Entry entry : data.entrySet()) { 170 | dest.writeString(entry.getKey()); 171 | dest.writeString(entry.getValue()); 172 | } 173 | }else{ 174 | dest.writeInt(0); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/RouterRequestUtil.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | 6 | import com.spinytech.macore.tools.Logger; 7 | import com.spinytech.macore.tools.ProcessUtil; 8 | 9 | import org.json.JSONException; 10 | import org.json.JSONObject; 11 | 12 | import java.io.UnsupportedEncodingException; 13 | import java.net.URLDecoder; 14 | import java.util.HashMap; 15 | import java.util.Iterator; 16 | 17 | /** 18 | * Created by erfli on 2/17/17. 19 | */ 20 | 21 | public class RouterRequestUtil { 22 | static final String TAG = "RouterRequest"; 23 | static final int length = 64; 24 | public static volatile RouterRequest[] table = new RouterRequest[length]; 25 | static final int RESET_NUM = 1000; 26 | static volatile String DEFAULT_PROCESS = ""; 27 | 28 | static { 29 | for (int i = 0; i < RouterRequestUtil.length; i++) { 30 | RouterRequestUtil.table[i] = new RouterRequest(); 31 | } 32 | } 33 | 34 | public static RouterRequest json(String requestJsonString) { 35 | RouterRequest routerRequest = new RouterRequest(); 36 | try { 37 | JSONObject jsonObject = new JSONObject(requestJsonString); 38 | routerRequest.from = jsonObject.getString("from"); 39 | routerRequest.domain = jsonObject.getString("domain"); 40 | routerRequest.provider = jsonObject.getString("provider"); 41 | routerRequest.action = jsonObject.getString("action"); 42 | try { 43 | JSONObject jsonData = new JSONObject(jsonObject.getString("data")); 44 | Iterator it = jsonData.keys(); 45 | while (it.hasNext()) { 46 | String key = String.valueOf(it.next()); 47 | String value = (String) jsonData.get(key); 48 | routerRequest.data.put(key, value); 49 | } 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | routerRequest.data = new HashMap<>(); 53 | } 54 | } catch (JSONException e) { 55 | e.printStackTrace(); 56 | } 57 | return routerRequest; 58 | } 59 | 60 | public static RouterRequest url(String url) { 61 | RouterRequest routerRequest = new RouterRequest(); 62 | int questIndex = url.indexOf('?'); 63 | String[] urls = url.split("\\?"); 64 | if (urls.length != 1 && urls.length != 2) { 65 | Logger.e(TAG, "The url is illegal."); 66 | return routerRequest; 67 | } 68 | String[] targets = urls[0].split("/"); 69 | if (targets.length == 3) { 70 | routerRequest.domain = targets[0]; 71 | routerRequest.provider = targets[1]; 72 | routerRequest.action = targets[2]; 73 | } else { 74 | Logger.e(TAG, "The url is illegal."); 75 | return routerRequest; 76 | } 77 | //Add params 78 | if (questIndex != -1) { 79 | String queryString = urls[1]; 80 | if (queryString != null && queryString.length() > 0) { 81 | int ampersandIndex, lastAmpersandIndex = 0; 82 | String subStr, key, value; 83 | String[] paramPair, values, newValues; 84 | do { 85 | ampersandIndex = queryString.indexOf('&', lastAmpersandIndex) + 1; 86 | if (ampersandIndex > 0) { 87 | subStr = queryString.substring(lastAmpersandIndex, ampersandIndex - 1); 88 | lastAmpersandIndex = ampersandIndex; 89 | } else { 90 | subStr = queryString.substring(lastAmpersandIndex); 91 | } 92 | paramPair = subStr.split("="); 93 | key = paramPair[0]; 94 | value = paramPair.length == 1 ? "" : paramPair[1]; 95 | try { 96 | value = URLDecoder.decode(value, "UTF-8"); 97 | } catch (UnsupportedEncodingException e) { 98 | e.printStackTrace(); 99 | } 100 | routerRequest.data.put(key, value); 101 | } while (ampersandIndex > 0); 102 | } 103 | } 104 | return routerRequest; 105 | } 106 | 107 | public static String getProcess(Context context) { 108 | if (TextUtils.isEmpty(DEFAULT_PROCESS) || ProcessUtil.UNKNOWN_PROCESS_NAME.equals(DEFAULT_PROCESS)) { 109 | DEFAULT_PROCESS = ProcessUtil.getProcessName(context, ProcessUtil.getMyProcessId()); 110 | } 111 | return DEFAULT_PROCESS; 112 | } 113 | 114 | public static RouterRequest obtain(Context context) { 115 | return obtain(context, 0); 116 | } 117 | 118 | public static RouterRequest obtain(Context context, int retryTime) { 119 | int index = RouterRequest.sIndex.getAndIncrement(); 120 | if (index > RESET_NUM) { 121 | RouterRequest.sIndex.compareAndSet(index, 0); 122 | if (index > RESET_NUM * 2) { 123 | RouterRequest.sIndex.set(0); 124 | } 125 | } 126 | 127 | int num = index & (length - 1); 128 | 129 | RouterRequest target = table[num]; 130 | 131 | if (target.isIdle.compareAndSet(true, false)) { 132 | target.from = getProcess(context); 133 | target.domain = getProcess(context); 134 | target.provider = ""; 135 | target.action = ""; 136 | target.data.clear(); 137 | return target; 138 | } else { 139 | if (retryTime < 5) { 140 | return obtain(context, retryTime++); 141 | } else { 142 | return new RouterRequest(context); 143 | } 144 | 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/WideRouter.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | import android.content.ServiceConnection; 6 | import android.os.Bundle; 7 | import android.os.IBinder; 8 | import android.os.RemoteException; 9 | import android.support.annotation.NonNull; 10 | import android.text.TextUtils; 11 | 12 | import com.spinytech.macore.MaApplication; 13 | import com.spinytech.macore.tools.Logger; 14 | import com.spinytech.macore.tools.ProcessUtil; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | 20 | import static android.content.Context.BIND_AUTO_CREATE; 21 | 22 | /** 23 | * Created by wanglei on 2016/11/29. 24 | */ 25 | 26 | public class WideRouter { 27 | private static final String TAG = "WideRouter"; 28 | public static final String PROCESS_NAME = "com.spiny.ma.widerouter"; 29 | private static HashMap sLocalRouterClasses; 30 | private static WideRouter sInstance = null; 31 | private MaApplication mApplication; 32 | private HashMap mLocalRouterConnectionMap; 33 | private HashMap mLocalRouterAIDLMap; 34 | boolean mIsStopping = false; 35 | 36 | private WideRouter(MaApplication context) { 37 | mApplication = context; 38 | String checkProcessName = ProcessUtil.getProcessName(context, ProcessUtil.getMyProcessId()); 39 | if (!PROCESS_NAME.equals(checkProcessName)) { 40 | throw new RuntimeException("You should not initialize the WideRouter in process:" + checkProcessName); 41 | } 42 | sLocalRouterClasses = new HashMap<>(); 43 | mLocalRouterConnectionMap = new HashMap<>(); 44 | mLocalRouterAIDLMap = new HashMap<>(); 45 | } 46 | 47 | public static synchronized WideRouter getInstance(@NonNull MaApplication context) { 48 | if (sInstance == null) { 49 | sInstance = new WideRouter(context); 50 | } 51 | return sInstance; 52 | } 53 | 54 | public static void registerLocalRouter(String processName, Class targetClass) { 55 | if (null == sLocalRouterClasses) { 56 | sLocalRouterClasses = new HashMap<>(); 57 | } 58 | ConnectServiceWrapper connectServiceWrapper = new ConnectServiceWrapper(targetClass); 59 | sLocalRouterClasses.put(processName, connectServiceWrapper); 60 | } 61 | 62 | boolean checkLocalRouterHasRegistered(final String domain) { 63 | ConnectServiceWrapper connectServiceWrapper = sLocalRouterClasses.get(domain); 64 | if(null == connectServiceWrapper){ 65 | return false; 66 | } 67 | Class clazz = connectServiceWrapper.targetClass; 68 | if (null == clazz) { 69 | return false; 70 | } else { 71 | return true; 72 | } 73 | } 74 | 75 | boolean connectLocalRouter(final String domain) { 76 | ConnectServiceWrapper connectServiceWrapper = sLocalRouterClasses.get(domain); 77 | if(null == connectServiceWrapper){ 78 | return false; 79 | } 80 | Class clazz = connectServiceWrapper.targetClass; 81 | if (null == clazz) { 82 | return false; 83 | } 84 | Intent binderIntent = new Intent(mApplication, clazz); 85 | Bundle bundle = new Bundle(); 86 | binderIntent.putExtras(bundle); 87 | final ServiceConnection serviceConnection = new ServiceConnection() { 88 | @Override 89 | public void onServiceConnected(ComponentName name, IBinder service) { 90 | ILocalRouterAIDL mLocalRouterAIDL = ILocalRouterAIDL.Stub.asInterface(service); 91 | ILocalRouterAIDL temp = mLocalRouterAIDLMap.get(domain); 92 | if (null == temp) { 93 | mLocalRouterAIDLMap.put(domain, mLocalRouterAIDL); 94 | mLocalRouterConnectionMap.put(domain, this); 95 | try { 96 | mLocalRouterAIDL.connectWideRouter(); 97 | } catch (RemoteException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | } 102 | 103 | @Override 104 | public void onServiceDisconnected(ComponentName name) { 105 | mLocalRouterAIDLMap.remove(domain); 106 | mLocalRouterConnectionMap.remove(domain); 107 | } 108 | }; 109 | mApplication.bindService(binderIntent, serviceConnection, BIND_AUTO_CREATE); 110 | return true; 111 | } 112 | 113 | boolean disconnectLocalRouter(String domain) { 114 | if (TextUtils.isEmpty(domain)) { 115 | return false; 116 | } else if (PROCESS_NAME.equals(domain)) { 117 | stopSelf(); 118 | return true; 119 | } else if (null == mLocalRouterConnectionMap.get(domain)) { 120 | return false; 121 | } else { 122 | ILocalRouterAIDL aidl = mLocalRouterAIDLMap.get(domain); 123 | if (null != aidl) { 124 | try { 125 | aidl.stopWideRouter(); 126 | } catch (RemoteException e) { 127 | e.printStackTrace(); 128 | } 129 | } 130 | mApplication.unbindService(mLocalRouterConnectionMap.get(domain)); 131 | mLocalRouterAIDLMap.remove(domain); 132 | mLocalRouterConnectionMap.remove(domain); 133 | return true; 134 | } 135 | } 136 | 137 | /** 138 | */ 139 | void stopSelf() { 140 | mIsStopping = true; 141 | new Thread(new Runnable() { 142 | @Override 143 | public void run() { 144 | List locals = new ArrayList<>(); 145 | locals.addAll(mLocalRouterAIDLMap.keySet()); 146 | for (String domain : locals) { 147 | ILocalRouterAIDL aidl = mLocalRouterAIDLMap.get(domain); 148 | if (null != aidl) { 149 | try { 150 | aidl.stopWideRouter(); 151 | } catch (RemoteException e) { 152 | e.printStackTrace(); 153 | } 154 | mApplication.unbindService(mLocalRouterConnectionMap.get(domain)); 155 | mLocalRouterAIDLMap.remove(domain); 156 | mLocalRouterConnectionMap.remove(domain); 157 | } 158 | } 159 | try { 160 | Thread.sleep(1000); 161 | mApplication.stopService(new Intent(mApplication, WideRouterConnectService.class)); 162 | Thread.sleep(1000); 163 | } catch (InterruptedException e) { 164 | e.printStackTrace(); 165 | } 166 | System.exit(0); 167 | } 168 | }).start(); 169 | } 170 | 171 | boolean answerLocalAsync(String domain, RouterRequest routerRequest) { 172 | ILocalRouterAIDL target = mLocalRouterAIDLMap.get(domain); 173 | if (target == null) { 174 | ConnectServiceWrapper connectServiceWrapper = sLocalRouterClasses.get(domain); 175 | if(null == connectServiceWrapper){ 176 | return false; 177 | } 178 | Class clazz = connectServiceWrapper.targetClass; 179 | if (null == clazz) { 180 | return false; 181 | } else { 182 | return true; 183 | } 184 | } else { 185 | try { 186 | return target.checkResponseAsync(routerRequest); 187 | } catch (RemoteException e) { 188 | e.printStackTrace(); 189 | return true; 190 | } 191 | } 192 | } 193 | 194 | public MaActionResult route(String domain, RouterRequest routerRequest) { 195 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide rxRoute start: " + System.currentTimeMillis()); 196 | if (mIsStopping) { 197 | 198 | MaActionResult result = new MaActionResult.Builder() 199 | .code(MaActionResult.CODE_WIDE_STOPPING) 200 | .msg("Wide router is stopping.") 201 | .build(); 202 | return result; 203 | } 204 | if (PROCESS_NAME.equals(domain)) { 205 | MaActionResult result = new MaActionResult.Builder() 206 | .code(MaActionResult.CODE_TARGET_IS_WIDE) 207 | .msg("Domain can not be " + PROCESS_NAME + ".") 208 | .build(); 209 | return result; 210 | } 211 | ILocalRouterAIDL target = mLocalRouterAIDLMap.get(domain); 212 | if (null == target) { 213 | if (!connectLocalRouter(domain)) { 214 | MaActionResult result = new MaActionResult.Builder() 215 | .code(MaActionResult.CODE_ROUTER_NOT_REGISTER) 216 | .msg("The " + domain + " has not registered.") 217 | .build(); 218 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nLocal not register end: " + System.currentTimeMillis()); 219 | return result; 220 | } else { 221 | // Wait to bind the target process connect service, timeout is 30s. 222 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nBind local router start: " + System.currentTimeMillis()); 223 | int time = 0; 224 | while (true) { 225 | target = mLocalRouterAIDLMap.get(domain); 226 | if (null == target) { 227 | try { 228 | Thread.sleep(50); 229 | } catch (InterruptedException e) { 230 | e.printStackTrace(); 231 | } 232 | time++; 233 | } else { 234 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nBind local router end: " + System.currentTimeMillis()); 235 | break; 236 | } 237 | if (time >= 600) { 238 | MaActionResult result = new MaActionResult.Builder() 239 | .code(MaActionResult.CODE_CANNOT_BIND_LOCAL) 240 | .msg("Can not bind " + domain + ", time out.") 241 | .build(); 242 | return result; 243 | } 244 | } 245 | } 246 | } 247 | try { 248 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide target start: " + System.currentTimeMillis()); 249 | MaActionResult maActionResult = target.route(routerRequest); 250 | Logger.d(TAG, "Process:" + PROCESS_NAME + "\nWide rxRoute end: " + System.currentTimeMillis()); 251 | return maActionResult; 252 | } catch (RemoteException e) { 253 | e.printStackTrace(); 254 | MaActionResult result = new MaActionResult.Builder() 255 | .code(MaActionResult.CODE_REMOTE_EXCEPTION) 256 | .msg(e.getMessage()) 257 | .build(); 258 | return result; 259 | } 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/WideRouterApplicationLogic.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import com.spinytech.macore.multiprocess.BaseApplicationLogic; 4 | 5 | /** 6 | * Created by wanglei on 2016/11/25. 7 | */ 8 | 9 | public final class WideRouterApplicationLogic extends BaseApplicationLogic { 10 | @Override 11 | public void onCreate() { 12 | super.onCreate(); 13 | initRouter(); 14 | } 15 | 16 | protected void initRouter() { 17 | WideRouter.getInstance(mApplication); 18 | mApplication.initializeAllProcessRouter(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/router/WideRouterConnectService.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.router; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | import android.os.RemoteException; 7 | import android.support.annotation.Nullable; 8 | 9 | import com.spinytech.macore.MaApplication; 10 | import com.spinytech.macore.tools.Logger; 11 | 12 | 13 | /** 14 | * Created by wanglei on 2016/11/29. 15 | */ 16 | 17 | public final class WideRouterConnectService extends Service { 18 | private static final String TAG = "WideRouterConnectService"; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | if (!(getApplication() instanceof MaApplication)) { 24 | throw new RuntimeException("Please check your AndroidManifest.xml and make sure the application is instance of MaApplication."); 25 | } 26 | } 27 | 28 | @Override 29 | public void onDestroy() { 30 | super.onDestroy(); 31 | } 32 | 33 | @Override 34 | public int onStartCommand(Intent intent, int flags, int startId) { 35 | return START_NOT_STICKY; 36 | } 37 | 38 | @Nullable 39 | @Override 40 | public IBinder onBind(Intent intent) { 41 | String domain = intent.getStringExtra("domain"); 42 | if (WideRouter.getInstance(MaApplication.getMaApplication()).mIsStopping) { 43 | Logger.e(TAG, "Bind error: The wide router is stopping."); 44 | return null; 45 | } 46 | if (domain != null && domain.length() > 0) { 47 | boolean hasRegistered = WideRouter.getInstance(MaApplication.getMaApplication()).checkLocalRouterHasRegistered(domain); 48 | if (!hasRegistered) { 49 | Logger.e(TAG, "Bind error: The local router of process " + domain + " is not bidirectional." + 50 | "\nPlease create a Service extend LocalRouterConnectService then register it in AndroidManifest.xml and the initializeAllProcessRouter method of MaApplication." + 51 | "\nFor example:" + 52 | "\n" + 53 | "\nWideRouter.registerLocalRouter(\"your process name\",XXXConnectService.class);"); 54 | return null; 55 | } 56 | WideRouter.getInstance(MaApplication.getMaApplication()).connectLocalRouter(domain); 57 | } else { 58 | Logger.e(TAG, "Bind error: Intent do not have \"domain\" extra!"); 59 | return null; 60 | } 61 | return stub; 62 | } 63 | 64 | IWideRouterAIDL.Stub stub = new IWideRouterAIDL.Stub() { 65 | 66 | @Override 67 | public boolean checkResponseAsync(String domain, RouterRequest routerRequest) throws RemoteException { 68 | return 69 | WideRouter.getInstance(MaApplication.getMaApplication()) 70 | .answerLocalAsync(domain, routerRequest); 71 | } 72 | 73 | @Override 74 | public MaActionResult route(String domain, RouterRequest routerRequest) { 75 | try { 76 | return WideRouter.getInstance(MaApplication.getMaApplication()) 77 | .route(domain, routerRequest); 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | return new MaActionResult.Builder() 81 | .code(MaActionResult.CODE_ERROR) 82 | .msg(e.getMessage()) 83 | .build(); 84 | } 85 | } 86 | 87 | @Override 88 | public boolean stopRouter(String domain) throws RemoteException { 89 | return WideRouter.getInstance(MaApplication.getMaApplication()) 90 | .disconnectLocalRouter(domain); 91 | } 92 | 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/tools/Logger.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.tools; 2 | 3 | import android.util.Log; 4 | 5 | /** 6 | * Created by wanglei on 2017/1/10. 7 | */ 8 | public class Logger { 9 | public final static int ERROR = 1; 10 | public final static int WARN = 2; 11 | public final static int INFO = 3; 12 | public final static int DEBUG = 4; 13 | public final static int VERBOSE = 5; 14 | 15 | public static int LOG_LEVEL = VERBOSE; 16 | 17 | 18 | public static void e(String tag, String msg) { 19 | if (LOG_LEVEL >= ERROR) 20 | Log.e(tag, msg); 21 | } 22 | 23 | public static void w(String tag, String msg) { 24 | if (LOG_LEVEL >= WARN) 25 | Log.w(tag, msg); 26 | } 27 | 28 | public static void i(String tag, String msg) { 29 | if (LOG_LEVEL >= INFO) 30 | Log.i(tag, msg); 31 | } 32 | 33 | public static void d(String tag, String msg) { 34 | if (LOG_LEVEL >= DEBUG) 35 | Log.d(tag, msg); 36 | } 37 | 38 | public static void v(String tag, String msg) { 39 | if (LOG_LEVEL >= VERBOSE) 40 | Log.v(tag, msg); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/tools/ProcessUtil.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.tools; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.text.TextUtils; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.FileReader; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by wanglei on 2016/11/25. 14 | */ 15 | 16 | public class ProcessUtil { 17 | 18 | public static final String UNKNOWN_PROCESS_NAME = "unknown_process_name"; 19 | 20 | public static int getMyProcessId() { 21 | return android.os.Process.myPid(); 22 | } 23 | 24 | public static String getProcessName(int pid) { 25 | String processName = UNKNOWN_PROCESS_NAME; 26 | try { 27 | File file = new File("/proc/" + pid + "/" + "cmdline"); 28 | BufferedReader mBufferedReader = new BufferedReader(new FileReader(file)); 29 | processName = mBufferedReader.readLine().trim(); 30 | mBufferedReader.close(); 31 | return processName; 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } finally { 35 | if (!TextUtils.isEmpty(processName)) { 36 | return processName; 37 | } 38 | } 39 | return UNKNOWN_PROCESS_NAME; 40 | } 41 | 42 | public static String getProcessName(Context context, int pid) { 43 | String processName = getProcessName(pid); 44 | if(UNKNOWN_PROCESS_NAME.equals(processName)){ 45 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 46 | List runningApps = am.getRunningAppProcesses(); 47 | if (runningApps == null) { 48 | return UNKNOWN_PROCESS_NAME; 49 | } 50 | for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) { 51 | if (procInfo.pid == pid) { 52 | return procInfo.processName; 53 | } 54 | } 55 | }else{ 56 | return processName; 57 | } 58 | return UNKNOWN_PROCESS_NAME; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /macore/src/main/java/com/spinytech/macore/tools/RouterMessageUtil.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore.tools; 2 | 3 | /** 4 | * Created by wanglei on 2016/12/27. 5 | */ 6 | 7 | public class RouterMessageUtil { 8 | } 9 | -------------------------------------------------------------------------------- /macore/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MaCore 3 | 4 | -------------------------------------------------------------------------------- /macore/src/test/java/com/spinytech/macore/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.macore; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /maindemo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /maindemo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.0" 6 | 7 | defaultConfig { 8 | applicationId "com.spinytech.maindemo" 9 | minSdkVersion 14 10 | targetSdkVersion 24 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(include: ['*.jar'], dir: 'libs') 27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 28 | exclude group: 'com.android.support', module: 'support-annotations' 29 | }) 30 | compile 'com.android.support:appcompat-v7:24.2.1' 31 | testCompile 'junit:junit:4.12' 32 | if (Local.toBoolean()){ 33 | compile project(':macore') 34 | annotationProcessor project(':compile') 35 | }else{ 36 | annotationProcessor 'com.github.wutongke.modularization:compile:1.1.1' 37 | compile 'com.github.wutongke.modularization:macore:1.1.1' 38 | } 39 | compile project(':musicdemo') 40 | compile project(':picdemo') 41 | compile project(':webdemo') 42 | } 43 | -------------------------------------------------------------------------------- /maindemo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/wanglei/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /maindemo/src/androidTest/java/com/spinytech/maindemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.spinytech.maindemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /maindemo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/AsyncAction.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | import com.linked.annotion.Action; 8 | import com.spinytech.macore.MaAction; 9 | import com.spinytech.macore.router.MaActionResult; 10 | import com.spinytech.macore.router.RouterRequest; 11 | 12 | /** 13 | * Created by wanglei on 2016/12/28. 14 | */ 15 | @Action(processName = "com.spinytech.maindemo", providerName = "main") 16 | public class AsyncAction implements MaAction { 17 | 18 | @Override 19 | public boolean isAsync(Context context, RouterRequest requestData) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public MaActionResult invoke(Context context, RouterRequest requestData) { 25 | try { 26 | Thread.sleep(3000); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | String temp = ""; 31 | if (!TextUtils.isEmpty((String) requestData.getData().get("1"))) { 32 | temp += requestData.getData().get("1"); 33 | } 34 | if (!TextUtils.isEmpty((CharSequence) requestData.getData().get("2"))) { 35 | temp += requestData.getData().get("2"); 36 | } 37 | Log.e("AsyncAction", temp); 38 | MaActionResult result = new MaActionResult.Builder() 39 | .code(MaActionResult.CODE_SUCCESS) 40 | .msg("success") 41 | .data(temp) 42 | .result(null) 43 | .build(); 44 | return result; 45 | } 46 | 47 | @Override 48 | public String getName() { 49 | return "async"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | import com.spinytech.macore.router.MaActionResult; 9 | import com.spinytech.macore.MaApplication; 10 | import com.spinytech.macore.router.LocalRouter; 11 | import com.spinytech.macore.router.RouterRequestUtil; 12 | import com.spinytech.macore.Song; 13 | 14 | import io.reactivex.android.schedulers.AndroidSchedulers; 15 | import io.reactivex.functions.Consumer; 16 | import io.reactivex.schedulers.Schedulers; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | findViewById(R.id.main_local_sync_btn).setOnClickListener(new View.OnClickListener() { 25 | @Override 26 | public void onClick(View v) { 27 | try { 28 | LocalRouter.getInstance(MaApplication.getMaApplication()) 29 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this).provider("main") 30 | .action("sync") 31 | .data("1", "Hello") 32 | .data("2", "World")) 33 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 34 | .observeOn(AndroidSchedulers.mainThread()) 35 | .subscribe(new Consumer() { 36 | @Override 37 | public void accept(MaActionResult maActionResult) throws Exception { 38 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 39 | } 40 | }, new Consumer() { 41 | @Override 42 | public void accept(Throwable throwable) throws Exception { 43 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 44 | } 45 | }); 46 | 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | }); 52 | 53 | findViewById(R.id.main_local_async_btn).setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View v) { 56 | try { 57 | LocalRouter.getInstance(MaApplication.getMaApplication()) 58 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this).provider("main") 59 | .action("async") 60 | .data("1", "Hello") 61 | .data("2", "World")) 62 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 63 | .observeOn(AndroidSchedulers.mainThread()) 64 | .subscribe(new Consumer() { 65 | @Override 66 | public void accept(MaActionResult maActionResult) throws Exception { 67 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 68 | } 69 | }, new Consumer() { 70 | @Override 71 | public void accept(Throwable throwable) throws Exception { 72 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 73 | } 74 | }); 75 | 76 | Toast.makeText(MainActivity.this, "please wait", Toast.LENGTH_SHORT).show(); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | }); 82 | 83 | 84 | findViewById(R.id.main_play_btn).setOnClickListener(new View.OnClickListener() { 85 | @Override 86 | public void onClick(View v) { 87 | try { 88 | LocalRouter.getInstance(MaApplication.getMaApplication()) 89 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 90 | .domain("com.spinytech.maindemo:music") 91 | .provider("music") 92 | .action("play") 93 | .reqeustObject(new Song("see you")) 94 | ) 95 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 96 | .observeOn(AndroidSchedulers.mainThread()) 97 | .subscribe(new Consumer() { 98 | @Override 99 | public void accept(MaActionResult maActionResult) throws Exception { 100 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 101 | } 102 | }, new Consumer() { 103 | @Override 104 | public void accept(Throwable throwable) throws Exception { 105 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 106 | } 107 | }); 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | }); 113 | findViewById(R.id.main_stop_btn).setOnClickListener(new View.OnClickListener() { 114 | @Override 115 | public void onClick(View v) { 116 | try { 117 | LocalRouter.getInstance(MaApplication.getMaApplication()) 118 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 119 | .domain("com.spinytech.maindemo:music") 120 | .provider("music") 121 | .action("stop")) 122 | .observeOn(AndroidSchedulers.mainThread()) 123 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 124 | .subscribe(new Consumer() { 125 | @Override 126 | public void accept(MaActionResult maActionResult) throws Exception { 127 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 128 | } 129 | }, new Consumer() { 130 | @Override 131 | public void accept(Throwable throwable) throws Exception { 132 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 133 | } 134 | }); 135 | } catch (Exception e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | }); 140 | 141 | 142 | findViewById(R.id.main_music_shutdown_btn).setOnClickListener(new View.OnClickListener() { 143 | @Override 144 | public void onClick(View v) { 145 | try { 146 | LocalRouter.getInstance(MaApplication.getMaApplication()) 147 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 148 | .domain("com.spinytech.maindemo:music") 149 | .provider("music") 150 | .action("shutdown")) 151 | .observeOn(AndroidSchedulers.mainThread()) 152 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 153 | .subscribe(new Consumer() { 154 | @Override 155 | public void accept(MaActionResult maActionResult) throws Exception { 156 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 157 | } 158 | }, new Consumer() { 159 | @Override 160 | public void accept(Throwable throwable) throws Exception { 161 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 162 | } 163 | }); 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | } 167 | } 168 | }); 169 | findViewById(R.id.main_wide_shutdown_btn).setOnClickListener(new View.OnClickListener() { 170 | @Override 171 | public void onClick(View v) { 172 | LocalRouter.getInstance(MaApplication.getMaApplication()).stopWideRouter(); 173 | } 174 | }); 175 | findViewById(R.id.main_pic_btn).setOnClickListener(new View.OnClickListener() { 176 | @Override 177 | public void onClick(View v) { 178 | try { 179 | LocalRouter.getInstance(MaApplication.getMaApplication()) 180 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 181 | .domain("com.spinytech.maindemo:pic") 182 | .provider("pic") 183 | .action("pic") 184 | .data("is_big", "0")) 185 | .observeOn(AndroidSchedulers.mainThread()) 186 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 187 | .subscribe(new Consumer() { 188 | @Override 189 | public void accept(MaActionResult maActionResult) throws Exception { 190 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 191 | } 192 | }, new Consumer() { 193 | @Override 194 | public void accept(Throwable throwable) throws Exception { 195 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 196 | } 197 | }); 198 | } catch (Exception e) { 199 | e.printStackTrace(); 200 | } 201 | } 202 | }); 203 | 204 | findViewById(R.id.main_big_pic_btn).setOnClickListener(new View.OnClickListener() { 205 | @Override 206 | public void onClick(View v) { 207 | try { 208 | LocalRouter.getInstance(MaApplication.getMaApplication()) 209 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 210 | .domain("com.spinytech.maindemo:pic") 211 | .provider("pic") 212 | .action("pic") 213 | .data("is_big", "1")) 214 | .observeOn(AndroidSchedulers.mainThread()) 215 | .subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton())) 216 | .subscribe(new Consumer() { 217 | @Override 218 | public void accept(MaActionResult maActionResult) throws Exception { 219 | Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show(); 220 | } 221 | }, new Consumer() { 222 | @Override 223 | public void accept(Throwable throwable) throws Exception { 224 | Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show(); 225 | } 226 | }); 227 | } catch (Exception e) { 228 | e.printStackTrace(); 229 | } 230 | } 231 | }); 232 | findViewById(R.id.main_web_btn).setOnClickListener(new View.OnClickListener() { 233 | @Override 234 | public void onClick(View v) { 235 | try { 236 | LocalRouter.getInstance(MaApplication.getMaApplication()) 237 | .rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this) 238 | .provider("web") 239 | .action("web") 240 | ); 241 | } catch (Exception e) { 242 | e.printStackTrace(); 243 | } 244 | } 245 | }); 246 | 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/MainApplicationLogic.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import com.spinytech.macore.multiprocess.BaseApplicationLogic; 4 | import com.spinytech.macore.router.LocalRouter; 5 | 6 | /** 7 | * Created by wanglei on 2016/11/29. 8 | */ 9 | 10 | public class MainApplicationLogic extends BaseApplicationLogic { 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/MainProvider.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import com.linked.annotion.Provider; 4 | import com.spinytech.macore.MaProvider; 5 | 6 | /** 7 | * Created by wanglei on 2016/12/28. 8 | */ 9 | @Provider(processName = "com.spinytech.maindemo") 10 | public class MainProvider extends MaProvider { 11 | @Override 12 | protected String getName() { 13 | return "main"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/MainRouterConnectService.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import com.spinytech.macore.router.LocalRouterConnectService; 4 | 5 | /** 6 | * Created by wanglei on 2016/12/28. 7 | */ 8 | 9 | public class MainRouterConnectService extends LocalRouterConnectService { 10 | } 11 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import com.linked.annotion.Modules; 4 | import com.spinytech.macore.MaApplication; 5 | import com.spinytech.macore.router.WideRouter; 6 | import com.spinytech.musicdemo.MusicApplicationLogic; 7 | import com.spinytech.musicdemo.MusicRouterConnectService; 8 | import com.spinytech.picdemo.PicApplicationLogic; 9 | import com.spinytech.picdemo.PicRouterConnectService; 10 | import com.spinytech.webdemo.WebApplicationLogic; 11 | 12 | /** 13 | * Created by wanglei on 2016/11/29. 14 | */ 15 | @Modules(modules = {"music","pic","web"}) 16 | public class MyApplication extends MaApplication { 17 | @Override 18 | public void initializeAllProcessRouter() { 19 | WideRouter.registerLocalRouter("com.spinytech.maindemo",MainRouterConnectService.class); 20 | WideRouter.registerLocalRouter("com.spinytech.maindemo:music",MusicRouterConnectService.class); 21 | WideRouter.registerLocalRouter("com.spinytech.maindemo:pic",PicRouterConnectService.class); 22 | } 23 | 24 | @Override 25 | protected void initializeLogic() { 26 | registerApplicationLogic("com.spinytech.maindemo",999, MainApplicationLogic.class); 27 | registerApplicationLogic("com.spinytech.maindemo",998, WebApplicationLogic.class); 28 | registerApplicationLogic("com.spinytech.maindemo:music",999, MusicApplicationLogic.class); 29 | registerApplicationLogic("com.spinytech.maindemo:pic",999, PicApplicationLogic.class); 30 | } 31 | 32 | @Override 33 | public boolean needMultipleProcess() { 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/SyncAction.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.widget.Toast; 6 | 7 | import com.linked.annotion.Action; 8 | import com.spinytech.macore.MaAction; 9 | import com.spinytech.macore.router.MaActionResult; 10 | import com.spinytech.macore.router.RouterRequest; 11 | 12 | /** 13 | * Created by wanglei on 2016/12/28. 14 | */ 15 | @Action(processName = "com.spinytech.maindemo", providerName = "main") 16 | public class SyncAction implements MaAction { 17 | 18 | @Override 19 | public boolean isAsync(Context context, RouterRequest requestData) { 20 | return false; 21 | } 22 | 23 | @Override 24 | public MaActionResult invoke(Context context, RouterRequest requestData) { 25 | String temp = ""; 26 | if(!TextUtils.isEmpty((CharSequence) requestData.getData().get("1"))){ 27 | temp+=requestData.getData().get("1"); 28 | } 29 | if(!TextUtils.isEmpty((CharSequence) requestData.getData().get("2"))){ 30 | temp+=requestData.getData().get("2"); 31 | } 32 | Toast.makeText(context, "SyncAction.invoke:"+temp, Toast.LENGTH_SHORT).show(); 33 | MaActionResult result = new MaActionResult.Builder() 34 | .code(MaActionResult.CODE_SUCCESS) 35 | .msg("success") 36 | .data(temp) 37 | .result(null) 38 | .build(); 39 | return result; 40 | } 41 | 42 | @Override 43 | public String getName() { 44 | return "sync"; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /maindemo/src/main/java/com/spinytech/maindemo/ThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.spinytech.maindemo; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | /** 7 | * Created by erfli on 2/16/17. 8 | */ 9 | 10 | public class ThreadPool { 11 | private static ExecutorService executorService; 12 | public static ExecutorService getThreadPoolSingleton(){ 13 | if(executorService == null){ 14 | synchronized (ThreadPool.class){ 15 | executorService = Executors.newFixedThreadPool(3); 16 | } 17 | } 18 | return executorService; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /maindemo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 14 | 15 |