├── .gitignore ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── de │ │ └── greenrobot │ │ └── event │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── de │ │ │ └── greenrobot │ │ │ └── event │ │ │ ├── AsyncPoster.java │ │ │ ├── BackgroundPoster.java │ │ │ ├── EventBus.java │ │ │ ├── EventBusBuilder.java │ │ │ ├── EventBusException.java │ │ │ ├── HandlerPoster.java │ │ │ ├── NoSubscriberEvent.java │ │ │ ├── PendingPost.java │ │ │ ├── PendingPostQueue.java │ │ │ ├── SubscriberExceptionEvent.java │ │ │ ├── SubscriberMethod.java │ │ │ ├── SubscriberMethodFinder.java │ │ │ ├── Subscription.java │ │ │ ├── ThreadMode.java │ │ │ └── util │ │ │ ├── AsyncExecutor.java │ │ │ ├── ErrorDialogConfig.java │ │ │ ├── ErrorDialogFragmentFactory.java │ │ │ ├── ErrorDialogFragments.java │ │ │ ├── ErrorDialogManager.java │ │ │ ├── ExceptionToResourceMapping.java │ │ │ ├── HasExecutionScope.java │ │ │ └── ThrowableFailureEvent.java │ └── res │ │ ├── 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 │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── de │ └── greenrobot │ └── event │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | te.xml 2 | **pom.xml.releaseBackup 3 | release.properties 4 | gen 5 | */seed.txt 6 | notes 7 | logs 8 | gen-external-apklibs 9 | .idea 10 | *.iml 11 | .DS_Store 12 | *.swp 13 | out 14 | .gradle 15 | /local.properties 16 | /build 17 | 18 | ###OSX### 19 | 20 | .DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must ends with two \r. 25 | Icon 26 | 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear on external disk 32 | .Spotlight-V100 33 | .Trashes 34 | 35 | 36 | ###Linux### 37 | 38 | *~ 39 | 40 | # KDE directory preferences 41 | .directory 42 | 43 | 44 | ###Android### 45 | 46 | # Built application files 47 | *.apk 48 | *.ap_ 49 | 50 | # Files for ART and Dalvik VM 51 | *.dex 52 | 53 | # Java class files 54 | *.class 55 | 56 | # Generated files 57 | bin/ 58 | gen/ 59 | 60 | # Gradle files 61 | .gradle/ 62 | .gradletasknamecache 63 | build/ 64 | 65 | # Local configuration file (sdk path, etc) 66 | local.properties 67 | 68 | # Proguard folder generated by Eclipse 69 | proguard/ 70 | 71 | # Lint 72 | lint-report.html 73 | lint-report_files/ 74 | lint_result.txt 75 | 76 | # Mobile Tools for Java (J2ME) 77 | .mtj.tmp/ 78 | 79 | # Package Files # 80 | *.war 81 | *.ear 82 | 83 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 84 | hs_err_pid* 85 | 86 | 87 | ###IntelliJ### 88 | 89 | *.iml 90 | *.ipr 91 | *.iws 92 | .idea/ 93 | 94 | 95 | ###Eclipse### 96 | 97 | *.pydevproject 98 | .metadata 99 | tmp/ 100 | *.tmp 101 | *.bak 102 | *.swp 103 | *~.nib 104 | .settings/ 105 | .loadpath 106 | 107 | # External tool builders 108 | .externalToolBuilders/ 109 | 110 | # Locally stored "Eclipse launch configurations" 111 | *.launch 112 | 113 | # CDT-specific 114 | .cproject 115 | 116 | # PDT-specific 117 | .buildpath 118 | 119 | # sbteclipse plugin 120 | .target 121 | 122 | # TeXlipse plugin 123 | .texlipseXml version="1.0" encoding="UTF-8"?>captures/captures 124 | -------------------------------------------------------------------------------- /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 | 本项目fork自[greenrobot/EventBus](https://github.com/greenrobot/EventBus) 2 | 3 | 当我完整读完了源码后,添加了一些中文注释以及自己的理解。 4 | 5 | 仅作为笔记记录下来. 6 | 7 | EventBus 源码研读(上):[http://kymjs.com/code/2015/12/12/01/](http://kymjs.com/code/2015/12/12/01/) 8 | EventBus 源码研读(中):[http://kymjs.com/code/2015/12/13/01/](http://kymjs.com/code/2015/12/13/01/) 9 | 10 | 11 | -----张涛 [http://www.kymjs.com](http://www.kymjs.com) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "de.greenrobot.event" 9 | minSdkVersion 11 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.1.1' 26 | } 27 | -------------------------------------------------------------------------------- /app/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/kymjs/developer/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/de/greenrobot/event/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/AsyncPoster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | 19 | /** 20 | * Posts events in background. 21 | * 22 | * @author Markus 23 | */ 24 | class AsyncPoster implements Runnable { 25 | 26 | private final PendingPostQueue queue; 27 | private final EventBus eventBus; 28 | 29 | AsyncPoster(EventBus eventBus) { 30 | this.eventBus = eventBus; 31 | queue = new PendingPostQueue(); 32 | } 33 | 34 | public void enqueue(Subscription subscription, Object event) { 35 | PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); 36 | queue.enqueue(pendingPost); 37 | eventBus.getExecutorService().execute(this); 38 | } 39 | 40 | @Override 41 | public void run() { 42 | PendingPost pendingPost = queue.poll(); 43 | if(pendingPost == null) { 44 | throw new IllegalStateException("No pending post available"); 45 | } 46 | eventBus.invokeSubscriber(pendingPost); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/BackgroundPoster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import android.util.Log; 19 | 20 | /** 21 | * Posts events in background. 22 | * 23 | * @author Markus 24 | */ 25 | final class BackgroundPoster implements Runnable { 26 | 27 | private final PendingPostQueue queue; 28 | private final EventBus eventBus; 29 | 30 | private volatile boolean executorRunning; 31 | 32 | BackgroundPoster(EventBus eventBus) { 33 | this.eventBus = eventBus; 34 | queue = new PendingPostQueue(); 35 | } 36 | 37 | public void enqueue(Subscription subscription, Object event) { 38 | PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); 39 | synchronized (this) { 40 | queue.enqueue(pendingPost); 41 | if (!executorRunning) { 42 | executorRunning = true; 43 | eventBus.getExecutorService().execute(this); 44 | } 45 | } 46 | } 47 | 48 | @Override 49 | public void run() { 50 | try { 51 | try { 52 | while (true) { 53 | PendingPost pendingPost = queue.poll(1000); 54 | if (pendingPost == null) { 55 | synchronized (this) { 56 | // Check again, this time in synchronized 57 | pendingPost = queue.poll(); 58 | if (pendingPost == null) { 59 | executorRunning = false; 60 | return; 61 | } 62 | } 63 | } 64 | eventBus.invokeSubscriber(pendingPost); 65 | } 66 | } catch (InterruptedException e) { 67 | Log.w("Event", Thread.currentThread().getName() + " was interruppted", e); 68 | } 69 | } finally { 70 | executorRunning = false; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/EventBus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import android.os.Looper; 19 | import android.util.Log; 20 | 21 | import java.lang.reflect.InvocationTargetException; 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Set; 27 | import java.util.concurrent.ConcurrentHashMap; 28 | import java.util.concurrent.CopyOnWriteArrayList; 29 | import java.util.concurrent.ExecutorService; 30 | 31 | /** 32 | * EventBus is a central publish/subscribe event system for Android. Events are posted 33 | * ({@link #post(Object)}) to the 34 | * bus, which delivers it to subscribers that have a matching handler method for the event type. 35 | * To receive events, 36 | * subscribers must register themselves to the bus using {@link #register(Object)}. Once registered, 37 | * subscribers receive events until {@link #unregister(Object)} is called. By convention, event 38 | * handling methods must 39 | * be named "onEvent", be public, return nothing (void), and have exactly one parameter (the event). 40 | * 41 | * @author Markus Junginger, greenrobot 42 | */ 43 | public class EventBus { 44 | 45 | /** 46 | * Log tag, apps may override it. 47 | */ 48 | public static String TAG = "Event"; 49 | 50 | static volatile EventBus defaultInstance; 51 | 52 | private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); 53 | 54 | //key:事件类型,value:由事件的所有父类,父类的接口,父类接口的父类 组成的集合 55 | private static final Map, List>> eventTypesCache = new HashMap, 56 | List>>(); 57 | 58 | //key:订阅的事件,value:订阅这个事件的所有订阅者集合 59 | private final Map, CopyOnWriteArrayList> subscriptionsByEventType; 60 | //当前订阅者订阅了哪些事件 61 | //key:订阅者对象,value:这个订阅者订阅的事件集合 62 | private final Map>> typesBySubscriber; 63 | 64 | private final Map, Object> stickyEvents; 65 | 66 | private final ThreadLocal currentPostingThreadState = new 67 | ThreadLocal() { 68 | @Override 69 | protected PostingThreadState initialValue() { 70 | return new PostingThreadState(); 71 | } 72 | }; 73 | 74 | private final HandlerPoster mainThreadPoster; //前台发送者 75 | private final BackgroundPoster backgroundPoster; //后台发送者 76 | private final AsyncPoster asyncPoster; //后台发送者(只让队列第一个待订阅者去响应) 77 | private final SubscriberMethodFinder subscriberMethodFinder; //订阅者方法查询 78 | private final ExecutorService executorService; //线程池执行器 79 | 80 | //同EventBusBuilder中的属性 81 | private final boolean throwSubscriberException; 82 | private final boolean logSubscriberExceptions; 83 | private final boolean logNoSubscriberMessages; //如果某个事件没有订阅者,是否显示一条log 84 | private final boolean sendSubscriberExceptionEvent; 85 | private final boolean sendNoSubscriberEvent; //如果某个事件没有订阅者,是否发送一个特定的事件 86 | private final boolean eventInheritance;//event的子类是否也能响应订阅者 87 | 88 | /** 89 | * Convenience singleton for apps using a process-wide EventBus instance. 90 | */ 91 | public static EventBus getDefault() { 92 | if (defaultInstance == null) { 93 | synchronized (EventBus.class) { 94 | if (defaultInstance == null) { 95 | defaultInstance = new EventBus(); 96 | } 97 | } 98 | } 99 | return defaultInstance; 100 | } 101 | 102 | /** 103 | * Creates a new EventBus instance; each instance is a separate scope in which events are 104 | * delivered. To use a 105 | * central bus, consider {@link #getDefault()}. 106 | */ 107 | public EventBus() { 108 | this(DEFAULT_BUILDER); 109 | } 110 | 111 | EventBus(EventBusBuilder builder) { 112 | subscriptionsByEventType = new HashMap, CopyOnWriteArrayList>(); 113 | typesBySubscriber = new HashMap>>(); 114 | stickyEvents = new ConcurrentHashMap, Object>(); 115 | mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); 116 | backgroundPoster = new BackgroundPoster(this); 117 | asyncPoster = new AsyncPoster(this); 118 | subscriberMethodFinder = new SubscriberMethodFinder(builder 119 | .skipMethodVerificationForClasses); 120 | logSubscriberExceptions = builder.logSubscriberExceptions; 121 | logNoSubscriberMessages = builder.logNoSubscriberMessages; 122 | sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; 123 | sendNoSubscriberEvent = builder.sendNoSubscriberEvent; 124 | throwSubscriberException = builder.throwSubscriberException; 125 | eventInheritance = builder.eventInheritance; 126 | executorService = builder.executorService; 127 | } 128 | 129 | /** 130 | * @param subscriber 订阅者对象 131 | */ 132 | public void register(Object subscriber) { 133 | register(subscriber, false, 0); 134 | } 135 | 136 | /** 137 | * @param subscriber 订阅者对象 138 | * @param priority 优先级 139 | */ 140 | public void register(Object subscriber, int priority) { 141 | register(subscriber, false, priority); 142 | } 143 | 144 | /** 145 | * @param subscriber 订阅者对象 146 | */ 147 | public void registerSticky(Object subscriber) { 148 | register(subscriber, true, 0); 149 | } 150 | 151 | /** 152 | * @param subscriber 订阅者对象 153 | * @param priority 优先级 154 | */ 155 | public void registerSticky(Object subscriber, int priority) { 156 | register(subscriber, true, priority); 157 | } 158 | 159 | /** 160 | * @param subscriber 订阅者对象 161 | * @param sticky 是否有序 162 | * @param priority 优先级 163 | */ 164 | private synchronized void register(Object subscriber, boolean sticky, int priority) { 165 | List subscriberMethods = subscriberMethodFinder.findSubscriberMethods 166 | (subscriber.getClass()); 167 | for (SubscriberMethod subscriberMethod : subscriberMethods) { 168 | subscribe(subscriber, subscriberMethod, sticky, priority); 169 | } 170 | } 171 | 172 | /** 173 | * 必须在同步代码块调用 174 | * 175 | * @param subscriber 订阅者对象 176 | * @param subscriberMethod 响应的方法名 177 | * @param sticky 是否有序 178 | * @param priority 优先级 179 | */ 180 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, 181 | int priority) { 182 | //根据传入的响应方法名获取到响应事件(参数类型) 183 | Class eventType = subscriberMethod.eventType; 184 | Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); 185 | //通过响应事件作为key,并取得这个事件类型将会响应的全部订阅者 186 | //没个订阅者至少会订阅一个事件,多个订阅者可能订阅同一个事件(多对多) 187 | //key:订阅的事件,value:订阅这个事件的所有订阅者集合 188 | CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); 189 | if (subscriptions == null) { 190 | subscriptions = new CopyOnWriteArrayList(); 191 | subscriptionsByEventType.put(eventType, subscriptions); 192 | } else { 193 | if (subscriptions.contains(newSubscription)) { 194 | throw new EventBusException("Subscriber " + subscriber.getClass() + " already " + 195 | "registered to event " + eventType); 196 | } 197 | } 198 | 199 | //根据优先级插入到订阅者集合中 200 | int size = subscriptions.size(); 201 | for (int i = 0; i <= size; i++) { 202 | if (i == size || newSubscription.priority > subscriptions.get(i).priority) { 203 | subscriptions.add(i, newSubscription); 204 | break; 205 | } 206 | } 207 | 208 | //当前订阅者订阅了哪些事件 209 | List> subscribedEvents = typesBySubscriber.get(subscriber); 210 | if (subscribedEvents == null) { 211 | subscribedEvents = new ArrayList>(); 212 | typesBySubscriber.put(subscriber, subscribedEvents); 213 | } 214 | subscribedEvents.add(eventType); 215 | 216 | if (sticky) { 217 | if (eventInheritance) { 218 | // 注:遍历所有的事件可能是低效的,有很多黏事件,因此数据结构应该改变,以便更有效的查找 219 | // (例如额外的地图存储超类的子类:类 - >列表<类>)。 220 | Set, Object>> entries = stickyEvents.entrySet(); 221 | for (Map.Entry, Object> entry : entries) { 222 | Class candidateEventType = entry.getKey(); 223 | //如果eventtype是candidateEventType同一个类或是其子类 224 | if (eventType.isAssignableFrom(candidateEventType)) { 225 | Object stickyEvent = entry.getValue(); 226 | checkPostStickyEventToSubscription(newSubscription, stickyEvent); 227 | } 228 | } 229 | } else { 230 | Object stickyEvent = stickyEvents.get(eventType); 231 | checkPostStickyEventToSubscription(newSubscription, stickyEvent); 232 | } 233 | } 234 | } 235 | 236 | private void checkPostStickyEventToSubscription(Subscription newSubscription, Object 237 | stickyEvent) { 238 | if (stickyEvent != null) { 239 | // If the subscriber is trying to abort the event, it will fail (event is not tracked 240 | // in posting state) 241 | // --> Strange corner case, which we don't take care of here. 242 | postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper 243 | .myLooper()); 244 | } 245 | } 246 | 247 | public synchronized boolean isRegistered(Object subscriber) { 248 | return typesBySubscriber.containsKey(subscriber); 249 | } 250 | 251 | /** 252 | * Unregisters the given subscriber from all event classes. 253 | */ 254 | public synchronized void unregister(Object subscriber) { 255 | List> subscribedTypes = typesBySubscriber.get(subscriber); 256 | if (subscribedTypes != null) { 257 | for (Class eventType : subscribedTypes) { 258 | //取消注册subscriber对eventType事件的响应 259 | unsubscribeByEventType(subscriber, eventType); 260 | } 261 | //当subscriber对所有事件都不响应以后,移除订阅者 262 | typesBySubscriber.remove(subscriber); 263 | } else { 264 | Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber 265 | .getClass()); 266 | } 267 | } 268 | 269 | /** 270 | * 取消注册订阅者对参数eventType的响应 271 | * 注:只更新subscriptionsByEventType,不更新typesBySubscriber!调用者必须手动更新typesBySubscriber。 272 | */ 273 | private void unsubscribeByEventType(Object subscriber, Class eventType) { 274 | List subscriptions = subscriptionsByEventType.get(eventType); 275 | if (subscriptions != null) { 276 | int size = subscriptions.size(); 277 | for (int i = 0; i < size; i++) { 278 | Subscription subscription = subscriptions.get(i); 279 | if (subscription.subscriber == subscriber) { 280 | subscription.active = false; 281 | subscriptions.remove(i); 282 | i--; 283 | size--; 284 | } 285 | } 286 | } 287 | } 288 | 289 | /** 290 | * Posts the given event to the event bus. 291 | */ 292 | public void post(Object event) { 293 | PostingThreadState postingState = currentPostingThreadState.get(); 294 | List eventQueue = postingState.eventQueue; 295 | eventQueue.add(event); 296 | 297 | if (!postingState.isPosting) { 298 | postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); 299 | postingState.isPosting = true; 300 | if (postingState.canceled) { 301 | throw new EventBusException("Internal error. Abort state was not reset"); 302 | } 303 | try { 304 | while (!eventQueue.isEmpty()) { 305 | postSingleEvent(eventQueue.remove(0), postingState); 306 | } 307 | } finally { 308 | postingState.isPosting = false; 309 | postingState.isMainThread = false; 310 | } 311 | } 312 | } 313 | 314 | /** 315 | * Called from a subscriber's event handling method, further event delivery will be canceled. 316 | * Subsequent 317 | * subscribers 318 | * won't receive the event. Events are usually canceled by higher priority subscribers (see 319 | * {@link #register(Object, int)}). Canceling is restricted to event handling methods running 320 | * in posting thread 321 | * {@link ThreadMode#PostThread}. 322 | */ 323 | public void cancelEventDelivery(Object event) { 324 | PostingThreadState postingState = currentPostingThreadState.get(); 325 | if (!postingState.isPosting) { 326 | throw new EventBusException( 327 | "This method may only be called from inside event handling methods on the " + 328 | "posting thread"); 329 | } else if (event == null) { 330 | throw new EventBusException("Event may not be null"); 331 | } else if (postingState.event != event) { 332 | throw new EventBusException("Only the currently handled event may be aborted"); 333 | } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.PostThread) { 334 | throw new EventBusException(" event handlers may only abort the incoming event"); 335 | } 336 | 337 | postingState.canceled = true; 338 | } 339 | 340 | /** 341 | * Posts the given event to the event bus and holds on to the event (because it is sticky). 342 | * The most recent sticky 343 | * event of an event's type is kept in memory for future access. This can be 344 | * {@link #registerSticky(Object)} or 345 | * {@link #getStickyEvent(Class)}. 346 | */ 347 | public void postSticky(Object event) { 348 | synchronized (stickyEvents) { 349 | stickyEvents.put(event.getClass(), event); 350 | } 351 | // Should be posted after it is putted, in case the subscriber wants to remove immediately 352 | post(event); 353 | } 354 | 355 | /** 356 | * Gets the most recent sticky event for the given type. 357 | * 358 | * @see #postSticky(Object) 359 | */ 360 | public T getStickyEvent(Class eventType) { 361 | synchronized (stickyEvents) { 362 | return eventType.cast(stickyEvents.get(eventType)); 363 | } 364 | } 365 | 366 | /** 367 | * Remove and gets the recent sticky event for the given event type. 368 | * 369 | * @see #postSticky(Object) 370 | */ 371 | public T removeStickyEvent(Class eventType) { 372 | synchronized (stickyEvents) { 373 | return eventType.cast(stickyEvents.remove(eventType)); 374 | } 375 | } 376 | 377 | /** 378 | * Removes the sticky event if it equals to the given event. 379 | * 380 | * @return true if the events matched and the sticky event was removed. 381 | */ 382 | public boolean removeStickyEvent(Object event) { 383 | synchronized (stickyEvents) { 384 | Class eventType = event.getClass(); 385 | Object existingEvent = stickyEvents.get(eventType); 386 | if (event.equals(existingEvent)) { 387 | stickyEvents.remove(eventType); 388 | return true; 389 | } else { 390 | return false; 391 | } 392 | } 393 | } 394 | 395 | /** 396 | * Removes all sticky events. 397 | */ 398 | public void removeAllStickyEvents() { 399 | synchronized (stickyEvents) { 400 | stickyEvents.clear(); 401 | } 402 | } 403 | 404 | public boolean hasSubscriberForEvent(Class eventClass) { 405 | List> eventTypes = lookupAllEventTypes(eventClass); 406 | if (eventTypes != null) { 407 | int countTypes = eventTypes.size(); 408 | for (int h = 0; h < countTypes; h++) { 409 | Class clazz = eventTypes.get(h); 410 | CopyOnWriteArrayList subscriptions; 411 | synchronized (this) { 412 | subscriptions = subscriptionsByEventType.get(clazz); 413 | } 414 | if (subscriptions != null && !subscriptions.isEmpty()) { 415 | return true; 416 | } 417 | } 418 | } 419 | return false; 420 | } 421 | 422 | /** 423 | * 发送一次事件 424 | * 425 | * @param event 426 | * @param postingState 427 | * @throws Error 428 | */ 429 | private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { 430 | Class eventClass = event.getClass(); 431 | boolean subscriptionFound = false; 432 | if (eventInheritance) { 433 | //获取到eventClass所有父类的集合 434 | List> eventTypes = lookupAllEventTypes(eventClass); 435 | int countTypes = eventTypes.size(); 436 | for (int h = 0; h < countTypes; h++) { 437 | Class clazz = eventTypes.get(h); 438 | //左或右只要有一个为真则为真,并赋值给左 439 | subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); 440 | } 441 | } else { 442 | subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); 443 | } 444 | if (!subscriptionFound) { 445 | if (logNoSubscriberMessages) { 446 | Log.d(TAG, "No subscribers registered for event " + eventClass); 447 | } 448 | 449 | //参考sendNoSubscriberEvent注释 450 | if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && 451 | eventClass != SubscriberExceptionEvent.class) { 452 | post(new NoSubscriberEvent(this, event)); 453 | } 454 | } 455 | } 456 | 457 | /** 458 | * 找出所有订阅了eventClass事件的订阅者,并回调订阅者的响应方法 459 | * 460 | * @param event 要响应的事件 461 | * @param postingState 事件发送过程所需数据的封装 462 | * @param eventClass 响应的事件类型(也可能是event的父类类型) 463 | * @return 如果eventClass事件没有订阅者, 返回false 464 | */ 465 | private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, 466 | Class eventClass) { 467 | CopyOnWriteArrayList subscriptions; 468 | synchronized (this) { 469 | //所有订阅了eventClass的事件集合 470 | subscriptions = subscriptionsByEventType.get(eventClass); 471 | } 472 | if (subscriptions != null && !subscriptions.isEmpty()) { 473 | //回调subscription的响应方法 474 | for (Subscription subscription : subscriptions) { 475 | postingState.event = event; 476 | postingState.subscription = subscription; 477 | boolean aborted = false; 478 | try { 479 | postToSubscription(subscription, event, postingState.isMainThread); 480 | aborted = postingState.canceled; 481 | } finally { 482 | postingState.event = null; 483 | postingState.subscription = null; 484 | postingState.canceled = false; 485 | } 486 | if (aborted) { 487 | break; 488 | } 489 | } 490 | return true; 491 | } 492 | return false; 493 | } 494 | 495 | /** 496 | * 回调订阅者的响应方法 497 | * 498 | * @param subscription 订阅者对象的封装 499 | * @param event 要响应的事件 500 | * @param isMainThread 是否在UI线程中 501 | */ 502 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { 503 | switch (subscription.subscriberMethod.threadMode) { 504 | case PostThread: 505 | //直接调用响应方法 506 | invokeSubscriber(subscription, event); 507 | break; 508 | case MainThread: 509 | //如果是主线程则直接调用响应事件,否则使用handle去在主线程响应事件 510 | if (isMainThread) { 511 | invokeSubscriber(subscription, event); 512 | } else { 513 | mainThreadPoster.enqueue(subscription, event); 514 | } 515 | break; 516 | case BackgroundThread: 517 | //如果要求是在后台线程回调,后台线程使用相应的线程 518 | if (isMainThread) { 519 | backgroundPoster.enqueue(subscription, event); 520 | } else { 521 | invokeSubscriber(subscription, event); 522 | } 523 | break; 524 | case Async: 525 | asyncPoster.enqueue(subscription, event); 526 | break; 527 | default: 528 | throw new IllegalStateException("Unknown thread mode: " + subscription 529 | .subscriberMethod.threadMode); 530 | } 531 | } 532 | 533 | /** 534 | * 将参数eventClass的所有父类,父类的接口,父类接口的父类,全部添加到eventTypesCache集合中 535 | */ 536 | private List> lookupAllEventTypes(Class eventClass) { 537 | synchronized (eventTypesCache) { 538 | List> eventTypes = eventTypesCache.get(eventClass); 539 | if (eventTypes == null) { 540 | eventTypes = new ArrayList>(); 541 | Class clazz = eventClass; 542 | 543 | //通过循环,将父类,父类的接口,父类接口的父类,全部添加到eventTypes集合中 544 | while (clazz != null) { 545 | eventTypes.add(clazz); 546 | addInterfaces(eventTypes, clazz.getInterfaces()); 547 | clazz = clazz.getSuperclass(); 548 | } 549 | 550 | eventTypesCache.put(eventClass, eventTypes); 551 | } 552 | return eventTypes; 553 | } 554 | } 555 | 556 | /** 557 | * 通过递归,将所有interfaces以及其父类接口添加到eventTypes中 558 | * 559 | * @param eventTypes 容纳接口集合 560 | * @param interfaces 要遍历的接口 561 | */ 562 | static void addInterfaces(List> eventTypes, Class[] interfaces) { 563 | for (Class interfaceClass : interfaces) { 564 | // 只要当前接口没有被添加,就添加到集合中,并再次查找当前接口的父类接口 565 | if (!eventTypes.contains(interfaceClass)) { 566 | eventTypes.add(interfaceClass); 567 | addInterfaces(eventTypes, interfaceClass.getInterfaces()); 568 | } 569 | } 570 | } 571 | 572 | /** 573 | * Invokes the subscriber if the subscriptions is still active. Skipping subscriptions 574 | * prevents race conditions between {@link #unregister(Object)} and event delivery. 575 | * Otherwise the event might be delivered after the subscriber unregistered. 576 | * This is particularly important for main thread delivery and 577 | * registrations bound to the live cycle of an Activity or Fragment. 578 | */ 579 | void invokeSubscriber(PendingPost pendingPost) { 580 | Object event = pendingPost.event; 581 | Subscription subscription = pendingPost.subscription; 582 | PendingPost.releasePendingPost(pendingPost); 583 | if (subscription.active) { 584 | invokeSubscriber(subscription, event); 585 | } 586 | } 587 | 588 | /** 589 | * 调用订阅者中的回调方法去响应事件 590 | * 591 | * @param subscription 订阅者封装类的对象 592 | * @param event 要响应的事件 593 | */ 594 | void invokeSubscriber(Subscription subscription, Object event) { 595 | try { 596 | //调用subscription.subscriber对象中的subscription.subscriberMethod.method方法,并传递参数event 597 | subscription.subscriberMethod.method.invoke(subscription.subscriber, event); 598 | } catch (InvocationTargetException e) { 599 | handleSubscriberException(subscription, event, e.getCause()); 600 | } catch (IllegalAccessException e) { 601 | throw new IllegalStateException("Unexpected exception", e); 602 | } 603 | } 604 | 605 | private void handleSubscriberException(Subscription subscription, Object event, Throwable 606 | cause) { 607 | if (event instanceof SubscriberExceptionEvent) { 608 | if (logSubscriberExceptions) { 609 | // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, 610 | // just log 611 | Log.e(TAG, "SubscriberExceptionEvent subscriber " + subscription.subscriber 612 | .getClass() 613 | + " threw an exception", cause); 614 | SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event; 615 | Log.e(TAG, "Initial event " + exEvent.causingEvent + " caused exception in " 616 | + exEvent.causingSubscriber, exEvent.throwable); 617 | } 618 | } else { 619 | if (throwSubscriberException) { 620 | throw new EventBusException("Invoking subscriber failed", cause); 621 | } 622 | if (logSubscriberExceptions) { 623 | Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing " + 624 | "class " 625 | + subscription.subscriber.getClass(), cause); 626 | } 627 | if (sendSubscriberExceptionEvent) { 628 | SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event, 629 | subscription.subscriber); 630 | post(exEvent); 631 | } 632 | } 633 | } 634 | 635 | /** 636 | * 事件发送过程所需数据的封装 637 | */ 638 | final static class PostingThreadState { 639 | //通过post方法参数传入的事件集合 640 | final List eventQueue = new ArrayList(); 641 | boolean isPosting; //是否正在执行postSingleEvent()方法 642 | boolean isMainThread; 643 | Subscription subscription; 644 | Object event; 645 | boolean canceled; 646 | } 647 | 648 | ExecutorService getExecutorService() { 649 | return executorService; 650 | } 651 | 652 | // Just an idea: we could provide a callback to post() to be notified, an alternative would 653 | // be events, of course... 654 | /* public */interface PostCallback { 655 | void onPostCompleted(List exceptionEvents); 656 | } 657 | } 658 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/EventBusBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.concurrent.ExecutorService; 21 | import java.util.concurrent.Executors; 22 | 23 | /** 24 | * 使用自定义的参数创建EventBus实例,也可以使用默认的build()创建实例 25 | */ 26 | public class EventBusBuilder { 27 | private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); 28 | 29 | boolean logSubscriberExceptions = true;//监听异常日志 30 | boolean logNoSubscriberMessages = true; //如果没有订阅者,显示一个Log 31 | boolean sendSubscriberExceptionEvent = true; //发送监听到异常事件 32 | boolean sendNoSubscriberEvent = true; //如果没有订阅者,发送一条默认事件 33 | boolean throwSubscriberException; //如果失败则抛出异常 34 | boolean eventInheritance = true; //event的子类是否也能响应订阅者 35 | ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; 36 | List> skipMethodVerificationForClasses; 37 | 38 | EventBusBuilder() { 39 | } 40 | 41 | /** 42 | * Default: true 43 | */ 44 | public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) { 45 | this.logSubscriberExceptions = logSubscriberExceptions; 46 | return this; 47 | } 48 | 49 | /** 50 | * Default: true 51 | */ 52 | public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) { 53 | this.logNoSubscriberMessages = logNoSubscriberMessages; 54 | return this; 55 | } 56 | 57 | /** 58 | * Default: true 59 | */ 60 | public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) { 61 | this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent; 62 | return this; 63 | } 64 | 65 | /** 66 | * Default: true 67 | */ 68 | public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) { 69 | this.sendNoSubscriberEvent = sendNoSubscriberEvent; 70 | return this; 71 | } 72 | 73 | /** 74 | * Fails if an subscriber throws an exception (default: false). 75 | *

76 | * Tip:建议与BuildConfig.DEBUG配合使用,用于调试模式时显示崩溃日志。 77 | */ 78 | public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) { 79 | this.throwSubscriberException = throwSubscriberException; 80 | return this; 81 | } 82 | 83 | /** 84 | * 事件继承?event类型的子类也能响应订阅者 85 | * By default, EventBus considers the event class hierarchy (subscribers to super classes 86 | * will be notified). 87 | * Switching this feature off will improve posting of events. For simple event classes 88 | * extending Object directly, 89 | * we measured a speed up of 20% for event posting. For more complex event hierarchies, the 90 | * speed up should be >20%. 91 | */ 92 | public EventBusBuilder eventInheritance(boolean eventInheritance) { 93 | this.eventInheritance = eventInheritance; 94 | return this; 95 | } 96 | 97 | 98 | /** 99 | * Provide a custom thread pool to EventBus used for async and background event delivery. 100 | * This is an advanced setting to that can break things: ensure the given ExecutorService 101 | * won't get stuck to 102 | * avoid undefined behavior. 103 | */ 104 | public EventBusBuilder executorService(ExecutorService executorService) { 105 | this.executorService = executorService; 106 | return this; 107 | } 108 | 109 | /** 110 | * Method name verification is done for methods starting with onEvent to avoid typos; using 111 | * this method you can exclude subscriber classes from this check. 112 | * Also disables checks for method modifiers 113 | * (public, not static nor 114 | * abstract). 115 | */ 116 | public EventBusBuilder skipMethodVerificationFor(Class clazz) { 117 | if (skipMethodVerificationForClasses == null) { 118 | skipMethodVerificationForClasses = new ArrayList>(); 119 | } 120 | skipMethodVerificationForClasses.add(clazz); 121 | return this; 122 | } 123 | 124 | /** 125 | * 根据参数创建对象,并赋值给EventBus.defaultInstance, 必须在默认的eventbus对象使用以前调用 126 | * 127 | * @throws EventBusException if there's already a default EventBus instance in place 128 | */ 129 | public EventBus installDefaultEventBus() { 130 | synchronized (EventBus.class) { 131 | if (EventBus.defaultInstance != null) { 132 | throw new EventBusException("Default instance already exists." + 133 | " It may be only set once before it's used the first time to ensure " + 134 | "consistent behavior."); 135 | } 136 | EventBus.defaultInstance = build(); 137 | return EventBus.defaultInstance; 138 | } 139 | } 140 | 141 | /** 142 | * 根据参数创建对象 143 | */ 144 | public EventBus build() { 145 | return new EventBus(this); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/EventBusException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | /** 19 | * An {@link RuntimeException} thrown in cases something went wrong inside EventBus. 20 | * 21 | * @author Markus 22 | * 23 | */ 24 | public class EventBusException extends RuntimeException { 25 | 26 | private static final long serialVersionUID = -2912559384646531479L; 27 | 28 | public EventBusException(String detailMessage) { 29 | super(detailMessage); 30 | } 31 | 32 | public EventBusException(Throwable throwable) { 33 | super(throwable); 34 | } 35 | 36 | public EventBusException(String detailMessage, Throwable throwable) { 37 | super(detailMessage, throwable); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/HandlerPoster.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | import android.os.Message; 21 | import android.os.SystemClock; 22 | 23 | final class HandlerPoster extends Handler { 24 | 25 | private final PendingPostQueue queue; 26 | private final int maxMillisInsideHandleMessage; 27 | private final EventBus eventBus; 28 | private boolean handlerActive; //用于标识当前queue中是否有正在发送对象的任务 29 | 30 | HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { 31 | super(looper); 32 | this.eventBus = eventBus; 33 | this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; 34 | queue = new PendingPostQueue(); 35 | } 36 | 37 | /** 38 | * 根据参数创建待发送对象并加入队列,如果入队成功,则发送一条空消息让handleMessage响应 39 | * 40 | * @param subscription 订阅者 41 | * @param event 订阅事件 42 | */ 43 | void enqueue(Subscription subscription, Object event) { 44 | PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); 45 | synchronized (this) { 46 | queue.enqueue(pendingPost); 47 | if (!handlerActive) { 48 | handlerActive = true; 49 | if (!sendMessage(obtainMessage())) { 50 | throw new EventBusException("Could not send handler message"); 51 | } 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | public void handleMessage(Message msg) { 58 | boolean rescheduled = false; 59 | try { 60 | long started = SystemClock.uptimeMillis(); 61 | while (true) { 62 | PendingPost pendingPost = queue.poll(); 63 | if (pendingPost == null) { 64 | synchronized (this) { 65 | // 双重校验,类似单例中的实现 66 | pendingPost = queue.poll(); 67 | if (pendingPost == null) { 68 | handlerActive = false; 69 | return; 70 | } 71 | } 72 | } 73 | //如果订阅者没有取消注册,则分发消息 74 | eventBus.invokeSubscriber(pendingPost); 75 | 76 | //如果在一定时间内仍然没有发完队列中所有的待发送者,则退出 77 | long timeInMethod = SystemClock.uptimeMillis() - started; 78 | if (timeInMethod >= maxMillisInsideHandleMessage) { 79 | if (!sendMessage(obtainMessage())) { 80 | throw new EventBusException("Could not send handler message"); 81 | } 82 | rescheduled = true; 83 | return; 84 | } 85 | } 86 | } finally { 87 | handlerActive = rescheduled; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/NoSubscriberEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | /** 19 | * This Event is posted by EventBus when no subscriber is found for a posted event. 20 | * 21 | * @author Markus 22 | */ 23 | public final class NoSubscriberEvent { 24 | /** The {@link EventBus} instance to with the original event was posted to. */ 25 | public final EventBus eventBus; 26 | 27 | /** The original event that could not be delivered to any subscriber. */ 28 | public final Object originalEvent; 29 | 30 | public NoSubscriberEvent(EventBus eventBus, Object originalEvent) { 31 | this.eventBus = eventBus; 32 | this.originalEvent = originalEvent; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/PendingPost.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | final class PendingPost { 22 | //单例池,复用对象 23 | private final static List pendingPostPool = new ArrayList(); 24 | 25 | Object event; //事件类型 26 | Subscription subscription; //订阅者 27 | PendingPost next; //队列下一个待发送对象 28 | 29 | private PendingPost(Object event, Subscription subscription) { 30 | this.event = event; 31 | this.subscription = subscription; 32 | } 33 | 34 | /** 35 | * 首先检查复用池中是否有可用,如果有则返回复用,否则返回一个新的 36 | * 37 | * @param subscription 订阅者 38 | * @param event 订阅事件 39 | * @return 待发送对象 40 | */ 41 | static PendingPost obtainPendingPost(Subscription subscription, Object event) { 42 | synchronized (pendingPostPool) { 43 | int size = pendingPostPool.size(); 44 | if (size > 0) { 45 | PendingPost pendingPost = pendingPostPool.remove(size - 1); 46 | pendingPost.event = event; 47 | pendingPost.subscription = subscription; 48 | pendingPost.next = null; 49 | return pendingPost; 50 | } 51 | } 52 | return new PendingPost(event, subscription); 53 | } 54 | 55 | /** 56 | * 回收一个待发送对象,并加入复用池 57 | * 58 | * @param pendingPost 待回收的待发送对象 59 | */ 60 | static void releasePendingPost(PendingPost pendingPost) { 61 | pendingPost.event = null; 62 | pendingPost.subscription = null; 63 | pendingPost.next = null; 64 | synchronized (pendingPostPool) { 65 | // 防止池无限增长 66 | if (pendingPostPool.size() < 1000) { 67 | pendingPostPool.add(pendingPost); 68 | } 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/PendingPostQueue.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event; 2 | 3 | final class PendingPostQueue { 4 | 5 | private PendingPost head; //待发送对象队列头节点 6 | private PendingPost tail;//待发送对象队列尾节点 7 | 8 | /** 9 | * 入队 10 | */ 11 | synchronized void enqueue(PendingPost pendingPost) { 12 | if (pendingPost == null) { 13 | throw new NullPointerException("null cannot be enqueued"); 14 | } 15 | if (tail != null) { 16 | tail.next = pendingPost; 17 | tail = pendingPost; 18 | } else if (head == null) { 19 | head = tail = pendingPost; 20 | } else { 21 | throw new IllegalStateException("Head present, but no tail"); 22 | } 23 | notifyAll(); 24 | } 25 | 26 | /** 27 | * 取队列头节点的待发送对象 28 | */ 29 | synchronized PendingPost poll() { 30 | PendingPost pendingPost = head; 31 | if (head != null) { 32 | head = head.next; 33 | if (head == null) { 34 | tail = null; 35 | } 36 | } 37 | return pendingPost; 38 | } 39 | 40 | /** 41 | * 取待发送对象队列头节点的待发送对象 42 | */ 43 | synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException { 44 | if (head == null) { 45 | wait(maxMillisToWait); 46 | } 47 | return poll(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/SubscriberExceptionEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | /** 19 | * This Event is posted by EventBus when an exception occurs inside a subscriber's event handling method. 20 | * 21 | * @author Markus 22 | */ 23 | public final class SubscriberExceptionEvent { 24 | /** The {@link EventBus} instance to with the original event was posted to. */ 25 | public final EventBus eventBus; 26 | 27 | /** The Throwable thrown by a subscriber. */ 28 | public final Throwable throwable; 29 | 30 | /** The original event that could not be delivered to any subscriber. */ 31 | public final Object causingEvent; 32 | 33 | /** The subscriber that threw the Throwable. */ 34 | public final Object causingSubscriber; 35 | 36 | public SubscriberExceptionEvent(EventBus eventBus, Throwable throwable, Object causingEvent, 37 | Object causingSubscriber) { 38 | this.eventBus = eventBus; 39 | this.throwable = throwable; 40 | this.causingEvent = causingEvent; 41 | this.causingSubscriber = causingSubscriber; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/SubscriberMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | final class SubscriberMethod { 21 | final Method method; //方法名 22 | final ThreadMode threadMode; //工作在哪个线程 23 | final Class eventType; //参数类型 24 | /** Used for efficient comparison */ 25 | String methodString; 26 | 27 | SubscriberMethod(Method method, ThreadMode threadMode, Class eventType) { 28 | this.method = method; 29 | this.threadMode = threadMode; 30 | this.eventType = eventType; 31 | } 32 | 33 | @Override 34 | public boolean equals(Object other) { 35 | if (other instanceof SubscriberMethod) { 36 | checkMethodString(); 37 | SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other; 38 | otherSubscriberMethod.checkMethodString(); 39 | // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6 40 | return methodString.equals(otherSubscriberMethod.methodString); 41 | } else { 42 | return false; 43 | } 44 | } 45 | 46 | private synchronized void checkMethodString() { 47 | if (methodString == null) { 48 | // Method.toString has more overhead, just take relevant parts of the method 49 | StringBuilder builder = new StringBuilder(64); 50 | builder.append(method.getDeclaringClass().getName()); 51 | builder.append('#').append(method.getName()); 52 | builder.append('(').append(eventType.getName()); 53 | methodString = builder.toString(); 54 | } 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return method.hashCode(); 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/SubscriberMethodFinder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | import android.util.Log; 19 | 20 | import java.lang.reflect.Method; 21 | import java.lang.reflect.Modifier; 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Map; 27 | import java.util.concurrent.ConcurrentHashMap; 28 | 29 | class SubscriberMethodFinder { 30 | private static final String ON_EVENT_METHOD_NAME = "onEvent"; 31 | 32 | /** 33 | * 在较新的类文件,编译器可能会添加方法。那些被称为BRIDGE或SYNTHETIC方法。 34 | * EventBus必须忽略两者。有修饰符没有公开,但在Java类文件中有格式定义 35 | */ 36 | private static final int BRIDGE = 0x40; 37 | private static final int SYNTHETIC = 0x1000; 38 | //需要忽略的修饰符 39 | private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | 40 | SYNTHETIC; 41 | 42 | //key:类名,value:该类中需要相应的方法集合 43 | private static final Map> methodCache = new HashMap>(); 45 | 46 | //跳过校验方法的类(即通过构造函数传入的集合) 47 | private final Map, Class> skipMethodVerificationForClasses; 48 | 49 | /** 50 | * 构造方法 51 | * 52 | * @param skipMethodVerificationForClassesList 需要跳过校验方法的类 53 | */ 54 | SubscriberMethodFinder(List> skipMethodVerificationForClassesList) { 55 | skipMethodVerificationForClasses = new ConcurrentHashMap, Class>(); 56 | if (skipMethodVerificationForClassesList != null) { 57 | for (Class clazz : skipMethodVerificationForClassesList) { 58 | skipMethodVerificationForClasses.put(clazz, clazz); 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * 查找一个类中全部的需要响应的订阅方法 65 | * 66 | * @param subscriberClass 待查找的类 67 | */ 68 | List findSubscriberMethods(Class subscriberClass) { 69 | String key = subscriberClass.getName(); 70 | List subscriberMethods; 71 | synchronized (methodCache) { 72 | subscriberMethods = methodCache.get(key); 73 | } 74 | if (subscriberMethods != null) { 75 | return subscriberMethods; 76 | } 77 | 78 | subscriberMethods = new ArrayList(); 79 | Class clazz = subscriberClass; 80 | HashSet eventTypesFound = new HashSet(); 81 | StringBuilder methodKeyBuilder = new StringBuilder(); 82 | while (clazz != null) { 83 | String name = clazz.getName(); 84 | if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android" + 85 | ".")) { 86 | // Skip system classes, this just degrades performance 87 | break; 88 | } 89 | 90 | // 从2.2版本开始,响应的方法必须是public的 (might change with annotations again) 91 | Method[] methods = clazz.getDeclaredMethods(); 92 | for (Method method : methods) { 93 | String methodName = method.getName(); 94 | if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { 95 | int modifiers = method.getModifiers();//方法的修饰符 96 | //如果是public,且 不是之前定义要忽略的类型 97 | if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { 98 | Class[] parameterTypes = method.getParameterTypes(); 99 | //如果只有一个参数 100 | if (parameterTypes.length == 1) { 101 | String modifierString = methodName.substring(ON_EVENT_METHOD_NAME 102 | .length()); 103 | ThreadMode threadMode; 104 | if (modifierString.length() == 0) { 105 | threadMode = ThreadMode.PostThread; 106 | } else if (modifierString.equals("MainThread")) { 107 | threadMode = ThreadMode.MainThread; 108 | } else if (modifierString.equals("BackgroundThread")) { 109 | threadMode = ThreadMode.BackgroundThread; 110 | } else if (modifierString.equals("Async")) { 111 | threadMode = ThreadMode.Async; 112 | } else { 113 | if (skipMethodVerificationForClasses.containsKey(clazz)) { 114 | continue; 115 | } else { 116 | throw new EventBusException("Illegal onEvent method, check " + 117 | "for typos: " + method); 118 | } 119 | } 120 | Class eventType = parameterTypes[0]; 121 | methodKeyBuilder.setLength(0); 122 | methodKeyBuilder.append(methodName); 123 | methodKeyBuilder.append('>').append(eventType.getName()); 124 | String methodKey = methodKeyBuilder.toString(); 125 | if (eventTypesFound.add(methodKey)) { 126 | // 方法名,工作在哪个线程,事件类型 127 | subscriberMethods.add(new SubscriberMethod(method, threadMode, 128 | eventType)); 129 | } 130 | } 131 | } else if (!skipMethodVerificationForClasses.containsKey(clazz)) { 132 | Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " 133 | + clazz + "." + methodName); 134 | } 135 | } 136 | } 137 | clazz = clazz.getSuperclass(); 138 | } 139 | if (subscriberMethods.isEmpty()) { 140 | throw new EventBusException("Subscriber " + subscriberClass + " has no public methods" + 141 | " called " + ON_EVENT_METHOD_NAME); 142 | } else { 143 | synchronized (methodCache) { 144 | methodCache.put(key, subscriberMethods); 145 | } 146 | return subscriberMethods; 147 | } 148 | } 149 | 150 | static void clearCaches() { 151 | synchronized (methodCache) { 152 | methodCache.clear(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/Subscription.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | /** 19 | * 订阅者封装类 20 | */ 21 | final class Subscription { 22 | final Object subscriber; //订阅者对象 23 | final SubscriberMethod subscriberMethod; //响应的方法 24 | final int priority; //优先级 25 | /** 26 | * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked 27 | * by queued event delivery 28 | * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions. 29 | */ 30 | volatile boolean active; //是否仍在订阅中 31 | 32 | Subscription(Object subscriber, SubscriberMethod subscriberMethod, int priority) { 33 | this.subscriber = subscriber; 34 | this.subscriberMethod = subscriberMethod; 35 | this.priority = priority; 36 | active = true; 37 | } 38 | 39 | @Override 40 | public boolean equals(Object other) { 41 | if (other instanceof Subscription) { 42 | Subscription otherSubscription = (Subscription) other; 43 | return subscriber == otherSubscription.subscriber 44 | && subscriberMethod.equals(otherSubscription.subscriberMethod); 45 | } else { 46 | return false; 47 | } 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/ThreadMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event; 17 | 18 | /** 19 | * Each event handler method has a thread mode, which determines in which thread the method is to be called by EventBus. 20 | * EventBus takes care of threading independently from the posting thread. 21 | * 22 | * @see EventBus#register(Object) 23 | * @author Markus 24 | */ 25 | public enum ThreadMode { 26 | /** 27 | * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery 28 | * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for 29 | * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers 30 | * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread. 31 | */ 32 | PostThread, 33 | 34 | /** 35 | * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is 36 | * the main thread, event handler methods will be called directly. Event handlers using this mode must return 37 | * quickly to avoid blocking the main thread. 38 | */ 39 | MainThread, 40 | 41 | /** 42 | * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods 43 | * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single 44 | * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to 45 | * return quickly to avoid blocking the background thread. 46 | */ 47 | BackgroundThread, 48 | 49 | /** 50 | * Event handler methods are called in a separate thread. This is always independent from the posting thread and the 51 | * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should 52 | * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number 53 | * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus 54 | * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications. 55 | */ 56 | Async 57 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/AsyncExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event.util; 17 | 18 | import java.lang.reflect.Constructor; 19 | import java.util.concurrent.Executor; 20 | import java.util.concurrent.Executors; 21 | 22 | import android.app.Activity; 23 | import android.util.Log; 24 | import de.greenrobot.event.EventBus; 25 | 26 | /** 27 | * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events of any 28 | * given type (default is {@link ThrowableFailureEvent}). 29 | * 30 | * @author Markus 31 | */ 32 | public class AsyncExecutor { 33 | 34 | public static class Builder { 35 | private Executor threadPool; 36 | private Class failureEventType; 37 | private EventBus eventBus; 38 | 39 | private Builder() { 40 | } 41 | 42 | public Builder threadPool(Executor threadPool) { 43 | this.threadPool = threadPool; 44 | return this; 45 | } 46 | 47 | public Builder failureEventType(Class failureEventType) { 48 | this.failureEventType = failureEventType; 49 | return this; 50 | } 51 | 52 | public Builder eventBus(EventBus eventBus) { 53 | this.eventBus = eventBus; 54 | return this; 55 | } 56 | 57 | public AsyncExecutor build() { 58 | return buildForScope(null); 59 | } 60 | 61 | public AsyncExecutor buildForActivityScope(Activity activity) { 62 | return buildForScope(activity.getClass()); 63 | } 64 | 65 | public AsyncExecutor buildForScope(Object executionContext) { 66 | if (eventBus == null) { 67 | eventBus = EventBus.getDefault(); 68 | } 69 | if (threadPool == null) { 70 | threadPool = Executors.newCachedThreadPool(); 71 | } 72 | if (failureEventType == null) { 73 | failureEventType = ThrowableFailureEvent.class; 74 | } 75 | return new AsyncExecutor(threadPool, eventBus, failureEventType, executionContext); 76 | } 77 | } 78 | 79 | /** Like {@link Runnable}, but the run method may throw an exception. */ 80 | public interface RunnableEx { 81 | void run() throws Exception; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | public static AsyncExecutor create() { 89 | return new Builder().build(); 90 | } 91 | 92 | private final Executor threadPool; 93 | private final Constructor failureEventConstructor; 94 | private final EventBus eventBus; 95 | private final Object scope; 96 | 97 | private AsyncExecutor(Executor threadPool, EventBus eventBus, Class failureEventType, Object scope) { 98 | this.threadPool = threadPool; 99 | this.eventBus = eventBus; 100 | this.scope = scope; 101 | try { 102 | failureEventConstructor = failureEventType.getConstructor(Throwable.class); 103 | } catch (NoSuchMethodException e) { 104 | throw new RuntimeException( 105 | "Failure event class must have a constructor with one parameter of type Throwable", e); 106 | } 107 | } 108 | 109 | /** Posts an failure event if the given {@link RunnableEx} throws an Exception. */ 110 | public void execute(final RunnableEx runnable) { 111 | threadPool.execute(new Runnable() { 112 | @Override 113 | public void run() { 114 | try { 115 | runnable.run(); 116 | } catch (Exception e) { 117 | Object event; 118 | try { 119 | event = failureEventConstructor.newInstance(e); 120 | } catch (Exception e1) { 121 | Log.e(EventBus.TAG, "Original exception:", e); 122 | throw new RuntimeException("Could not create failure event", e1); 123 | } 124 | if (event instanceof HasExecutionScope) { 125 | ((HasExecutionScope) event).setExecutionScope(scope); 126 | } 127 | eventBus.post(event); 128 | } 129 | } 130 | }); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ErrorDialogConfig.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | import android.content.res.Resources; 4 | import android.util.Log; 5 | import de.greenrobot.event.EventBus; 6 | 7 | public class ErrorDialogConfig { 8 | final Resources resources; 9 | final int defaultTitleId; 10 | final int defaultErrorMsgId; 11 | final ExceptionToResourceMapping mapping; 12 | 13 | EventBus eventBus; 14 | boolean logExceptions = true; 15 | String tagForLoggingExceptions; 16 | int defaultDialogIconId; 17 | Class defaultEventTypeOnDialogClosed; 18 | 19 | public ErrorDialogConfig(Resources resources, int defaultTitleId, int defaultMsgId) { 20 | this.resources = resources; 21 | this.defaultTitleId = defaultTitleId; 22 | this.defaultErrorMsgId = defaultMsgId; 23 | mapping = new ExceptionToResourceMapping(); 24 | } 25 | 26 | public ErrorDialogConfig addMapping(Class clazz, int msgId) { 27 | mapping.addMapping(clazz, msgId); 28 | return this; 29 | } 30 | 31 | public int getMessageIdForThrowable(final Throwable throwable) { 32 | Integer resId = mapping.mapThrowable(throwable); 33 | if (resId != null) { 34 | return resId; 35 | } else { 36 | Log.d(EventBus.TAG, "No specific message ressource ID found for " + throwable); 37 | return defaultErrorMsgId; 38 | } 39 | } 40 | 41 | public void setDefaultDialogIconId(int defaultDialogIconId) { 42 | this.defaultDialogIconId = defaultDialogIconId; 43 | } 44 | 45 | public void setDefaultEventTypeOnDialogClosed(Class defaultEventTypeOnDialogClosed) { 46 | this.defaultEventTypeOnDialogClosed = defaultEventTypeOnDialogClosed; 47 | } 48 | 49 | public void disableExceptionLogging() { 50 | logExceptions = false; 51 | } 52 | 53 | public void setTagForLoggingExceptions(String tagForLoggingExceptions) { 54 | this.tagForLoggingExceptions = tagForLoggingExceptions; 55 | } 56 | 57 | public void setEventBus(EventBus eventBus) { 58 | this.eventBus = eventBus; 59 | } 60 | 61 | /** eventBus!=null ? eventBus: EventBus.getDefault() */ 62 | EventBus getEventBus() { 63 | return eventBus!=null ? eventBus: EventBus.getDefault(); 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ErrorDialogFragmentFactory.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | 8 | /** 9 | * Factory to allow injecting a more complex exception mapping; typically you would subclass one of {@link Honeycomb} or 10 | * {@link Support}. 11 | */ 12 | public abstract class ErrorDialogFragmentFactory { 13 | protected final ErrorDialogConfig config; 14 | 15 | protected ErrorDialogFragmentFactory(ErrorDialogConfig config) { 16 | this.config = config; 17 | } 18 | 19 | /** 20 | * Prepares the fragment's arguments and creates the fragment. May be overridden to provide custom error fragments. 21 | */ 22 | protected T prepareErrorFragment(ThrowableFailureEvent event, boolean finishAfterDialog, 23 | Bundle argumentsForErrorDialog) { 24 | if (event.isSuppressErrorUi()) { 25 | // Show nothing by default 26 | return null; 27 | } 28 | Bundle bundle; 29 | if (argumentsForErrorDialog != null) { 30 | bundle = (Bundle) argumentsForErrorDialog.clone(); 31 | } else { 32 | bundle = new Bundle(); 33 | } 34 | 35 | if (!bundle.containsKey(ErrorDialogManager.KEY_TITLE)) { 36 | String title = getTitleFor(event, bundle); 37 | bundle.putString(ErrorDialogManager.KEY_TITLE, title); 38 | } 39 | if (!bundle.containsKey(ErrorDialogManager.KEY_MESSAGE)) { 40 | String message = getMessageFor(event, bundle); 41 | bundle.putString(ErrorDialogManager.KEY_MESSAGE, message); 42 | } 43 | if (!bundle.containsKey(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG)) { 44 | bundle.putBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, finishAfterDialog); 45 | } 46 | if (!bundle.containsKey(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE) 47 | && config.defaultEventTypeOnDialogClosed != null) { 48 | bundle.putSerializable(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE, config.defaultEventTypeOnDialogClosed); 49 | } 50 | if (!bundle.containsKey(ErrorDialogManager.KEY_ICON_ID) && config.defaultDialogIconId != 0) { 51 | bundle.putInt(ErrorDialogManager.KEY_ICON_ID, config.defaultDialogIconId); 52 | } 53 | return createErrorFragment(event, bundle); 54 | } 55 | 56 | /** Returns either a new Honeycomb+ or a new support library DialogFragment. */ 57 | protected abstract T createErrorFragment(ThrowableFailureEvent event, Bundle arguments); 58 | 59 | /** May be overridden to provide custom error title. */ 60 | protected String getTitleFor(ThrowableFailureEvent event, Bundle arguments) { 61 | return config.resources.getString(config.defaultTitleId); 62 | } 63 | 64 | /** May be overridden to provide custom error messages. */ 65 | protected String getMessageFor(ThrowableFailureEvent event, Bundle arguments) { 66 | int msgResId = config.getMessageIdForThrowable(event.throwable); 67 | return config.resources.getString(msgResId); 68 | } 69 | 70 | public static class Support extends ErrorDialogFragmentFactory { 71 | 72 | public Support(ErrorDialogConfig config) { 73 | super(config); 74 | } 75 | 76 | protected Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) { 77 | ErrorDialogFragments.Support errorFragment = new ErrorDialogFragments.Support(); 78 | errorFragment.setArguments(arguments); 79 | return errorFragment; 80 | } 81 | 82 | } 83 | 84 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 85 | public static class Honeycomb extends ErrorDialogFragmentFactory { 86 | 87 | public Honeycomb(ErrorDialogConfig config) { 88 | super(config); 89 | } 90 | 91 | protected android.app.Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) { 92 | ErrorDialogFragments.Honeycomb errorFragment = new ErrorDialogFragments.Honeycomb(); 93 | errorFragment.setArguments(arguments); 94 | return errorFragment; 95 | } 96 | 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ErrorDialogFragments.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.app.Dialog; 7 | import android.content.Context; 8 | import android.content.DialogInterface; 9 | import android.content.DialogInterface.OnClickListener; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.support.v4.app.DialogFragment; 13 | import de.greenrobot.event.EventBus; 14 | 15 | public class ErrorDialogFragments { 16 | /** TODO Use config: Icon res ID to use for all error dialogs. May be configured by each app (optional). */ 17 | public static int ERROR_DIALOG_ICON = 0; 18 | 19 | /** TODO Use config: Event class to be fired on dismissing the dialog by the user. May be configured by each app. */ 20 | public static Class EVENT_TYPE_ON_CLICK; 21 | 22 | public static Dialog createDialog(Context context, Bundle arguments, OnClickListener onClickListener) { 23 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 24 | builder.setTitle(arguments.getString(ErrorDialogManager.KEY_TITLE)); 25 | builder.setMessage(arguments.getString(ErrorDialogManager.KEY_MESSAGE)); 26 | if (ERROR_DIALOG_ICON != 0) { 27 | builder.setIcon(ERROR_DIALOG_ICON); 28 | } 29 | builder.setPositiveButton(android.R.string.ok, onClickListener); 30 | return builder.create(); 31 | } 32 | 33 | public static void handleOnClick(DialogInterface dialog, int which, Activity activity, Bundle arguments) { 34 | if (EVENT_TYPE_ON_CLICK != null) { 35 | Object event; 36 | try { 37 | event = EVENT_TYPE_ON_CLICK.newInstance(); 38 | } catch (Exception e) { 39 | throw new RuntimeException("Event cannot be constructed", e); 40 | } 41 | EventBus eventBus = ErrorDialogManager.factory.config.getEventBus(); 42 | eventBus.post(event); 43 | } 44 | boolean finish = arguments.getBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, false); 45 | if (finish && activity != null) { 46 | activity.finish(); 47 | } 48 | } 49 | 50 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 51 | public static class Honeycomb extends android.app.DialogFragment implements OnClickListener { 52 | @Override 53 | public Dialog onCreateDialog(Bundle savedInstanceState) { 54 | return createDialog(getActivity(), getArguments(), this); 55 | } 56 | 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | handleOnClick(dialog, which, getActivity(), getArguments()); 60 | } 61 | } 62 | 63 | public static class Support extends DialogFragment implements OnClickListener { 64 | @Override 65 | public Dialog onCreateDialog(Bundle savedInstanceState) { 66 | return createDialog(getActivity(), getArguments(), this); 67 | } 68 | 69 | @Override 70 | public void onClick(DialogInterface dialog, int which) { 71 | handleOnClick(dialog, which, getActivity(), getArguments()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ErrorDialogManager.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.Application; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.support.v4.app.DialogFragment; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.app.FragmentActivity; 11 | import android.support.v4.app.FragmentManager; 12 | import android.util.Log; 13 | import de.greenrobot.event.EventBus; 14 | 15 | /** 16 | * Central class for app that want to use event based error dialogs.
17 | *
18 | * How to use: 19 | *

    20 | *
  1. Set the {@link #factory} to configure dialogs for your app, typically in {@link Application#onCreate()}
  2. 21 | *
  3. Use one of {@link #attachTo(Activity)}, {@link #attachTo(Activity, boolean)} or 22 | * {@link #attachTo(Activity, boolean, Bundle)} in your Activity, typically in onCreate.
  4. 23 | *
24 | * 25 | * For more complex mappings, you can supply your own {@link ErrorDialogFragmentFactory}. 26 | * 27 | * @author Markus 28 | */ 29 | public class ErrorDialogManager { 30 | 31 | public static class SupportManagerFragment extends Fragment { 32 | protected boolean finishAfterDialog; 33 | protected Bundle argumentsForErrorDialog; 34 | private EventBus eventBus; 35 | private boolean skipRegisterOnNextResume; 36 | private Object executionScope; 37 | 38 | @Override 39 | public void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | eventBus = ErrorDialogManager.factory.config.getEventBus(); 42 | eventBus.register(this); 43 | skipRegisterOnNextResume = true; 44 | } 45 | 46 | @Override 47 | public void onResume() { 48 | super.onResume(); 49 | if (skipRegisterOnNextResume) { 50 | // registered in onCreate, skip registration in this run 51 | skipRegisterOnNextResume = false; 52 | } else { 53 | eventBus = ErrorDialogManager.factory.config.getEventBus(); 54 | eventBus.register(this); 55 | } 56 | } 57 | 58 | @Override 59 | public void onPause() { 60 | eventBus.unregister(this); 61 | super.onPause(); 62 | } 63 | 64 | public void onEventMainThread(ThrowableFailureEvent event) { 65 | if (!isInExecutionScope(executionScope, event)) { 66 | return; 67 | } 68 | checkLogException(event); 69 | // Execute pending commits before finding to avoid multiple error fragments being shown 70 | FragmentManager fm = getFragmentManager(); 71 | fm.executePendingTransactions(); 72 | 73 | DialogFragment existingFragment = (DialogFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG); 74 | if (existingFragment != null) { 75 | // Just show the latest error 76 | existingFragment.dismiss(); 77 | } 78 | 79 | DialogFragment errorFragment = (DialogFragment) factory 80 | .prepareErrorFragment(event, finishAfterDialog, argumentsForErrorDialog); 81 | if (errorFragment != null) { 82 | errorFragment.show(fm, TAG_ERROR_DIALOG); 83 | } 84 | } 85 | 86 | public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, 87 | Bundle argumentsForErrorDialog) { 88 | FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager(); 89 | SupportManagerFragment fragment = (SupportManagerFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG_MANAGER); 90 | if (fragment == null) { 91 | fragment = new SupportManagerFragment(); 92 | fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit(); 93 | fm.executePendingTransactions(); 94 | } 95 | fragment.finishAfterDialog = finishAfterDialog; 96 | fragment.argumentsForErrorDialog = argumentsForErrorDialog; 97 | fragment.executionScope = executionScope; 98 | } 99 | } 100 | 101 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 102 | public static class HoneycombManagerFragment extends android.app.Fragment { 103 | protected boolean finishAfterDialog; 104 | protected Bundle argumentsForErrorDialog; 105 | private EventBus eventBus; 106 | private Object executionScope; 107 | 108 | @Override 109 | public void onResume() { 110 | super.onResume(); 111 | eventBus = ErrorDialogManager.factory.config.getEventBus(); 112 | eventBus.register(this); 113 | } 114 | 115 | @Override 116 | public void onPause() { 117 | eventBus.unregister(this); 118 | super.onPause(); 119 | } 120 | 121 | public void onEventMainThread(ThrowableFailureEvent event) { 122 | if (!isInExecutionScope(executionScope, event)) { 123 | return; 124 | } 125 | checkLogException(event); 126 | 127 | // Execute pending commits before finding to avoid multiple error fragments being shown 128 | android.app.FragmentManager fm = getFragmentManager(); 129 | fm.executePendingTransactions(); 130 | 131 | android.app.DialogFragment existingFragment = (android.app.DialogFragment) fm 132 | .findFragmentByTag(TAG_ERROR_DIALOG); 133 | if (existingFragment != null) { 134 | // Just show the latest error 135 | existingFragment.dismiss(); 136 | } 137 | 138 | android.app.DialogFragment errorFragment = (android.app.DialogFragment) factory.prepareErrorFragment(event, 139 | finishAfterDialog, argumentsForErrorDialog); 140 | if (errorFragment != null) { 141 | errorFragment.show(fm, TAG_ERROR_DIALOG); 142 | } 143 | } 144 | 145 | public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { 146 | android.app.FragmentManager fm = activity.getFragmentManager(); 147 | HoneycombManagerFragment fragment = (HoneycombManagerFragment) fm 148 | .findFragmentByTag(TAG_ERROR_DIALOG_MANAGER); 149 | if (fragment == null) { 150 | fragment = new HoneycombManagerFragment(); 151 | fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit(); 152 | fm.executePendingTransactions(); 153 | } 154 | fragment.finishAfterDialog = finishAfterDialog; 155 | fragment.argumentsForErrorDialog = argumentsForErrorDialog; 156 | fragment.executionScope = executionScope; 157 | } 158 | } 159 | 160 | /** Must be set by the application. */ 161 | public static ErrorDialogFragmentFactory factory; 162 | 163 | protected static final String TAG_ERROR_DIALOG = "de.greenrobot.eventbus.error_dialog"; 164 | protected static final String TAG_ERROR_DIALOG_MANAGER = "de.greenrobot.eventbus.error_dialog_manager"; 165 | 166 | public static final String KEY_TITLE = "de.greenrobot.eventbus.errordialog.title"; 167 | public static final String KEY_MESSAGE = "de.greenrobot.eventbus.errordialog.message"; 168 | public static final String KEY_FINISH_AFTER_DIALOG = "de.greenrobot.eventbus.errordialog.finish_after_dialog"; 169 | public static final String KEY_ICON_ID = "de.greenrobot.eventbus.errordialog.icon_id"; 170 | public static final String KEY_EVENT_TYPE_ON_CLOSE = "de.greenrobot.eventbus.errordialog.event_type_on_close"; 171 | 172 | /** Scope is limited to the activity's class. */ 173 | public static void attachTo(Activity activity) { 174 | attachTo(activity, false, null); 175 | } 176 | 177 | /** Scope is limited to the activity's class. */ 178 | public static void attachTo(Activity activity, boolean finishAfterDialog) { 179 | attachTo(activity, finishAfterDialog, null); 180 | } 181 | 182 | /** Scope is limited to the activity's class. */ 183 | public static void attachTo(Activity activity, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { 184 | Object executionScope = activity.getClass(); 185 | attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); 186 | } 187 | 188 | public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { 189 | if (factory == null) { 190 | throw new RuntimeException("You must set the static factory field to configure error dialogs for your app."); 191 | } 192 | if (isSupportActivity(activity)) { 193 | SupportManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); 194 | } else { 195 | HoneycombManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); 196 | } 197 | } 198 | 199 | private static boolean isSupportActivity(Activity activity) { 200 | boolean isSupport = false; 201 | for (Class c = activity.getClass().getSuperclass();; c = c.getSuperclass()) { 202 | if (c == null) { 203 | throw new RuntimeException("Illegal activity type: " + activity.getClass()); 204 | } 205 | String name = c.getName(); 206 | if (name.equals("android.support.v4.app.FragmentActivity")) { 207 | isSupport = true; 208 | break; 209 | } else if (name.startsWith("com.actionbarsherlock.app") 210 | && (name.endsWith(".SherlockActivity") || name.endsWith(".SherlockListActivity") || name 211 | .endsWith(".SherlockPreferenceActivity"))) { 212 | throw new RuntimeException("Please use SherlockFragmentActivity. Illegal activity: " + name); 213 | } else if (name.equals("android.app.Activity")) { 214 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 215 | throw new RuntimeException( 216 | "Illegal activity without fragment support. Either use Android 3.0+ or android.support.v4.app.FragmentActivity."); 217 | } 218 | break; 219 | } 220 | } 221 | return isSupport; 222 | } 223 | 224 | protected static void checkLogException(ThrowableFailureEvent event) { 225 | if (factory.config.logExceptions) { 226 | String tag = factory.config.tagForLoggingExceptions; 227 | if (tag == null) { 228 | tag = EventBus.TAG; 229 | } 230 | Log.i(tag, "Error dialog manager received exception", event.throwable); 231 | } 232 | } 233 | 234 | private static boolean isInExecutionScope(Object executionScope, ThrowableFailureEvent event) { 235 | if (event != null) { 236 | Object eventExecutionScope = event.getExecutionScope(); 237 | if (eventExecutionScope != null && !eventExecutionScope.equals(executionScope)) { 238 | // Event not in our scope, do nothing 239 | return false; 240 | } 241 | } 242 | return true; 243 | } 244 | 245 | } 246 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ExceptionToResourceMapping.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | import java.util.Set; 7 | 8 | import android.util.Log; 9 | import de.greenrobot.event.EventBus; 10 | 11 | 12 | /** 13 | * Maps throwables to texts for error dialogs. Use Config to configure the mapping. 14 | * 15 | * @author Markus 16 | */ 17 | public class ExceptionToResourceMapping { 18 | 19 | public final Map, Integer> throwableToMsgIdMap; 20 | 21 | public ExceptionToResourceMapping() { 22 | throwableToMsgIdMap = new HashMap, Integer>(); 23 | } 24 | 25 | /** Looks at the exception and its causes trying to find an ID. */ 26 | public Integer mapThrowable(final Throwable throwable) { 27 | Throwable throwableToCheck = throwable; 28 | int depthToGo = 20; 29 | 30 | while (true) { 31 | Integer resId = mapThrowableFlat(throwableToCheck); 32 | if (resId != null) { 33 | return resId; 34 | } else { 35 | throwableToCheck = throwableToCheck.getCause(); 36 | depthToGo--; 37 | if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) { 38 | Log.d(EventBus.TAG, "No specific message ressource ID found for " + throwable); 39 | // return config.defaultErrorMsgId; 40 | return null; 41 | } 42 | } 43 | } 44 | 45 | } 46 | 47 | /** Mapping without checking the cause (done in mapThrowable). */ 48 | protected Integer mapThrowableFlat(Throwable throwable) { 49 | Class throwableClass = throwable.getClass(); 50 | Integer resId = throwableToMsgIdMap.get(throwableClass); 51 | if (resId == null) { 52 | Class closestClass = null; 53 | Set, Integer>> mappings = throwableToMsgIdMap.entrySet(); 54 | for (Entry, Integer> mapping : mappings) { 55 | Class candidate = mapping.getKey(); 56 | if (candidate.isAssignableFrom(throwableClass)) { 57 | if (closestClass == null || closestClass.isAssignableFrom(candidate)) { 58 | closestClass = candidate; 59 | resId = mapping.getValue(); 60 | } 61 | } 62 | } 63 | 64 | } 65 | return resId; 66 | } 67 | 68 | public ExceptionToResourceMapping addMapping(Class clazz, int msgId) { 69 | throwableToMsgIdMap.put(clazz, msgId); 70 | return this; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/HasExecutionScope.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event.util; 2 | 3 | public interface HasExecutionScope { 4 | Object getExecutionScope(); 5 | 6 | void setExecutionScope(Object executionScope); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/de/greenrobot/event/util/ThrowableFailureEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.greenrobot.event.util; 17 | 18 | /** 19 | * A generic failure event, which can be used by apps to propagate thrown exceptions. Also used in conjunction with 20 | * {@link ErrorDialogManager}. 21 | */ 22 | public class ThrowableFailureEvent implements HasExecutionScope { 23 | protected final Throwable throwable; 24 | protected final boolean suppressErrorUi; 25 | private Object executionContext; 26 | 27 | public ThrowableFailureEvent(Throwable throwable) { 28 | this.throwable = throwable; 29 | suppressErrorUi = false; 30 | } 31 | 32 | /** 33 | * @param suppressErrorUi 34 | * true indicates to the receiver that no error UI (e.g. dialog) should now displayed. 35 | */ 36 | public ThrowableFailureEvent(Throwable throwable, boolean suppressErrorUi) { 37 | this.throwable = throwable; 38 | this.suppressErrorUi = suppressErrorUi; 39 | } 40 | 41 | public Throwable getThrowable() { 42 | return throwable; 43 | } 44 | 45 | public boolean isSuppressErrorUi() { 46 | return suppressErrorUi; 47 | } 48 | 49 | public Object getExecutionScope() { 50 | return executionContext; 51 | } 52 | 53 | public void setExecutionScope(Object executionContext) { 54 | this.executionContext = executionContext; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | EventBus 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/de/greenrobot/event/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package de.greenrobot.event; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /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.0.0-alpha2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymjs/EventBus/092dcc1b00934cca0edde7daa98788f08873a7d7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 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.8-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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------