diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..365ea1a6 --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,28 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: Java CI with Gradle + +on: + push: + pull_request: + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Gradle Info + run: ./gradlew -version + - name: Build with Gradle + run: ./gradlew build diff --git a/.gitignore b/.gitignore index 36573eee..36c5498b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,32 @@ -.settings/ +# Built application files +*.apk +*.ap_ -*.iml -.idea +# Files for the Dalvik VM +*.dex -gen/ +# Java class files +*.class + +# Generated files bin/ +gen/ release/ -build/ +# Gradle files .gradle/ +build/ gradle.properties +# Local configuration file (sdk path, etc) local.properties + +# IntelliJ project files +*.iml +.idea/ + +# Eclipse project files +.settings/ + +# Misc +.DS_Store diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f2076297..00000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: android -jdk: - - openjdk6 - - oraclejdk7 - -# http://docs.travis-ci.com/user/languages/android/ -android: - components: - - build-tools-21.0.2 - - android-10 - -before_script: - - chmod +x gradlew -# - echo no | android create avd --force -n test -t android-10 --abi armeabi -# - emulator -avd test -no-skin -no-audio -no-window & -# - android-wait-for-emulator -# - adb shell input keyevent 82 & - -# Currently connectedCheck fails, so don't run unit test on Emulator for now. Issue: -# com.android.builder.testing.ConnectedDevice > hasTests[test(AVD) - 2.3.3] FAILED -# No tests found. - -script: - - TERM=dumb ./gradlew check diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2db0fc27..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,66 +0,0 @@ -### V2.4.0 (2014-11-11) Clean up release -* Removed deprecated APIs: A year ago in Version 2.2.0, a couple of EventBus methods were deprecated and flagged to be removed in a future release. Well, version 2.4.0 is that release. Clean ups like this one keep the API concise and simple. - -**Note:** No new feature were added since 2.3.0. Use this release if you do not rely on deprecated APIs. - -### V2.3.0 (2014-11-11) Feature release: EventBusBuilder and performance fix -* New EventBusBuilder to configure EventBus instances (including the getDefault() instance, #124) -* Added configuration to disable "No subscribers registered for event" logs (EventBusBuilder, #107, #117) -* Added configuration to disable sending SubscriberExceptionEvent and NoSubscriberEvent (EventBusBuilder) -* Added configuration to fail when subscribers throw exceptions (EventBusBuilder, #55) -* Added configuration to use an existing thread pool (EventBusBuilder, #115) -* Added configuration to disable event inheritance improving performance for apps with high event rates (EventBusBuilder) -* Fixed performance regression sneaked into V2.2.x affecting (first time) registration of subscribers -* Updated to Gradle 2.1, using wrapper -* EventBusTest and EventBusPerformance use Gradle to build -* Added hasSubscriberForEvent to check if currently subscribers exist registered to a given event type -* Improved README.md and extracted an extended HOWTO.md and CHANGELOG.md from it -* Ignore compiler generated methods (#76) -* Various small code improvements (#120 among many others) - -**Note:** This is your last chance to use APIs that were deprecated in V2.2.0. It's recommended to switch to Version 2.4.0 (or above) at your earliest convenience. - -### V2.2.1 (2014-05-21) Bug fix release -* Fixed an issue with AsyncExecutor and execution scope - -### V2.2.0 (2013-11-18) Feature release, subscriber priority -* Register subscribers with a priority to to influence the order of event delivery (per delivery thread) -* Event delivery can be canceled by subscribers so subsequent subscribers will not receive the event -* Added "isRegistered" and "removeAllStickyEvents" methods -* Deprecated registration methods with custom method names and event class filters -* Starting with EventBus 2.2 we enforced methods to be public -* Fixed a race conditions with subscriber registration -* Fixed NoSubscriberEvent delivery after unregister - -### V2.1.0 (2013-11-15) Bug fix release, experimental util package -* Experimental: AsyncExecutor executes RunnableEx and wraps exceptions into FailureEvents -* Experimental: exception to UI mapping (for now based on dialogs) -* Fixed race condition with queued events that were delivered after subscription was unregistered. This is important for main thread events tied to application life cycle. -* Fixed typos and improved readme (#17, #22, #37, #39) -* Make getStickyEvent and removeStickyEvent generic (#45) -* Fixed bug in SubscriberMethod.equals() (#38) - -### V2.0.2 (2013-03-02) Bug fix release -* Fixed build dependencies, are "provided" now - -### V2.0.1 (2013-02-25) Bug fix release, Gradle and Maven Central -* Fixed #15: removeStickyEvent(...) does not remove event the first time -* Introduced Gradle build scripts for main project -* Maven artifacts are pushed to Maven Central starting with this version -* Added Travis CI - -### V2.0.0 (2012-10-23) Major feature release -* Event methods define for themselves in which thread they get called. This is done by providing "modifiers" to the method name, e.g. onEventMainThread is called by the main thread without further configuration. Have a look at the JavaDoc of the enum ThreadMode for all available thread modes. -* The event method modifiers replace registerForMainThread methods. Moving this information to the method itself should make things clearer. -* Using event method modifiers, subscribers can receive the same event type in different threads if they choose to. -* New "BackgroundThread" modifier for onEvent handler methods ensures that event handler methods are called in a background thread. If an event is posted from a non-main thread, handler methods will be called directly. If posted from the main thread, EventBus will use a background thread to call the handler methods. -* New "Async" modifier for onEvent handler methods ensures that each event handler method is called completely asynchronously. -* Better performance: Delivery of multiple events in the main thread got significantly faster. -* Added sticky events, which are inspired by sticky broadcasts of the Android system. EventBus keeps the most recent sticky events in memory. Subscribers registering with the new method registerSticky, will receive sticky events right away. You can also query and remove sticky events (methods getStickyEvent and removeStickyEvent). -* By listening to SubscriberExceptionEvent, it is possible to react to Exceptions occuring in subscribers. -* Bug fixes, and internal refactorings - -### V1.0.1 (2012-07-31): Important bug fix release -Please update! Now, EventBus.unregister releases all internal references to the subscriber. - -### V1.0.0 (2012-07-16): First public release diff --git a/COMPARISON.md b/COMPARISON.md index 1ba60ccd..2e0493cc 100644 --- a/COMPARISON.md +++ b/COMPARISON.md @@ -12,14 +12,14 @@ Otto is another event bus library for Android; actually it's a fork of Guava's E Declare event handling methods - Name conventions + Annotations (since 3.0, can be precompiled for best performance) Annotations - + Event inheritance Yes Yes - + Subscriber inheritance Yes @@ -39,26 +39,30 @@ Otto is another event bus library for Android; actually it's a fork of Guava's E Event delivery in posting thread Yes (Default) Yes - + Event delivery in main thread Yes No - + Event delivery in background thread Yes No - + - Aynchronous event delivery + Asynchronous event delivery Yes No +_**Note:** the following information is outdated, preprocessed annotations are much faster than EventBus 2.x, on which the following table is based._ + Besides features, performance is another differentiator. To compare performance, we created an Android application, which is also part of this repository (EventBusPerformance). You can also run the app on your phone to benchmark different scenarios. +TODO: Update for EventBus 3 with and without index. + Benchmark results indicate that EventBus is significantly faster in almost every scenario: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1402e6bb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +Before you create an Issue... +============================= + +There are better Places for Support +----------------------------------- +We want your question to be answered, so it is important that you ask at the right place. Be aware that an issue tracker is not the best place to ask for support. An issue tracker is used to track issues (bugs or feature requests). +Instead, please use [stackoverflow.com](https://stackoverflow.com/questions/tagged/greenrobot-eventbus?sort=frequent) and use the tag [greenrobot-eventbus](http://stackoverflow.com/tags/greenrobot-eventbus/info) for your question. + +If you want professional support, check http://greenrobot.org/contact-support/. + +Examples for support questions that are more likely to be answered on StackOverflow: + +* Asking how something works +* Asking how to use EventBus in a specific scenario +* Your app crashes/misbehaves and you are not sure why + +The perfect Issue Report +------------------------ +A couple of simple steps can save time for everyone. + +Check before reporting: + +* It's not a support inquiry +* You have read the docs +* You searched the web and stackoverflow +* You searched existing issues to avoid duplicates + +Reporting bugs: + + * Please investigate if is the bug is really caused by the library. Isolate the issue: what's the minimal code to reproduce the bug? + * Bonus steps to gain extra karma points: once you isolated and identified the issue, you can prepare an push request. Submit an unit test causing the bug, and ideally a fix for the bug. + +Requesting features: + + * Ask yourself: is the feature useful for a majority users? One of our major goals is to keep the API simple and concise. We do not want to cover all possible use cases, but those that make 80% of users happy. + +A Note on Pull Requests +======================= +Pull requests (and issues) may queue up up a bit. Usually, pull requests and issues are checked when new releases are planned. + +For bigger pull requests, it's a good idea to check with the maintainer upfront about the idea and the implementation outline. + +Thanks for reading! +=================== +It's your contributions and feedback that makes maintaining this library fun. diff --git a/EventBus/.classpath b/EventBus/.classpath deleted file mode 100644 index 7bc01d9a..00000000 --- a/EventBus/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/EventBus/.project b/EventBus/.project deleted file mode 100644 index 3321bf38..00000000 --- a/EventBus/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - EventBus - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/EventBus/AndroidManifest.xml b/EventBus/AndroidManifest.xml deleted file mode 100644 index 2ad17bbd..00000000 --- a/EventBus/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/EventBus/build.gradle b/EventBus/build.gradle index 153a6440..ffea11b6 100644 --- a/EventBus/build.gradle +++ b/EventBus/build.gradle @@ -1,42 +1,13 @@ apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'signing' -group = 'de.greenrobot' -version = '2.4.0' -sourceCompatibility = 1.6 +group = rootProject.group +version = rootProject.version -def isSnapshot = version.endsWith('-SNAPSHOT') -def sonatypeRepositoryUrl -if(isSnapshot) { - sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" -} else { - sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -repositories { - mavenCentral() -} - -// Still unsupported, see http://issues.gradle.org/browse/GRADLE-784 -// Like this, it won't appear at all in the POM -configurations { - provided - deployerJars -} - -dependencies { - provided 'com.google.android:android:4.1.1.4' - provided 'com.google.android:android-test:4.1.1.4' - provided 'com.google.android:annotations:4.1.1.4' - provided 'com.google.android:support-v4:r7' - // deployerJars 'org.apache.maven.wagon:wagon-webdav-jackrabbit:2.4' - deployerJars 'org.apache.maven.wagon:wagon-webdav:1.0-beta-2' -} +java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.targetCompatibility = JavaVersion.VERSION_1_8 sourceSets { main { - compileClasspath += configurations.provided java { srcDir 'src' // exclude 'de/greenrobot/event/util/**' @@ -45,92 +16,36 @@ sourceSets { } javadoc { - classpath += configurations.provided + failOnError = false title = "EventBus ${version} API" - options.bottom = 'Available under the Apache License, Version 2.0 - Copyright © 2012-2014 greenrobot.de. All Rights Reserved.' + options.bottom = 'Available under the Apache License, Version 2.0 - Copyright © 2012-2020 greenrobot.org. All Rights Reserved.' } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier.set("javadoc") from 'build/docs/javadoc' } task sourcesJar(type: Jar) { + archiveClassifier.set("sources") from sourceSets.main.allSource - classifier = 'sources' -} - -artifacts { - archives jar - archives javadocJar - archives sourcesJar } -signing { - if(project.hasProperty('signing.keyId') && project.hasProperty('signing.password') && - project.hasProperty('signing.secretKeyRingFile')) { - sign configurations.archives - } else { - println "Signing information missing/incomplete for ${project.name}" - } -} - -uploadArchives { - repositories { - mavenDeployer { - if(project.hasProperty('preferedRepo') && project.hasProperty('preferedUsername') - && project.hasProperty('preferedPassword')) { - configuration = configurations.deployerJars - repository(url: preferedRepo) { - authentication(userName: preferedUsername, password: preferedPassword) - } - } else if(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - repository(url: sonatypeRepositoryUrl) { - authentication(userName: sonatypeUsername, password: sonatypePassword) - } - } else { - println "Settings sonatypeUsername/sonatypePassword missing/incomplete for ${project.name}" +apply from: rootProject.file("gradle/publish.gradle") +// Set project-specific properties +afterEvaluate { + publishing.publications { + mavenJava(MavenPublication) { + artifactId = "eventbus-java" + + from components.java + artifact javadocJar + artifact sourcesJar + pom { + name = "EventBus" + description = "EventBus is a publish/subscribe event bus." + packaging = "jar" } - - pom.artifactId = 'eventbus' - pom.project { - name 'EventBus' - packaging 'jar' - description 'EventBus is a publish/subscribe event bus optimized for Android .' - url 'https://github.com/greenrobot/EventBus' - - scm { - url 'https://github.com/greenrobot/EventBus' - connection 'scm:git@github.com:greenrobot/EventBus.git' - developerConnection 'scm:git@github.com:greenrobot/EventBus.git' - } - - licenses { - license { - name 'The Apache Software License, Version 2.0' - url 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution 'repo' - } - } - - developers { - developer { - id 'greenrobot' - name 'greenrobot' - } - } - - issueManagement { - system 'GitHub Issues' - url 'https://github.com/greenrobot/EventBus/issues' - } - - organization { - name 'greenrobot' - url 'http://greenrobot.de' - } - } } } -} \ No newline at end of file +} diff --git a/EventBus/libs/android-support-v4.jar b/EventBus/libs/android-support-v4.jar deleted file mode 100644 index 6080877d..00000000 Binary files a/EventBus/libs/android-support-v4.jar and /dev/null differ diff --git a/EventBus/mybuild.xml b/EventBus/mybuild.xml deleted file mode 100644 index 0ab1cf4b..00000000 --- a/EventBus/mybuild.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/EventBus/project.properties b/EventBus/project.properties deleted file mode 100644 index 484dab07..00000000 --- a/EventBus/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-17 -android.library=true diff --git a/EventBus/res/values/strings.xml b/EventBus/res/values/strings.xml deleted file mode 100644 index 85420055..00000000 --- a/EventBus/res/values/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/EventBus/settings.gradle b/EventBus/settings.gradle deleted file mode 100644 index 4d211e40..00000000 --- a/EventBus/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'eventbus' \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/SubscriberMethodFinder.java b/EventBus/src/de/greenrobot/event/SubscriberMethodFinder.java deleted file mode 100644 index 823cb0ec..00000000 --- a/EventBus/src/de/greenrobot/event/SubscriberMethodFinder.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.greenrobot.event; - -import android.util.Log; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -class SubscriberMethodFinder { - private static final String ON_EVENT_METHOD_NAME = "onEvent"; - - /* - * In newer class files, compilers may add methods. Those are called bridge or synthetic methods. - * EventBus must ignore both. There modifiers are not public but defined in the Java class file format: - * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1 - */ - private static final int BRIDGE = 0x40; - private static final int SYNTHETIC = 0x1000; - - private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC; - private static final Map> methodCache = new HashMap>(); - - private final Map, Class> skipMethodVerificationForClasses; - - SubscriberMethodFinder(List> skipMethodVerificationForClassesList) { - skipMethodVerificationForClasses = new ConcurrentHashMap, Class>(); - if (skipMethodVerificationForClassesList != null) { - for (Class clazz : skipMethodVerificationForClassesList) { - skipMethodVerificationForClasses.put(clazz, clazz); - } - } - } - - List findSubscriberMethods(Class subscriberClass) { - String key = subscriberClass.getName(); - List subscriberMethods; - synchronized (methodCache) { - subscriberMethods = methodCache.get(key); - } - if (subscriberMethods != null) { - return subscriberMethods; - } - subscriberMethods = new ArrayList(); - Class clazz = subscriberClass; - HashSet eventTypesFound = new HashSet(); - StringBuilder methodKeyBuilder = new StringBuilder(); - while (clazz != null) { - String name = clazz.getName(); - if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { - // Skip system classes, this just degrades performance - break; - } - - // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - String methodName = method.getName(); - if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { - int modifiers = method.getModifiers(); - if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { - Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length == 1) { - String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); - ThreadMode threadMode; - if (modifierString.length() == 0) { - threadMode = ThreadMode.PostThread; - } else if (modifierString.equals("MainThread")) { - threadMode = ThreadMode.MainThread; - } else if (modifierString.equals("BackgroundThread")) { - threadMode = ThreadMode.BackgroundThread; - } else if (modifierString.equals("Async")) { - threadMode = ThreadMode.Async; - } else { - if (skipMethodVerificationForClasses.containsKey(clazz)) { - continue; - } else { - throw new EventBusException("Illegal onEvent method, check for typos: " + method); - } - } - Class eventType = parameterTypes[0]; - methodKeyBuilder.setLength(0); - methodKeyBuilder.append(methodName); - methodKeyBuilder.append('>').append(eventType.getName()); - String methodKey = methodKeyBuilder.toString(); - if (eventTypesFound.add(methodKey)) { - // Only add if not already found in a sub class - subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); - } - } - } else if (!skipMethodVerificationForClasses.containsKey(clazz)) { - Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "." - + methodName); - } - } - } - clazz = clazz.getSuperclass(); - } - if (subscriberMethods.isEmpty()) { - throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " - + ON_EVENT_METHOD_NAME); - } else { - synchronized (methodCache) { - methodCache.put(key, subscriberMethods); - } - return subscriberMethods; - } - } - - static void clearCaches() { - synchronized (methodCache) { - methodCache.clear(); - } - } - -} diff --git a/EventBus/src/de/greenrobot/event/ThreadMode.java b/EventBus/src/de/greenrobot/event/ThreadMode.java deleted file mode 100644 index 4022ace5..00000000 --- a/EventBus/src/de/greenrobot/event/ThreadMode.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.greenrobot.event; - -/** - * Each event handler method has a thread mode, which determines in which thread the method is to be called by EventBus. - * EventBus takes care of threading independently from the posting thread. - * - * @see EventBus#register(Object) - * @author Markus - */ -public enum ThreadMode { - /** - * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery - * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for - * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers - * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread. - */ - PostThread, - - /** - * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is - * the main thread, event handler methods will be called directly. Event handlers using this mode must return - * quickly to avoid blocking the main thread. - */ - MainThread, - - /** - * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods - * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single - * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to - * return quickly to avoid blocking the background thread. - */ - BackgroundThread, - - /** - * Event handler methods are called in a separate thread. This is always independent from the posting thread and the - * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should - * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number - * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus - * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications. - */ - Async -} \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/util/ErrorDialogConfig.java b/EventBus/src/de/greenrobot/event/util/ErrorDialogConfig.java deleted file mode 100644 index 25432631..00000000 --- a/EventBus/src/de/greenrobot/event/util/ErrorDialogConfig.java +++ /dev/null @@ -1,65 +0,0 @@ -package de.greenrobot.event.util; - -import android.content.res.Resources; -import android.util.Log; -import de.greenrobot.event.EventBus; - -public class ErrorDialogConfig { - final Resources resources; - final int defaultTitleId; - final int defaultErrorMsgId; - final ExceptionToResourceMapping mapping; - - EventBus eventBus; - boolean logExceptions = true; - String tagForLoggingExceptions; - int defaultDialogIconId; - Class defaultEventTypeOnDialogClosed; - - public ErrorDialogConfig(Resources resources, int defaultTitleId, int defaultMsgId) { - this.resources = resources; - this.defaultTitleId = defaultTitleId; - this.defaultErrorMsgId = defaultMsgId; - mapping = new ExceptionToResourceMapping(); - } - - public ErrorDialogConfig addMapping(Class clazz, int msgId) { - mapping.addMapping(clazz, msgId); - return this; - } - - public int getMessageIdForThrowable(final Throwable throwable) { - Integer resId = mapping.mapThrowable(throwable); - if (resId != null) { - return resId; - } else { - Log.d(EventBus.TAG, "No specific message ressource ID found for " + throwable); - return defaultErrorMsgId; - } - } - - public void setDefaultDialogIconId(int defaultDialogIconId) { - this.defaultDialogIconId = defaultDialogIconId; - } - - public void setDefaultEventTypeOnDialogClosed(Class defaultEventTypeOnDialogClosed) { - this.defaultEventTypeOnDialogClosed = defaultEventTypeOnDialogClosed; - } - - public void disableExceptionLogging() { - logExceptions = false; - } - - public void setTagForLoggingExceptions(String tagForLoggingExceptions) { - this.tagForLoggingExceptions = tagForLoggingExceptions; - } - - public void setEventBus(EventBus eventBus) { - this.eventBus = eventBus; - } - - /** eventBus!=null ? eventBus: EventBus.getDefault() */ - EventBus getEventBus() { - return eventBus!=null ? eventBus: EventBus.getDefault(); - } -} \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/util/ErrorDialogFragmentFactory.java b/EventBus/src/de/greenrobot/event/util/ErrorDialogFragmentFactory.java deleted file mode 100644 index 47b693b6..00000000 --- a/EventBus/src/de/greenrobot/event/util/ErrorDialogFragmentFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -package de.greenrobot.event.util; - -import android.annotation.TargetApi; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.Fragment; - -/** - * Factory to allow injecting a more complex exception mapping; typically you would subclass one of {@link Honeycomb} or - * {@link Support}. - */ -public abstract class ErrorDialogFragmentFactory { - protected final ErrorDialogConfig config; - - protected ErrorDialogFragmentFactory(ErrorDialogConfig config) { - this.config = config; - } - - /** - * Prepares the fragment's arguments and creates the fragment. May be overridden to provide custom error fragments. - */ - protected T prepareErrorFragment(ThrowableFailureEvent event, boolean finishAfterDialog, - Bundle argumentsForErrorDialog) { - if (event.isSuppressErrorUi()) { - // Show nothing by default - return null; - } - Bundle bundle; - if (argumentsForErrorDialog != null) { - bundle = (Bundle) argumentsForErrorDialog.clone(); - } else { - bundle = new Bundle(); - } - - if (!bundle.containsKey(ErrorDialogManager.KEY_TITLE)) { - String title = getTitleFor(event, bundle); - bundle.putString(ErrorDialogManager.KEY_TITLE, title); - } - if (!bundle.containsKey(ErrorDialogManager.KEY_MESSAGE)) { - String message = getMessageFor(event, bundle); - bundle.putString(ErrorDialogManager.KEY_MESSAGE, message); - } - if (!bundle.containsKey(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG)) { - bundle.putBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, finishAfterDialog); - } - if (!bundle.containsKey(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE) - && config.defaultEventTypeOnDialogClosed != null) { - bundle.putSerializable(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE, config.defaultEventTypeOnDialogClosed); - } - if (!bundle.containsKey(ErrorDialogManager.KEY_ICON_ID) && config.defaultDialogIconId != 0) { - bundle.putInt(ErrorDialogManager.KEY_ICON_ID, config.defaultDialogIconId); - } - return createErrorFragment(event, bundle); - } - - /** Returns either a new Honeycomb+ or a new support library DialogFragment. */ - protected abstract T createErrorFragment(ThrowableFailureEvent event, Bundle arguments); - - /** May be overridden to provide custom error title. */ - protected String getTitleFor(ThrowableFailureEvent event, Bundle arguments) { - return config.resources.getString(config.defaultTitleId); - } - - /** May be overridden to provide custom error messages. */ - protected String getMessageFor(ThrowableFailureEvent event, Bundle arguments) { - int msgResId = config.getMessageIdForThrowable(event.throwable); - return config.resources.getString(msgResId); - } - - public static class Support extends ErrorDialogFragmentFactory { - - public Support(ErrorDialogConfig config) { - super(config); - } - - protected Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) { - ErrorDialogFragments.Support errorFragment = new ErrorDialogFragments.Support(); - errorFragment.setArguments(arguments); - return errorFragment; - } - - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static class Honeycomb extends ErrorDialogFragmentFactory { - - public Honeycomb(ErrorDialogConfig config) { - super(config); - } - - protected android.app.Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) { - ErrorDialogFragments.Honeycomb errorFragment = new ErrorDialogFragments.Honeycomb(); - errorFragment.setArguments(arguments); - return errorFragment; - } - - } -} \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/util/ErrorDialogFragments.java b/EventBus/src/de/greenrobot/event/util/ErrorDialogFragments.java deleted file mode 100644 index 04002df2..00000000 --- a/EventBus/src/de/greenrobot/event/util/ErrorDialogFragments.java +++ /dev/null @@ -1,74 +0,0 @@ -package de.greenrobot.event.util; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import de.greenrobot.event.EventBus; - -public class ErrorDialogFragments { - /** TODO Use config: Icon res ID to use for all error dialogs. May be configured by each app (optional). */ - public static int ERROR_DIALOG_ICON = 0; - - /** TODO Use config: Event class to be fired on dismissing the dialog by the user. May be configured by each app. */ - public static Class EVENT_TYPE_ON_CLICK; - - public static Dialog createDialog(Context context, Bundle arguments, OnClickListener onClickListener) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(arguments.getString(ErrorDialogManager.KEY_TITLE)); - builder.setMessage(arguments.getString(ErrorDialogManager.KEY_MESSAGE)); - if (ERROR_DIALOG_ICON != 0) { - builder.setIcon(ERROR_DIALOG_ICON); - } - builder.setPositiveButton(android.R.string.ok, onClickListener); - return builder.create(); - } - - public static void handleOnClick(DialogInterface dialog, int which, Activity activity, Bundle arguments) { - if (EVENT_TYPE_ON_CLICK != null) { - Object event; - try { - event = EVENT_TYPE_ON_CLICK.newInstance(); - } catch (Exception e) { - throw new RuntimeException("Event cannot be constructed", e); - } - EventBus eventBus = ErrorDialogManager.factory.config.getEventBus(); - eventBus.post(event); - } - boolean finish = arguments.getBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, false); - if (finish && activity != null) { - activity.finish(); - } - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static class Honeycomb extends android.app.DialogFragment implements OnClickListener { - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return createDialog(getActivity(), getArguments(), this); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - handleOnClick(dialog, which, getActivity(), getArguments()); - } - } - - public static class Support extends DialogFragment implements OnClickListener { - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - return createDialog(getActivity(), getArguments(), this); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - handleOnClick(dialog, which, getActivity(), getArguments()); - } - } -} diff --git a/EventBus/src/de/greenrobot/event/util/ErrorDialogManager.java b/EventBus/src/de/greenrobot/event/util/ErrorDialogManager.java deleted file mode 100644 index cee7c6a5..00000000 --- a/EventBus/src/de/greenrobot/event/util/ErrorDialogManager.java +++ /dev/null @@ -1,245 +0,0 @@ -package de.greenrobot.event.util; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.Application; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.util.Log; -import de.greenrobot.event.EventBus; - -/** - * Central class for app that want to use event based error dialogs.
- *
- * How to use: - *
    - *
  1. Set the {@link #factory} to configure dialogs for your app, typically in {@link Application#onCreate()}
  2. - *
  3. Use one of {@link #attachTo(Activity)}, {@link #attachTo(Activity, boolean)} or - * {@link #attachTo(Activity, boolean, Bundle)} in your Activity, typically in onCreate.
  4. - *
- * - * For more complex mappings, you can supply your own {@link ErrorDialogFragmentFactory}. - * - * @author Markus - */ -public class ErrorDialogManager { - - public static class SupportManagerFragment extends Fragment { - protected boolean finishAfterDialog; - protected Bundle argumentsForErrorDialog; - private EventBus eventBus; - private boolean skipRegisterOnNextResume; - private Object executionScope; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - eventBus = ErrorDialogManager.factory.config.getEventBus(); - eventBus.register(this); - skipRegisterOnNextResume = true; - } - - @Override - public void onResume() { - super.onResume(); - if (skipRegisterOnNextResume) { - // registered in onCreate, skip registration in this run - skipRegisterOnNextResume = false; - } else { - eventBus = ErrorDialogManager.factory.config.getEventBus(); - eventBus.register(this); - } - } - - @Override - public void onPause() { - eventBus.unregister(this); - super.onPause(); - } - - public void onEventMainThread(ThrowableFailureEvent event) { - if (!isInExecutionScope(executionScope, event)) { - return; - } - checkLogException(event); - // Execute pending commits before finding to avoid multiple error fragments being shown - FragmentManager fm = getFragmentManager(); - fm.executePendingTransactions(); - - DialogFragment existingFragment = (DialogFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG); - if (existingFragment != null) { - // Just show the latest error - existingFragment.dismiss(); - } - - android.support.v4.app.DialogFragment errorFragment = (android.support.v4.app.DialogFragment) factory - .prepareErrorFragment(event, finishAfterDialog, argumentsForErrorDialog); - if (errorFragment != null) { - errorFragment.show(fm, TAG_ERROR_DIALOG); - } - } - - public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, - Bundle argumentsForErrorDialog) { - FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager(); - SupportManagerFragment fragment = (SupportManagerFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG_MANAGER); - if (fragment == null) { - fragment = new SupportManagerFragment(); - fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit(); - fm.executePendingTransactions(); - } - fragment.finishAfterDialog = finishAfterDialog; - fragment.argumentsForErrorDialog = argumentsForErrorDialog; - fragment.executionScope = executionScope; - } - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static class HoneycombManagerFragment extends android.app.Fragment { - protected boolean finishAfterDialog; - protected Bundle argumentsForErrorDialog; - private EventBus eventBus; - private Object executionScope; - - @Override - public void onResume() { - super.onResume(); - eventBus = ErrorDialogManager.factory.config.getEventBus(); - eventBus.register(this); - } - - @Override - public void onPause() { - eventBus.unregister(this); - super.onPause(); - } - - public void onEventMainThread(ThrowableFailureEvent event) { - if (!isInExecutionScope(executionScope, event)) { - return; - } - checkLogException(event); - - // Execute pending commits before finding to avoid multiple error fragments being shown - android.app.FragmentManager fm = getFragmentManager(); - fm.executePendingTransactions(); - - android.app.DialogFragment existingFragment = (android.app.DialogFragment) fm - .findFragmentByTag(TAG_ERROR_DIALOG); - if (existingFragment != null) { - // Just show the latest error - existingFragment.dismiss(); - } - - android.app.DialogFragment errorFragment = (android.app.DialogFragment) factory.prepareErrorFragment(event, - finishAfterDialog, argumentsForErrorDialog); - if (errorFragment != null) { - errorFragment.show(fm, TAG_ERROR_DIALOG); - } - } - - public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { - android.app.FragmentManager fm = activity.getFragmentManager(); - HoneycombManagerFragment fragment = (HoneycombManagerFragment) fm - .findFragmentByTag(TAG_ERROR_DIALOG_MANAGER); - if (fragment == null) { - fragment = new HoneycombManagerFragment(); - fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit(); - fm.executePendingTransactions(); - } - fragment.finishAfterDialog = finishAfterDialog; - fragment.argumentsForErrorDialog = argumentsForErrorDialog; - fragment.executionScope = executionScope; - } - } - - /** Must be set by the application. */ - public static ErrorDialogFragmentFactory factory; - - protected static final String TAG_ERROR_DIALOG = "de.greenrobot.eventbus.error_dialog"; - protected static final String TAG_ERROR_DIALOG_MANAGER = "de.greenrobot.eventbus.error_dialog_manager"; - - public static final String KEY_TITLE = "de.greenrobot.eventbus.errordialog.title"; - public static final String KEY_MESSAGE = "de.greenrobot.eventbus.errordialog.message"; - public static final String KEY_FINISH_AFTER_DIALOG = "de.greenrobot.eventbus.errordialog.finish_after_dialog"; - public static final String KEY_ICON_ID = "de.greenrobot.eventbus.errordialog.icon_id"; - public static final String KEY_EVENT_TYPE_ON_CLOSE = "de.greenrobot.eventbus.errordialog.event_type_on_close"; - - /** Scope is limited to the activity's class. */ - public static void attachTo(Activity activity) { - attachTo(activity, false, null); - } - - /** Scope is limited to the activity's class. */ - public static void attachTo(Activity activity, boolean finishAfterDialog) { - attachTo(activity, finishAfterDialog, null); - } - - /** Scope is limited to the activity's class. */ - public static void attachTo(Activity activity, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { - Object executionScope = activity.getClass(); - attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); - } - - public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) { - if (factory == null) { - throw new RuntimeException("You must set the static factory field to configure error dialogs for your app."); - } - if (isSupportActivity(activity)) { - SupportManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); - } else { - HoneycombManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog); - } - } - - private static boolean isSupportActivity(Activity activity) { - boolean isSupport = false; - for (Class c = activity.getClass().getSuperclass();; c = c.getSuperclass()) { - if (c == null) { - throw new RuntimeException("Illegal activity type: " + activity.getClass()); - } - String name = c.getName(); - if (name.equals("android.support.v4.app.FragmentActivity")) { - isSupport = true; - break; - } else if (name.startsWith("com.actionbarsherlock.app") - && (name.endsWith(".SherlockActivity") || name.endsWith(".SherlockListActivity") || name - .endsWith(".SherlockPreferenceActivity"))) { - throw new RuntimeException("Please use SherlockFragmentActivity. Illegal activity: " + name); - } else if (name.equals("android.app.Activity")) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - throw new RuntimeException( - "Illegal activity without fragment support. Either use Android 3.0+ or android.support.v4.app.FragmentActivity."); - } - break; - } - } - return isSupport; - } - - protected static void checkLogException(ThrowableFailureEvent event) { - if (factory.config.logExceptions) { - String tag = factory.config.tagForLoggingExceptions; - if (tag == null) { - tag = EventBus.TAG; - } - Log.i(tag, "Error dialog manager received exception", event.throwable); - } - } - - private static boolean isInExecutionScope(Object executionScope, ThrowableFailureEvent event) { - if (event != null) { - Object eventExecutionScope = event.getExecutionScope(); - if (eventExecutionScope != null && !eventExecutionScope.equals(executionScope)) { - // Event not in our scope, do nothing - return false; - } - } - return true; - } - -} diff --git a/EventBus/src/de/greenrobot/event/util/HasExecutionScope.java b/EventBus/src/de/greenrobot/event/util/HasExecutionScope.java deleted file mode 100644 index d759d2c2..00000000 --- a/EventBus/src/de/greenrobot/event/util/HasExecutionScope.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.greenrobot.event.util; - -public interface HasExecutionScope { - Object getExecutionScope(); - - void setExecutionScope(Object executionScope); - -} diff --git a/EventBus/src/de/greenrobot/event/AsyncPoster.java b/EventBus/src/org/greenrobot/eventbus/AsyncPoster.java similarity index 89% rename from EventBus/src/de/greenrobot/event/AsyncPoster.java rename to EventBus/src/org/greenrobot/eventbus/AsyncPoster.java index 936527b7..90a30d1e 100644 --- a/EventBus/src/de/greenrobot/event/AsyncPoster.java +++ b/EventBus/src/org/greenrobot/eventbus/AsyncPoster.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; /** @@ -21,7 +21,7 @@ * * @author Markus */ -class AsyncPoster implements Runnable { +class AsyncPoster implements Runnable, Poster { private final PendingPostQueue queue; private final EventBus eventBus; diff --git a/EventBus/src/de/greenrobot/event/BackgroundPoster.java b/EventBus/src/org/greenrobot/eventbus/BackgroundPoster.java similarity index 86% rename from EventBus/src/de/greenrobot/event/BackgroundPoster.java rename to EventBus/src/org/greenrobot/eventbus/BackgroundPoster.java index 00e9ee54..94db640e 100644 --- a/EventBus/src/de/greenrobot/event/BackgroundPoster.java +++ b/EventBus/src/org/greenrobot/eventbus/BackgroundPoster.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; -import android.util.Log; +import java.util.logging.Level; /** * Posts events in background. - * + * * @author Markus */ -final class BackgroundPoster implements Runnable { +final class BackgroundPoster implements Runnable, Poster { private final PendingPostQueue queue; private final EventBus eventBus; @@ -64,7 +64,7 @@ public void run() { eventBus.invokeSubscriber(pendingPost); } } catch (InterruptedException e) { - Log.w("Event", Thread.currentThread().getName() + " was interruppted", e); + eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interrupted", e); } } finally { executorRunning = false; diff --git a/EventBus/src/de/greenrobot/event/EventBus.java b/EventBus/src/org/greenrobot/eventbus/EventBus.java similarity index 71% rename from EventBus/src/de/greenrobot/event/EventBus.java rename to EventBus/src/org/greenrobot/eventbus/EventBus.java index 4bac5cbe..147bb5ff 100644 --- a/EventBus/src/de/greenrobot/event/EventBus.java +++ b/EventBus/src/org/greenrobot/eventbus/EventBus.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2020 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,38 +13,40 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; - -import android.os.Looper; -import android.util.Log; +package org.greenrobot.eventbus; +import org.greenrobot.eventbus.android.AndroidDependenciesDetector; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; +import java.util.logging.Level; /** - * EventBus is a central publish/subscribe event system for Android. Events are posted ({@link #post(Object)}) to the - * bus, which delivers it to subscribers that have a matching handler method for the event type. To receive events, - * subscribers must register themselves to the bus using {@link #register(Object)}. Once registered, - * subscribers receive events until {@link #unregister(Object)} is called. By convention, event handling methods must - * be named "onEvent", be public, return nothing (void), and have exactly one parameter (the event). + * EventBus is a central publish/subscribe event system for Java and Android. + * Events are posted ({@link #post(Object)}) to the bus, which delivers it to subscribers that have a matching handler + * method for the event type. + * To receive events, subscribers must register themselves to the bus using {@link #register(Object)}. + * Once registered, subscribers receive events until {@link #unregister(Object)} is called. + * Event handling methods must be annotated by {@link Subscribe}, must be public, return nothing (void), + * and have exactly one parameter (the event). * * @author Markus Junginger, greenrobot */ public class EventBus { /** Log tag, apps may override it. */ - public static String TAG = "Event"; + public static String TAG = "EventBus"; static volatile EventBus defaultInstance; private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder(); - private static final Map, List>> eventTypesCache = new HashMap, List>>(); + private static final Map, List>> eventTypesCache = new HashMap<>(); private final Map, CopyOnWriteArrayList> subscriptionsByEventType; private final Map>> typesBySubscriber; @@ -57,8 +59,10 @@ protected PostingThreadState initialValue() { } }; - - private final HandlerPoster mainThreadPoster; + // @Nullable + private final MainThreadSupport mainThreadSupport; + // @Nullable + private final Poster mainThreadPoster; private final BackgroundPoster backgroundPoster; private final AsyncPoster asyncPoster; private final SubscriberMethodFinder subscriberMethodFinder; @@ -71,16 +75,21 @@ protected PostingThreadState initialValue() { private final boolean sendNoSubscriberEvent; private final boolean eventInheritance; + private final int indexCount; + private final Logger logger; + /** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { - if (defaultInstance == null) { + EventBus instance = defaultInstance; + if (instance == null) { synchronized (EventBus.class) { - if (defaultInstance == null) { - defaultInstance = new EventBus(); + instance = EventBus.defaultInstance; + if (instance == null) { + instance = EventBus.defaultInstance = new EventBus(); } } } - return defaultInstance; + return instance; } public static EventBusBuilder builder() { @@ -102,13 +111,17 @@ public EventBus() { } EventBus(EventBusBuilder builder) { - subscriptionsByEventType = new HashMap, CopyOnWriteArrayList>(); - typesBySubscriber = new HashMap>>(); - stickyEvents = new ConcurrentHashMap, Object>(); - mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); + logger = builder.getLogger(); + subscriptionsByEventType = new HashMap<>(); + typesBySubscriber = new HashMap<>(); + stickyEvents = new ConcurrentHashMap<>(); + mainThreadSupport = builder.getMainThreadSupport(); + mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; backgroundPoster = new BackgroundPoster(this); asyncPoster = new AsyncPoster(this); - subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses); + indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; + subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, + builder.strictMethodVerification, builder.ignoreGeneratedIndex); logSubscriberExceptions = builder.logSubscriberExceptions; logNoSubscriberMessages = builder.logNoSubscriberMessages; sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; @@ -118,61 +131,37 @@ public EventBus() { executorService = builder.executorService; } - /** * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they * are no longer interested in receiving events. *

- * Subscribers have event handling methods that are identified by their name, typically called "onEvent". Event - * handling methods must have exactly one parameter, the event. If the event handling method is to be called in a - * specific thread, a modifier is appended to the method name. Valid modifiers match one of the {@link ThreadMode} - * enums. For example, if a method is to be called in the UI/main thread by EventBus, it would be called - * "onEventMainThread". + * Subscribers have event handling methods that must be annotated by {@link Subscribe}. + * The {@link Subscribe} annotation also allows configuration like {@link + * ThreadMode} and priority. */ public void register(Object subscriber) { - register(subscriber, false, 0); - } - - /** - * Like {@link #register(Object)} with an additional subscriber priority to influence the order of event delivery. - * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before - * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of - * delivery among subscribers with different {@link ThreadMode}s! - */ - public void register(Object subscriber, int priority) { - register(subscriber, false, priority); - } - - /** - * Like {@link #register(Object)}, but also triggers delivery of the most recent sticky event (posted with - * {@link #postSticky(Object)}) to the given subscriber. - */ - public void registerSticky(Object subscriber) { - register(subscriber, true, 0); - } - - /** - * Like {@link #register(Object, int)}, but also triggers delivery of the most recent sticky event (posted with - * {@link #postSticky(Object)}) to the given subscriber. - */ - public void registerSticky(Object subscriber, int priority) { - register(subscriber, true, priority); - } + if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) { + // Crash if the user (developer) has not imported the Android compatibility library. + throw new RuntimeException("It looks like you are using EventBus on Android, " + + "make sure to add the \"eventbus\" Android library to your dependencies."); + } - private synchronized void register(Object subscriber, boolean sticky, int priority) { - List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); - for (SubscriberMethod subscriberMethod : subscriberMethods) { - subscribe(subscriber, subscriberMethod, sticky, priority); + Class subscriberClass = subscriber.getClass(); + List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); + synchronized (this) { + for (SubscriberMethod subscriberMethod : subscriberMethods) { + subscribe(subscriber, subscriberMethod); + } } } // Must be called in synchronized block - private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { + private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class eventType = subscriberMethod.eventType; + Subscription newSubscription = new Subscription(subscriber, subscriberMethod); CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); - Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); if (subscriptions == null) { - subscriptions = new CopyOnWriteArrayList(); + subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { @@ -181,12 +170,9 @@ private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boo } } - // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) - // subscriberMethod.method.setAccessible(true); - int size = subscriptions.size(); for (int i = 0; i <= size; i++) { - if (i == size || newSubscription.priority > subscriptions.get(i).priority) { + if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } @@ -194,30 +180,56 @@ private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boo List> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { - subscribedEvents = new ArrayList>(); + subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); - if (sticky) { - Object stickyEvent; - synchronized (stickyEvents) { - stickyEvent = stickyEvents.get(eventType); - } - if (stickyEvent != null) { - // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) - // --> Strange corner case, which we don't take care of here. - postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); + if (subscriberMethod.sticky) { + if (eventInheritance) { + // Existing sticky events of all subclasses of eventType have to be considered. + // Note: Iterating over all events may be inefficient with lots of sticky events, + // thus data structure should be changed to allow a more efficient lookup + // (e.g. an additional map storing sub classes of super classes: Class -> List). + Set, Object>> entries = stickyEvents.entrySet(); + for (Map.Entry, Object> entry : entries) { + Class candidateEventType = entry.getKey(); + if (eventType.isAssignableFrom(candidateEventType)) { + Object stickyEvent = entry.getValue(); + checkPostStickyEventToSubscription(newSubscription, stickyEvent); + } + } + } else { + Object stickyEvent = stickyEvents.get(eventType); + checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } + private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { + if (stickyEvent != null) { + // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) + // --> Strange corner case, which we don't take care of here. + postToSubscription(newSubscription, stickyEvent, isMainThread()); + } + } + + /** + * Checks if the current thread is running in the main thread. + * If there is no main thread support (e.g. non-Android), "true" is always returned. In that case MAIN thread + * subscribers are always called in posting thread, and BACKGROUND subscribers are always called from a background + * poster. + */ + private boolean isMainThread() { + return mainThreadSupport == null || mainThreadSupport.isMainThread(); + } + public synchronized boolean isRegistered(Object subscriber) { return typesBySubscriber.containsKey(subscriber); } /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ - private void unubscribeByEventType(Object subscriber, Class eventType) { + private void unsubscribeByEventType(Object subscriber, Class eventType) { List subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); @@ -238,11 +250,11 @@ public synchronized void unregister(Object subscriber) { List> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class eventType : subscribedTypes) { - unubscribeByEventType(subscriber, eventType); + unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { - Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); + logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } } @@ -253,7 +265,7 @@ public void post(Object event) { eventQueue.add(event); if (!postingState.isPosting) { - postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); + postingState.isMainThread = isMainThread(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); @@ -273,8 +285,8 @@ public void post(Object event) { * Called from a subscriber's event handling method, further event delivery will be canceled. Subsequent * subscribers * won't receive the event. Events are usually canceled by higher priority subscribers (see - * {@link #register(Object, int)}). Canceling is restricted to event handling methods running in posting thread - * {@link ThreadMode#PostThread}. + * {@link Subscribe#priority()}). Canceling is restricted to event handling methods running in posting thread + * {@link ThreadMode#POSTING}. */ public void cancelEventDelivery(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); @@ -285,7 +297,7 @@ public void cancelEventDelivery(Object event) { throw new EventBusException("Event may not be null"); } else if (postingState.event != event) { throw new EventBusException("Only the currently handled event may be aborted"); - } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.PostThread) { + } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) { throw new EventBusException(" event handlers may only abort the incoming event"); } @@ -294,8 +306,7 @@ public void cancelEventDelivery(Object event) { /** * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky - * event of an event's type is kept in memory for future access. This can be {@link #registerSticky(Object)} or - * {@link #getStickyEvent(Class)}. + * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}. */ public void postSticky(Object event) { synchronized (stickyEvents) { @@ -387,7 +398,7 @@ private void postSingleEvent(Object event, PostingThreadState postingState) thro } if (!subscriptionFound) { if (logNoSubscriberMessages) { - Log.d(TAG, "No subscribers registered for event " + eventClass); + logger.log(Level.FINE, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { @@ -405,7 +416,7 @@ private boolean postSingleEventForEventType(Object event, PostingThreadState pos for (Subscription subscription : subscriptions) { postingState.event = event; postingState.subscription = subscription; - boolean aborted = false; + boolean aborted; try { postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; @@ -425,24 +436,32 @@ private boolean postSingleEventForEventType(Object event, PostingThreadState pos private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { - case PostThread: + case POSTING: invokeSubscriber(subscription, event); break; - case MainThread: + case MAIN: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; - case BackgroundThread: + case MAIN_ORDERED: + if (mainThreadPoster != null) { + mainThreadPoster.enqueue(subscription, event); + } else { + // temporary: technically not correct as poster not decoupled from subscriber + invokeSubscriber(subscription, event); + } + break; + case BACKGROUND: if (isMainThread) { backgroundPoster.enqueue(subscription, event); } else { invokeSubscriber(subscription, event); } break; - case Async: + case ASYNC: asyncPoster.enqueue(subscription, event); break; default: @@ -451,11 +470,11 @@ private void postToSubscription(Subscription subscription, Object event, boolean } /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ - private List> lookupAllEventTypes(Class eventClass) { + private static List> lookupAllEventTypes(Class eventClass) { synchronized (eventTypesCache) { List> eventTypes = eventTypesCache.get(eventClass); if (eventTypes == null) { - eventTypes = new ArrayList>(); + eventTypes = new ArrayList<>(); Class clazz = eventClass; while (clazz != null) { eventTypes.add(clazz); @@ -507,10 +526,10 @@ private void handleSubscriberException(Subscription subscription, Object event, if (event instanceof SubscriberExceptionEvent) { if (logSubscriberExceptions) { // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log - Log.e(TAG, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass() + logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass() + " threw an exception", cause); SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event; - Log.e(TAG, "Initial event " + exEvent.causingEvent + " caused exception in " + logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in " + exEvent.causingSubscriber, exEvent.throwable); } } else { @@ -518,7 +537,7 @@ private void handleSubscriberException(Subscription subscription, Object event, throw new EventBusException("Invoking subscriber failed", cause); } if (logSubscriberExceptions) { - Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class " + logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class " + subscription.subscriber.getClass(), cause); } if (sendSubscriberExceptionEvent) { @@ -531,7 +550,7 @@ private void handleSubscriberException(Subscription subscription, Object event, /** For ThreadLocal, much faster to set (and get multiple values). */ final static class PostingThreadState { - final List eventQueue = new ArrayList(); + final List eventQueue = new ArrayList<>(); boolean isPosting; boolean isMainThread; Subscription subscription; @@ -543,9 +562,20 @@ ExecutorService getExecutorService() { return executorService; } + /** + * For internal use only. + */ + public Logger getLogger() { + return logger; + } + // Just an idea: we could provide a callback to post() to be notified, an alternative would be events, of course... /* public */interface PostCallback { void onPostCompleted(List exceptionEvents); } + @Override + public String toString() { + return "EventBus[indexCount=" + indexCount + ", eventInheritance=" + eventInheritance + "]"; + } } diff --git a/EventBus/src/de/greenrobot/event/EventBusBuilder.java b/EventBus/src/org/greenrobot/eventbus/EventBusBuilder.java similarity index 71% rename from EventBus/src/de/greenrobot/event/EventBusBuilder.java rename to EventBus/src/org/greenrobot/eventbus/EventBusBuilder.java index 06350609..6df346d6 100644 --- a/EventBus/src/de/greenrobot/event/EventBusBuilder.java +++ b/EventBus/src/org/greenrobot/eventbus/EventBusBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2020 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; +import org.greenrobot.eventbus.android.AndroidComponents; +import org.greenrobot.eventbus.meta.SubscriberInfoIndex; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; @@ -24,6 +26,7 @@ * Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance. * Create a new builder using {@link EventBus#builder()}. */ +@SuppressWarnings("unused") public class EventBusBuilder { private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); @@ -33,8 +36,13 @@ public class EventBusBuilder { boolean sendNoSubscriberEvent = true; boolean throwSubscriberException; boolean eventInheritance = true; + boolean ignoreGeneratedIndex; + boolean strictMethodVerification; ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; List> skipMethodVerificationForClasses; + List subscriberInfoIndexes; + Logger logger; + MainThreadSupport mainThreadSupport; EventBusBuilder() { } @@ -78,7 +86,7 @@ public EventBusBuilder throwSubscriberException(boolean throwSubscriberException * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified). * Switching this feature off will improve posting of events. For simple event classes extending Object directly, * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be - * >20%. + * greater than 20%. *

* However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app, * unless it is posting at high rates, e.g. hundreds/thousands of events per second. @@ -105,12 +113,61 @@ public EventBusBuilder executorService(ExecutorService executorService) { */ public EventBusBuilder skipMethodVerificationFor(Class clazz) { if (skipMethodVerificationForClasses == null) { - skipMethodVerificationForClasses = new ArrayList>(); + skipMethodVerificationForClasses = new ArrayList<>(); } skipMethodVerificationForClasses.add(clazz); return this; } + /** Forces the use of reflection even if there's a generated index (default: false). */ + public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) { + this.ignoreGeneratedIndex = ignoreGeneratedIndex; + return this; + } + + /** Enables strict method verification (default: false). */ + public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) { + this.strictMethodVerification = strictMethodVerification; + return this; + } + + /** Adds an index generated by EventBus' annotation preprocessor. */ + public EventBusBuilder addIndex(SubscriberInfoIndex index) { + if (subscriberInfoIndexes == null) { + subscriberInfoIndexes = new ArrayList<>(); + } + subscriberInfoIndexes.add(index); + return this; + } + + /** + * Set a specific log handler for all EventBus logging. + *

+ * By default, all logging is via {@code android.util.Log} on Android or System.out on JVM. + */ + public EventBusBuilder logger(Logger logger) { + this.logger = logger; + return this; + } + + Logger getLogger() { + if (logger != null) { + return logger; + } else { + return Logger.Default.get(); + } + } + + MainThreadSupport getMainThreadSupport() { + if (mainThreadSupport != null) { + return mainThreadSupport; + } else if (AndroidComponents.areAvailable()) { + return AndroidComponents.get().defaultMainThreadSupport; + } else { + return null; + } + } + /** * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be * done only once before the first usage of the default EventBus. diff --git a/EventBus/src/de/greenrobot/event/EventBusException.java b/EventBus/src/org/greenrobot/eventbus/EventBusException.java similarity index 90% rename from EventBus/src/de/greenrobot/event/EventBusException.java rename to EventBus/src/org/greenrobot/eventbus/EventBusException.java index 80c51f86..20d50e6b 100644 --- a/EventBus/src/de/greenrobot/event/EventBusException.java +++ b/EventBus/src/org/greenrobot/eventbus/EventBusException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; /** * An {@link RuntimeException} thrown in cases something went wrong inside EventBus. diff --git a/EventBus/src/org/greenrobot/eventbus/Logger.java b/EventBus/src/org/greenrobot/eventbus/Logger.java new file mode 100644 index 00000000..e9ec8e88 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/Logger.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012-2020 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.greenrobot.eventbus.android.AndroidComponents; +import java.util.logging.Level; + +public interface Logger { + + void log(Level level, String msg); + + void log(Level level, String msg, Throwable th); + + class JavaLogger implements Logger { + protected final java.util.logging.Logger logger; + + public JavaLogger(String tag) { + logger = java.util.logging.Logger.getLogger(tag); + } + + @Override + public void log(Level level, String msg) { + // TODO Replace logged method with caller method + logger.log(level, msg); + } + + @Override + public void log(Level level, String msg, Throwable th) { + // TODO Replace logged method with caller method + logger.log(level, msg, th); + } + + } + + class SystemOutLogger implements Logger { + + @Override + public void log(Level level, String msg) { + System.out.println("[" + level + "] " + msg); + } + + @Override + public void log(Level level, String msg, Throwable th) { + System.out.println("[" + level + "] " + msg); + th.printStackTrace(System.out); + } + + } + + class Default { + public static Logger get() { + if (AndroidComponents.areAvailable()) { + return AndroidComponents.get().logger; + } + + return new SystemOutLogger(); + } + } + +} diff --git a/EventBus/src/org/greenrobot/eventbus/MainThreadSupport.java b/EventBus/src/org/greenrobot/eventbus/MainThreadSupport.java new file mode 100644 index 00000000..22605811 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/MainThreadSupport.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +/** + * Interface to the "main" thread, which can be whatever you like. Typically on Android, Android's main thread is used. + */ +public interface MainThreadSupport { + + boolean isMainThread(); + + Poster createPoster(EventBus eventBus); +} diff --git a/EventBus/src/de/greenrobot/event/NoSubscriberEvent.java b/EventBus/src/org/greenrobot/eventbus/NoSubscriberEvent.java similarity index 90% rename from EventBus/src/de/greenrobot/event/NoSubscriberEvent.java rename to EventBus/src/org/greenrobot/eventbus/NoSubscriberEvent.java index a7378ae8..e4da4757 100644 --- a/EventBus/src/de/greenrobot/event/NoSubscriberEvent.java +++ b/EventBus/src/org/greenrobot/eventbus/NoSubscriberEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; /** * This Event is posted by EventBus when no subscriber is found for a posted event. diff --git a/EventBus/src/de/greenrobot/event/PendingPost.java b/EventBus/src/org/greenrobot/eventbus/PendingPost.java similarity index 94% rename from EventBus/src/de/greenrobot/event/PendingPost.java rename to EventBus/src/org/greenrobot/eventbus/PendingPost.java index 0bd5a2ec..01f474c2 100644 --- a/EventBus/src/de/greenrobot/event/PendingPost.java +++ b/EventBus/src/org/greenrobot/eventbus/PendingPost.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; import java.util.ArrayList; import java.util.List; diff --git a/EventBus/src/de/greenrobot/event/PendingPostQueue.java b/EventBus/src/org/greenrobot/eventbus/PendingPostQueue.java similarity index 59% rename from EventBus/src/de/greenrobot/event/PendingPostQueue.java rename to EventBus/src/org/greenrobot/eventbus/PendingPostQueue.java index 5440559b..55db529a 100644 --- a/EventBus/src/de/greenrobot/event/PendingPostQueue.java +++ b/EventBus/src/org/greenrobot/eventbus/PendingPostQueue.java @@ -1,4 +1,20 @@ -package de.greenrobot.event; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; final class PendingPostQueue { private PendingPost head; diff --git a/EventBus/src/org/greenrobot/eventbus/Poster.java b/EventBus/src/org/greenrobot/eventbus/Poster.java new file mode 100644 index 00000000..67cfd67c --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/Poster.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +/** + * Posts events. + * + * @author William Ferguson + */ +public interface Poster { + + /** + * Enqueue an event to be posted for a particular subscription. + * + * @param subscription Subscription which will receive the event. + * @param event Event that will be posted to subscribers. + */ + void enqueue(Subscription subscription, Object event); +} diff --git a/EventBus/src/org/greenrobot/eventbus/Subscribe.java b/EventBus/src/org/greenrobot/eventbus/Subscribe.java new file mode 100644 index 00000000..ed0b8c82 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/Subscribe.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Subscribe { + ThreadMode threadMode() default ThreadMode.POSTING; + + /** + * If true, delivers the most recent sticky event (posted with + * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). + */ + boolean sticky() default false; + + /** Subscriber priority to influence the order of event delivery. + * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before + * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of + * delivery among subscribers with different {@link ThreadMode}s! */ + int priority() default 0; +} + diff --git a/EventBus/src/de/greenrobot/event/SubscriberExceptionEvent.java b/EventBus/src/org/greenrobot/eventbus/SubscriberExceptionEvent.java similarity index 92% rename from EventBus/src/de/greenrobot/event/SubscriberExceptionEvent.java rename to EventBus/src/org/greenrobot/eventbus/SubscriberExceptionEvent.java index 5d3b9b55..ff69f05a 100644 --- a/EventBus/src/de/greenrobot/event/SubscriberExceptionEvent.java +++ b/EventBus/src/org/greenrobot/eventbus/SubscriberExceptionEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; /** * This Event is posted by EventBus when an exception occurs inside a subscriber's event handling method. diff --git a/EventBus/src/de/greenrobot/event/SubscriberMethod.java b/EventBus/src/org/greenrobot/eventbus/SubscriberMethod.java similarity index 77% rename from EventBus/src/de/greenrobot/event/SubscriberMethod.java rename to EventBus/src/org/greenrobot/eventbus/SubscriberMethod.java index 5e0df398..1d78d479 100644 --- a/EventBus/src/de/greenrobot/event/SubscriberMethod.java +++ b/EventBus/src/org/greenrobot/eventbus/SubscriberMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,26 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; import java.lang.reflect.Method; -final class SubscriberMethod { +/** Used internally by EventBus and generated subscriber indexes. */ +public class SubscriberMethod { final Method method; final ThreadMode threadMode; final Class eventType; + final int priority; + final boolean sticky; /** Used for efficient comparison */ String methodString; - SubscriberMethod(Method method, ThreadMode threadMode, Class eventType) { + public SubscriberMethod(Method method, Class eventType, ThreadMode threadMode, int priority, boolean sticky) { this.method = method; this.threadMode = threadMode; this.eventType = eventType; + this.priority = priority; + this.sticky = sticky; } @Override public boolean equals(Object other) { - if (other instanceof SubscriberMethod) { + if (other == this) { + return true; + } else if (other instanceof SubscriberMethod) { checkMethodString(); SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other; otherSubscriberMethod.checkMethodString(); diff --git a/EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java b/EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java new file mode 100644 index 00000000..3f2812bc --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.greenrobot.eventbus.meta.SubscriberInfo; +import org.greenrobot.eventbus.meta.SubscriberInfoIndex; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +class SubscriberMethodFinder { + /* + * In newer class files, compilers may add methods. Those are called bridge or synthetic methods. + * EventBus must ignore both. There modifiers are not public but defined in the Java class file format: + * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1 + */ + private static final int BRIDGE = 0x40; + private static final int SYNTHETIC = 0x1000; + + private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC; + private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>(); + + private List subscriberInfoIndexes; + private final boolean strictMethodVerification; + private final boolean ignoreGeneratedIndex; + + private static final int POOL_SIZE = 4; + private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]; + + SubscriberMethodFinder(List subscriberInfoIndexes, boolean strictMethodVerification, + boolean ignoreGeneratedIndex) { + this.subscriberInfoIndexes = subscriberInfoIndexes; + this.strictMethodVerification = strictMethodVerification; + this.ignoreGeneratedIndex = ignoreGeneratedIndex; + } + + List findSubscriberMethods(Class subscriberClass) { + List subscriberMethods = METHOD_CACHE.get(subscriberClass); + if (subscriberMethods != null) { + return subscriberMethods; + } + + if (ignoreGeneratedIndex) { + subscriberMethods = findUsingReflection(subscriberClass); + } else { + subscriberMethods = findUsingInfo(subscriberClass); + } + if (subscriberMethods.isEmpty()) { + throw new EventBusException("Subscriber " + subscriberClass + + " and its super classes have no public methods with the @Subscribe annotation"); + } else { + METHOD_CACHE.put(subscriberClass, subscriberMethods); + return subscriberMethods; + } + } + + private List findUsingInfo(Class subscriberClass) { + FindState findState = prepareFindState(); + findState.initForSubscriber(subscriberClass); + while (findState.clazz != null) { + findState.subscriberInfo = getSubscriberInfo(findState); + if (findState.subscriberInfo != null) { + SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); + for (SubscriberMethod subscriberMethod : array) { + if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { + findState.subscriberMethods.add(subscriberMethod); + } + } + } else { + findUsingReflectionInSingleClass(findState); + } + findState.moveToSuperclass(); + } + return getMethodsAndRelease(findState); + } + + private List getMethodsAndRelease(FindState findState) { + List subscriberMethods = new ArrayList<>(findState.subscriberMethods); + findState.recycle(); + synchronized (FIND_STATE_POOL) { + for (int i = 0; i < POOL_SIZE; i++) { + if (FIND_STATE_POOL[i] == null) { + FIND_STATE_POOL[i] = findState; + break; + } + } + } + return subscriberMethods; + } + + private FindState prepareFindState() { + synchronized (FIND_STATE_POOL) { + for (int i = 0; i < POOL_SIZE; i++) { + FindState state = FIND_STATE_POOL[i]; + if (state != null) { + FIND_STATE_POOL[i] = null; + return state; + } + } + } + return new FindState(); + } + + private SubscriberInfo getSubscriberInfo(FindState findState) { + if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { + SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); + if (findState.clazz == superclassInfo.getSubscriberClass()) { + return superclassInfo; + } + } + if (subscriberInfoIndexes != null) { + for (SubscriberInfoIndex index : subscriberInfoIndexes) { + SubscriberInfo info = index.getSubscriberInfo(findState.clazz); + if (info != null) { + return info; + } + } + } + return null; + } + + private List findUsingReflection(Class subscriberClass) { + FindState findState = prepareFindState(); + findState.initForSubscriber(subscriberClass); + while (findState.clazz != null) { + findUsingReflectionInSingleClass(findState); + findState.moveToSuperclass(); + } + return getMethodsAndRelease(findState); + } + + private void findUsingReflectionInSingleClass(FindState findState) { + Method[] methods; + try { + // This is faster than getMethods, especially when subscribers are fat classes like Activities + methods = findState.clazz.getDeclaredMethods(); + } catch (Throwable th) { + // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 + try { + methods = findState.clazz.getMethods(); + } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad... + String msg = "Could not inspect methods of " + findState.clazz.getName(); + if (ignoreGeneratedIndex) { + msg += ". Please consider using EventBus annotation processor to avoid reflection."; + } else { + msg += ". Please make this class visible to EventBus annotation processor to avoid reflection."; + } + throw new EventBusException(msg, error); + } + findState.skipSuperClasses = true; + } + for (Method method : methods) { + int modifiers = method.getModifiers(); + if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 1) { + Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); + if (subscribeAnnotation != null) { + Class eventType = parameterTypes[0]; + if (findState.checkAdd(method, eventType)) { + ThreadMode threadMode = subscribeAnnotation.threadMode(); + findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, + subscribeAnnotation.priority(), subscribeAnnotation.sticky())); + } + } + } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { + String methodName = method.getDeclaringClass().getName() + "." + method.getName(); + throw new EventBusException("@Subscribe method " + methodName + + "must have exactly 1 parameter but has " + parameterTypes.length); + } + } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { + String methodName = method.getDeclaringClass().getName() + "." + method.getName(); + throw new EventBusException(methodName + + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); + } + } + } + + static void clearCaches() { + METHOD_CACHE.clear(); + } + + static class FindState { + final List subscriberMethods = new ArrayList<>(); + final Map anyMethodByEventType = new HashMap<>(); + final Map subscriberClassByMethodKey = new HashMap<>(); + final StringBuilder methodKeyBuilder = new StringBuilder(128); + + Class subscriberClass; + Class clazz; + boolean skipSuperClasses; + SubscriberInfo subscriberInfo; + + void initForSubscriber(Class subscriberClass) { + this.subscriberClass = clazz = subscriberClass; + skipSuperClasses = false; + subscriberInfo = null; + } + + void recycle() { + subscriberMethods.clear(); + anyMethodByEventType.clear(); + subscriberClassByMethodKey.clear(); + methodKeyBuilder.setLength(0); + subscriberClass = null; + clazz = null; + skipSuperClasses = false; + subscriberInfo = null; + } + + boolean checkAdd(Method method, Class eventType) { + // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required. + // Usually a subscriber doesn't have methods listening to the same event type. + Object existing = anyMethodByEventType.put(eventType, method); + if (existing == null) { + return true; + } else { + if (existing instanceof Method) { + if (!checkAddWithMethodSignature((Method) existing, eventType)) { + // Paranoia check + throw new IllegalStateException(); + } + // Put any non-Method object to "consume" the existing Method + anyMethodByEventType.put(eventType, this); + } + return checkAddWithMethodSignature(method, eventType); + } + } + + private boolean checkAddWithMethodSignature(Method method, Class eventType) { + methodKeyBuilder.setLength(0); + methodKeyBuilder.append(method.getName()); + methodKeyBuilder.append('>').append(eventType.getName()); + + String methodKey = methodKeyBuilder.toString(); + Class methodClass = method.getDeclaringClass(); + Class methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); + if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { + // Only add if not already found in a sub class + return true; + } else { + // Revert the put, old class is further down the class hierarchy + subscriberClassByMethodKey.put(methodKey, methodClassOld); + return false; + } + } + + void moveToSuperclass() { + if (skipSuperClasses) { + clazz = null; + } else { + clazz = clazz.getSuperclass(); + String clazzName = clazz.getName(); + // Skip system classes, this degrades performance. + // Also we might avoid some ClassNotFoundException (see FAQ for background). + if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || + clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) { + clazz = null; + } + } + } + } + +} diff --git a/EventBus/src/de/greenrobot/event/Subscription.java b/EventBus/src/org/greenrobot/eventbus/Subscription.java similarity index 89% rename from EventBus/src/de/greenrobot/event/Subscription.java rename to EventBus/src/org/greenrobot/eventbus/Subscription.java index 6c84c440..cc0de1e3 100644 --- a/EventBus/src/de/greenrobot/event/Subscription.java +++ b/EventBus/src/org/greenrobot/eventbus/Subscription.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,22 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; final class Subscription { final Object subscriber; final SubscriberMethod subscriberMethod; - final int priority; /** * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions. */ volatile boolean active; - Subscription(Object subscriber, SubscriberMethod subscriberMethod, int priority) { + Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = subscriber; this.subscriberMethod = subscriberMethod; - this.priority = priority; active = true; } diff --git a/EventBus/src/org/greenrobot/eventbus/ThreadMode.java b/EventBus/src/org/greenrobot/eventbus/ThreadMode.java new file mode 100644 index 00000000..b1ff2427 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/ThreadMode.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +/** + * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus. + * EventBus takes care of threading independently of the posting thread. + * + * @see EventBus#register(Object) + */ +public enum ThreadMode { + /** + * This is the default. Subscriber will be called directly in the same thread, which is posting the event. Event delivery + * implies the least overhead because it avoids thread switching completely. Thus, this is the recommended mode for + * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers + * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread. + */ + POSTING, + + /** + * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is + * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event + * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread. + *

+ * If not on Android, behaves the same as {@link #POSTING}. + */ + MAIN, + + /** + * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN}, + * the event will always be queued for delivery. This ensures that the post call is non-blocking. + *

+ * If not on Android, behaves the same as {@link #POSTING}. + */ + MAIN_ORDERED, + + /** + * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods + * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single + * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to + * return quickly to avoid blocking the background thread. + *

+ * If not on Android, always uses a background thread. + */ + BACKGROUND, + + /** + * Subscriber will be called in a separate thread. This is always independent of the posting thread and the + * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should + * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number + * of long-running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus + * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications. + */ + ASYNC +} \ No newline at end of file diff --git a/EventBus/src/org/greenrobot/eventbus/android/AndroidComponents.java b/EventBus/src/org/greenrobot/eventbus/android/AndroidComponents.java new file mode 100644 index 00000000..7138b6d9 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/android/AndroidComponents.java @@ -0,0 +1,31 @@ +package org.greenrobot.eventbus.android; + +import org.greenrobot.eventbus.Logger; +import org.greenrobot.eventbus.MainThreadSupport; + +public abstract class AndroidComponents { + + private static final AndroidComponents implementation; + + static { + implementation = AndroidDependenciesDetector.isAndroidSDKAvailable() + ? AndroidDependenciesDetector.instantiateAndroidComponents() + : null; + } + + public static boolean areAvailable() { + return implementation != null; + } + + public static AndroidComponents get() { + return implementation; + } + + public final Logger logger; + public final MainThreadSupport defaultMainThreadSupport; + + public AndroidComponents(Logger logger, MainThreadSupport defaultMainThreadSupport) { + this.logger = logger; + this.defaultMainThreadSupport = defaultMainThreadSupport; + } +} diff --git a/EventBus/src/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java b/EventBus/src/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java new file mode 100644 index 00000000..1783f143 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/android/AndroidDependenciesDetector.java @@ -0,0 +1,48 @@ +package org.greenrobot.eventbus.android; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +@SuppressWarnings("TryWithIdenticalCatches") +public class AndroidDependenciesDetector { + + public static boolean isAndroidSDKAvailable() { + + try { + Class looperClass = Class.forName("android.os.Looper"); + Method getMainLooper = looperClass.getDeclaredMethod("getMainLooper"); + Object mainLooper = getMainLooper.invoke(null); + return mainLooper != null; + } + catch (ClassNotFoundException ignored) {} + catch (NoSuchMethodException ignored) {} + catch (IllegalAccessException ignored) {} + catch (InvocationTargetException ignored) {} + + return false; + } + + private static final String ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME = "org.greenrobot.eventbus.android.AndroidComponentsImpl"; + + public static boolean areAndroidComponentsAvailable() { + + try { + Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); + return true; + } + catch (ClassNotFoundException ex) { + return false; + } + } + + public static AndroidComponents instantiateAndroidComponents() { + + try { + Class impl = Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME); + return (AndroidComponents) impl.getConstructor().newInstance(); + } + catch (Throwable ex) { + return null; + } + } +} diff --git a/EventBus/src/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java b/EventBus/src/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java new file mode 100644 index 00000000..9020c24b --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.meta; + +import org.greenrobot.eventbus.EventBusException; +import org.greenrobot.eventbus.SubscriberMethod; +import org.greenrobot.eventbus.ThreadMode; + +import java.lang.reflect.Method; + +/** Base class for generated subscriber meta info classes created by annotation processing. */ +public abstract class AbstractSubscriberInfo implements SubscriberInfo { + private final Class subscriberClass; + private final Class superSubscriberInfoClass; + private final boolean shouldCheckSuperclass; + + protected AbstractSubscriberInfo(Class subscriberClass, Class superSubscriberInfoClass, + boolean shouldCheckSuperclass) { + this.subscriberClass = subscriberClass; + this.superSubscriberInfoClass = superSubscriberInfoClass; + this.shouldCheckSuperclass = shouldCheckSuperclass; + } + + @Override + public Class getSubscriberClass() { + return subscriberClass; + } + + @Override + public SubscriberInfo getSuperSubscriberInfo() { + if(superSubscriberInfoClass == null) { + return null; + } + try { + return superSubscriberInfoClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean shouldCheckSuperclass() { + return shouldCheckSuperclass; + } + + protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType) { + return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false); + } + + protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType, ThreadMode threadMode) { + return createSubscriberMethod(methodName, eventType, threadMode, 0, false); + } + + protected SubscriberMethod createSubscriberMethod(String methodName, Class eventType, ThreadMode threadMode, + int priority, boolean sticky) { + try { + Method method = subscriberClass.getDeclaredMethod(methodName, eventType); + return new SubscriberMethod(method, eventType, threadMode, priority, sticky); + } catch (NoSuchMethodException e) { + throw new EventBusException("Could not find subscriber method in " + subscriberClass + + ". Maybe a missing ProGuard rule?", e); + } + } + +} diff --git a/EventBus/src/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java b/EventBus/src/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java new file mode 100644 index 00000000..3ee4442d --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.meta; + +import org.greenrobot.eventbus.SubscriberMethod; + +/** + * Uses {@link SubscriberMethodInfo} objects to create {@link org.greenrobot.eventbus.SubscriberMethod} objects on demand. + */ +public class SimpleSubscriberInfo extends AbstractSubscriberInfo { + + private final SubscriberMethodInfo[] methodInfos; + + public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) { + super(subscriberClass, null, shouldCheckSuperclass); + this.methodInfos = methodInfos; + } + + @Override + public synchronized SubscriberMethod[] getSubscriberMethods() { + int length = methodInfos.length; + SubscriberMethod[] methods = new SubscriberMethod[length]; + for (int i = 0; i < length; i++) { + SubscriberMethodInfo info = methodInfos[i]; + methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode, + info.priority, info.sticky); + } + return methods; + } +} \ No newline at end of file diff --git a/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfo.java b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfo.java new file mode 100644 index 00000000..83c1e741 --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfo.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.meta; + +import org.greenrobot.eventbus.SubscriberMethod; + +/** Base class for generated index classes created by annotation processing. */ +public interface SubscriberInfo { + Class getSubscriberClass(); + + SubscriberMethod[] getSubscriberMethods(); + + SubscriberInfo getSuperSubscriberInfo(); + + boolean shouldCheckSuperclass(); +} diff --git a/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java new file mode 100644 index 00000000..9fc65f6f --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.meta; + +/** + * Interface for generated indexes. + */ +public interface SubscriberInfoIndex { + SubscriberInfo getSubscriberInfo(Class subscriberClass); +} diff --git a/EventBus/src/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java new file mode 100644 index 00000000..2152554c --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.meta; + +import org.greenrobot.eventbus.ThreadMode; + +public class SubscriberMethodInfo { + final String methodName; + final ThreadMode threadMode; + final Class eventType; + final int priority; + final boolean sticky; + + public SubscriberMethodInfo(String methodName, Class eventType, ThreadMode threadMode, + int priority, boolean sticky) { + this.methodName = methodName; + this.threadMode = threadMode; + this.eventType = eventType; + this.priority = priority; + this.sticky = sticky; + } + + public SubscriberMethodInfo(String methodName, Class eventType) { + this(methodName, eventType, ThreadMode.POSTING, 0, false); + } + + public SubscriberMethodInfo(String methodName, Class eventType, ThreadMode threadMode) { + this(methodName, eventType, threadMode, 0, false); + } + +} \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/util/AsyncExecutor.java b/EventBus/src/org/greenrobot/eventbus/util/AsyncExecutor.java similarity index 70% rename from EventBus/src/de/greenrobot/event/util/AsyncExecutor.java rename to EventBus/src/org/greenrobot/eventbus/util/AsyncExecutor.java index 71048a43..bd9cb365 100644 --- a/EventBus/src/de/greenrobot/event/util/AsyncExecutor.java +++ b/EventBus/src/org/greenrobot/eventbus/util/AsyncExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,21 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.util; +package org.greenrobot.eventbus.util; + +import org.greenrobot.eventbus.EventBus; import java.lang.reflect.Constructor; import java.util.concurrent.Executor; import java.util.concurrent.Executors; - -import android.app.Activity; -import android.util.Log; -import de.greenrobot.event.EventBus; +import java.util.logging.Level; /** - * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events of any - * given type (default is {@link ThrowableFailureEvent}). - * - * @author Markus + * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events. + * By default, uses {@link ThrowableFailureEvent}. + *

+ * Set a custom event type using {@link Builder#failureEventType(Class)}. + * The failure event class must have a constructor with one parameter of type {@link Throwable}. + * If using ProGuard or R8 make sure the constructor of the failure event class is kept, it is accessed via reflection. + * E.g. add a rule like + *

+ * -keepclassmembers class com.example.CustomThrowableFailureEvent {
+ *     <init>(java.lang.Throwable);
+ * }
+ * 
*/ public class AsyncExecutor { @@ -58,10 +65,6 @@ public AsyncExecutor build() { return buildForScope(null); } - public AsyncExecutor buildForActivityScope(Activity activity) { - return buildForScope(activity.getClass()); - } - public AsyncExecutor buildForScope(Object executionContext) { if (eventBus == null) { eventBus = EventBus.getDefault(); @@ -108,24 +111,21 @@ private AsyncExecutor(Executor threadPool, EventBus eventBus, Class failureEv /** Posts an failure event if the given {@link RunnableEx} throws an Exception. */ public void execute(final RunnableEx runnable) { - threadPool.execute(new Runnable() { - @Override - public void run() { + threadPool.execute(() -> { + try { + runnable.run(); + } catch (Exception e) { + Object event; try { - runnable.run(); - } catch (Exception e) { - Object event; - try { - event = failureEventConstructor.newInstance(e); - } catch (Exception e1) { - Log.e(EventBus.TAG, "Original exception:", e); - throw new RuntimeException("Could not create failure event", e1); - } - if (event instanceof HasExecutionScope) { - ((HasExecutionScope) event).setExecutionScope(scope); - } - eventBus.post(event); + event = failureEventConstructor.newInstance(e); + } catch (Exception e1) { + eventBus.getLogger().log(Level.SEVERE, "Original exception:", e); + throw new RuntimeException("Could not create failure event", e1); + } + if (event instanceof HasExecutionScope) { + ((HasExecutionScope) event).setExecutionScope(scope); } + eventBus.post(event); } }); } diff --git a/EventBus/src/de/greenrobot/event/util/ExceptionToResourceMapping.java b/EventBus/src/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java similarity index 69% rename from EventBus/src/de/greenrobot/event/util/ExceptionToResourceMapping.java rename to EventBus/src/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java index d3286175..083bd394 100644 --- a/EventBus/src/de/greenrobot/event/util/ExceptionToResourceMapping.java +++ b/EventBus/src/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java @@ -1,12 +1,28 @@ -package de.greenrobot.event.util; +/* + * Copyright (C) 2012-2020 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.util; + +import org.greenrobot.eventbus.Logger; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - -import android.util.Log; -import de.greenrobot.event.EventBus; +import java.util.logging.Level; /** @@ -19,7 +35,7 @@ public class ExceptionToResourceMapping { public final Map, Integer> throwableToMsgIdMap; public ExceptionToResourceMapping() { - throwableToMsgIdMap = new HashMap, Integer>(); + throwableToMsgIdMap = new HashMap<>(); } /** Looks at the exception and its causes trying to find an ID. */ @@ -35,7 +51,8 @@ public Integer mapThrowable(final Throwable throwable) { throwableToCheck = throwableToCheck.getCause(); depthToGo--; if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) { - Log.d(EventBus.TAG, "No specific message ressource ID found for " + throwable); + Logger logger = Logger.Default.get(); // No EventBus instance here + logger.log(Level.FINE, "No specific message resource ID found for " + throwable); // return config.defaultErrorMsgId; return null; } diff --git a/EventBus/src/org/greenrobot/eventbus/util/HasExecutionScope.java b/EventBus/src/org/greenrobot/eventbus/util/HasExecutionScope.java new file mode 100644 index 00000000..fd20a0dc --- /dev/null +++ b/EventBus/src/org/greenrobot/eventbus/util/HasExecutionScope.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.util; + +public interface HasExecutionScope { + Object getExecutionScope(); + + void setExecutionScope(Object executionScope); + +} diff --git a/EventBus/src/de/greenrobot/event/util/ThrowableFailureEvent.java b/EventBus/src/org/greenrobot/eventbus/util/ThrowableFailureEvent.java similarity index 89% rename from EventBus/src/de/greenrobot/event/util/ThrowableFailureEvent.java rename to EventBus/src/org/greenrobot/eventbus/util/ThrowableFailureEvent.java index 7c6c07fa..7707e289 100644 --- a/EventBus/src/de/greenrobot/event/util/ThrowableFailureEvent.java +++ b/EventBus/src/org/greenrobot/eventbus/util/ThrowableFailureEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.util; +package org.greenrobot.eventbus.util; /** - * A generic failure event, which can be used by apps to propagate thrown exceptions. Also used in conjunction with - * {@link ErrorDialogManager}. + * A generic failure event, which can be used by apps to propagate thrown exceptions. + * Used as default failure event by {@link AsyncExecutor}. */ public class ThrowableFailureEvent implements HasExecutionScope { protected final Throwable throwable; diff --git a/EventBusAnnotationProcessor/build.gradle b/EventBusAnnotationProcessor/build.gradle new file mode 100644 index 00000000..0ec77d93 --- /dev/null +++ b/EventBusAnnotationProcessor/build.gradle @@ -0,0 +1,62 @@ +apply plugin: 'java' + +group = rootProject.group +version = rootProject.version + +java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.targetCompatibility = JavaVersion.VERSION_1_8 + +dependencies { + implementation project(':eventbus-java') + implementation 'de.greenrobot:java-common:2.3.1' + + // Generates the required META-INF descriptor to make the processor incremental. + def incap = '0.2' + compileOnly "net.ltgt.gradle.incap:incap:$incap" + annotationProcessor "net.ltgt.gradle.incap:incap-processor:$incap" +} + +sourceSets { + main { + java { + srcDir 'src' + } + resources { + srcDir 'res' + } + } +} + +javadoc { + title = "EventBus Annotation Processor ${version} API" + options.bottom = 'Available under the Apache License, Version 2.0 - Copyright © 2015-2020 greenrobot.org. All Rights Reserved.' +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + archiveClassifier.set("javadoc") + from 'build/docs/javadoc' +} + +task sourcesJar(type: Jar) { + archiveClassifier.set("sources") + from sourceSets.main.allSource +} + +apply from: rootProject.file("gradle/publish.gradle") +// Set project-specific properties +afterEvaluate { + publishing.publications { + mavenJava(MavenPublication) { + artifactId = "eventbus-annotation-processor" + + from components.java + artifact javadocJar + artifact sourcesJar + pom { + name = "EventBus Annotation Processor" + description = "Precompiler for EventBus Annotations." + packaging = "jar" + } + } + } +} diff --git a/EventBusAnnotationProcessor/res/META-INF/services/javax.annotation.processing.Processor b/EventBusAnnotationProcessor/res/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..e6e7aa55 --- /dev/null +++ b/EventBusAnnotationProcessor/res/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +org.greenrobot.eventbus.annotationprocessor.EventBusAnnotationProcessor diff --git a/EventBusAnnotationProcessor/src/org/greenrobot/eventbus/annotationprocessor/EventBusAnnotationProcessor.java b/EventBusAnnotationProcessor/src/org/greenrobot/eventbus/annotationprocessor/EventBusAnnotationProcessor.java new file mode 100644 index 00000000..058bc36e --- /dev/null +++ b/EventBusAnnotationProcessor/src/org/greenrobot/eventbus/annotationprocessor/EventBusAnnotationProcessor.java @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.annotationprocessor; + +import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; + +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Messager; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; + +import de.greenrobot.common.ListMap; + + +import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING; + +/** + * Is an aggregating processor as it writes a single file, the subscriber index file, + * based on found elements with the @Subscriber annotation. + */ +@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe") +@SupportedOptions(value = {"eventBusIndex", "verbose"}) +@IncrementalAnnotationProcessor(AGGREGATING) +public class EventBusAnnotationProcessor extends AbstractProcessor { + public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex"; + public static final String OPTION_VERBOSE = "verbose"; + + /** Found subscriber methods for a class (without superclasses). */ + private final ListMap methodsByClass = new ListMap<>(); + private final Set classesToSkip = new HashSet<>(); + + private boolean writerRoundDone; + private int round; + private boolean verbose; + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment env) { + Messager messager = processingEnv.getMessager(); + try { + String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX); + if (index == null) { + messager.printMessage(Diagnostic.Kind.ERROR, "No option " + OPTION_EVENT_BUS_INDEX + + " passed to annotation processor"); + return false; + } + verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE)); + int lastPeriod = index.lastIndexOf('.'); + String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null; + + round++; + if (verbose) { + messager.printMessage(Diagnostic.Kind.NOTE, "Processing round " + round + ", new annotations: " + + !annotations.isEmpty() + ", processingOver: " + env.processingOver()); + } + if (env.processingOver()) { + if (!annotations.isEmpty()) { + messager.printMessage(Diagnostic.Kind.ERROR, + "Unexpected processing state: annotations still available after processing over"); + return false; + } + } + if (annotations.isEmpty()) { + return false; + } + + if (writerRoundDone) { + messager.printMessage(Diagnostic.Kind.ERROR, + "Unexpected processing state: annotations still available after writing."); + } + collectSubscribers(annotations, env, messager); + checkForSubscribersToSkip(messager, indexPackage); + + if (!methodsByClass.isEmpty()) { + createInfoIndexFile(index); + } else { + messager.printMessage(Diagnostic.Kind.WARNING, "No @Subscribe annotations found"); + } + writerRoundDone = true; + } catch (RuntimeException e) { + // IntelliJ does not handle exceptions nicely, so log and print a message + e.printStackTrace(); + messager.printMessage(Diagnostic.Kind.ERROR, "Unexpected error in EventBusAnnotationProcessor: " + e); + } + return true; + } + + private void collectSubscribers(Set annotations, RoundEnvironment env, Messager messager) { + for (TypeElement annotation : annotations) { + Set elements = env.getElementsAnnotatedWith(annotation); + for (Element element : elements) { + if (element instanceof ExecutableElement) { + ExecutableElement method = (ExecutableElement) element; + if (checkHasNoErrors(method, messager)) { + TypeElement classElement = (TypeElement) method.getEnclosingElement(); + methodsByClass.putElement(classElement, method); + } + } else { + messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element); + } + } + } + } + + private boolean checkHasNoErrors(ExecutableElement element, Messager messager) { + if (element.getModifiers().contains(Modifier.STATIC)) { + messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must not be static", element); + return false; + } + + if (!element.getModifiers().contains(Modifier.PUBLIC)) { + messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must be public", element); + return false; + } + + List parameters = ((ExecutableElement) element).getParameters(); + if (parameters.size() != 1) { + messager.printMessage(Diagnostic.Kind.ERROR, "Subscriber method must have exactly 1 parameter", element); + return false; + } + return true; + } + + /** + * Subscriber classes should be skipped if their class or any involved event class are not visible to the index. + */ + private void checkForSubscribersToSkip(Messager messager, String myPackage) { + for (TypeElement skipCandidate : methodsByClass.keySet()) { + TypeElement subscriberClass = skipCandidate; + while (subscriberClass != null) { + if (!isVisible(myPackage, subscriberClass)) { + boolean added = classesToSkip.add(skipCandidate); + if (added) { + String msg; + if (subscriberClass.equals(skipCandidate)) { + msg = "Falling back to reflection because class is not public"; + } else { + msg = "Falling back to reflection because " + skipCandidate + + " has a non-public super class"; + } + messager.printMessage(Diagnostic.Kind.NOTE, msg, subscriberClass); + } + break; + } + List methods = methodsByClass.get(subscriberClass); + if (methods != null) { + for (ExecutableElement method : methods) { + String skipReason = null; + VariableElement param = method.getParameters().get(0); + TypeMirror typeMirror = getParamTypeMirror(param, messager); + if (!(typeMirror instanceof DeclaredType) || + !(((DeclaredType) typeMirror).asElement() instanceof TypeElement)) { + skipReason = "event type cannot be processed"; + } + if (skipReason == null) { + TypeElement eventTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement(); + if (!isVisible(myPackage, eventTypeElement)) { + skipReason = "event type is not public"; + } + } + if (skipReason != null) { + boolean added = classesToSkip.add(skipCandidate); + if (added) { + String msg = "Falling back to reflection because " + skipReason; + if (!subscriberClass.equals(skipCandidate)) { + msg += " (found in super class for " + skipCandidate + ")"; + } + messager.printMessage(Diagnostic.Kind.NOTE, msg, param); + } + break; + } + } + } + subscriberClass = getSuperclass(subscriberClass); + } + } + } + + private TypeMirror getParamTypeMirror(VariableElement param, Messager messager) { + TypeMirror typeMirror = param.asType(); + // Check for generic type + if (typeMirror instanceof TypeVariable) { + TypeMirror upperBound = ((TypeVariable) typeMirror).getUpperBound(); + if (upperBound instanceof DeclaredType) { + if (messager != null) { + messager.printMessage(Diagnostic.Kind.NOTE, "Using upper bound type " + upperBound + + " for generic parameter", param); + } + typeMirror = upperBound; + } + } + return typeMirror; + } + + private TypeElement getSuperclass(TypeElement type) { + if (type.getSuperclass().getKind() == TypeKind.DECLARED) { + TypeElement superclass = (TypeElement) processingEnv.getTypeUtils().asElement(type.getSuperclass()); + String name = superclass.getQualifiedName().toString(); + if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { + // Skip system classes, this just degrades performance + return null; + } else { + return superclass; + } + } else { + return null; + } + } + + private String getClassString(TypeElement typeElement, String myPackage) { + PackageElement packageElement = getPackageElement(typeElement); + String packageString = packageElement.getQualifiedName().toString(); + String className = typeElement.getQualifiedName().toString(); + if (packageString != null && !packageString.isEmpty()) { + if (packageString.equals(myPackage)) { + className = cutPackage(myPackage, className); + } else if (packageString.equals("java.lang")) { + className = typeElement.getSimpleName().toString(); + } + } + return className; + } + + private String cutPackage(String paket, String className) { + if (className.startsWith(paket + '.')) { + // Don't use TypeElement.getSimpleName, it doesn't work for us with inner classes + return className.substring(paket.length() + 1); + } else { + // Paranoia + throw new IllegalStateException("Mismatching " + paket + " vs. " + className); + } + } + + private PackageElement getPackageElement(TypeElement subscriberClass) { + Element candidate = subscriberClass.getEnclosingElement(); + while (!(candidate instanceof PackageElement)) { + candidate = candidate.getEnclosingElement(); + } + return (PackageElement) candidate; + } + + private void writeCreateSubscriberMethods(BufferedWriter writer, List methods, + String callPrefix, String myPackage) throws IOException { + for (ExecutableElement method : methods) { + List parameters = method.getParameters(); + TypeMirror paramType = getParamTypeMirror(parameters.get(0), null); + TypeElement paramElement = (TypeElement) processingEnv.getTypeUtils().asElement(paramType); + String methodName = method.getSimpleName().toString(); + String eventClass = getClassString(paramElement, myPackage) + ".class"; + + Subscribe subscribe = method.getAnnotation(Subscribe.class); + List parts = new ArrayList<>(); + parts.add(callPrefix + "(\"" + methodName + "\","); + String lineEnd = "),"; + if (subscribe.priority() == 0 && !subscribe.sticky()) { + if (subscribe.threadMode() == ThreadMode.POSTING) { + parts.add(eventClass + lineEnd); + } else { + parts.add(eventClass + ","); + parts.add("ThreadMode." + subscribe.threadMode().name() + lineEnd); + } + } else { + parts.add(eventClass + ","); + parts.add("ThreadMode." + subscribe.threadMode().name() + ","); + parts.add(subscribe.priority() + ","); + parts.add(subscribe.sticky() + lineEnd); + } + writeLine(writer, 3, parts.toArray(new String[parts.size()])); + + if (verbose) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Indexed @Subscribe at " + + method.getEnclosingElement().getSimpleName() + "." + methodName + + "(" + paramElement.getSimpleName() + ")"); + } + + } + } + + private void createInfoIndexFile(String index) { + BufferedWriter writer = null; + try { + JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index); + int period = index.lastIndexOf('.'); + String myPackage = period > 0 ? index.substring(0, period) : null; + String clazz = index.substring(period + 1); + writer = new BufferedWriter(sourceFile.openWriter()); + if (myPackage != null) { + writer.write("package " + myPackage + ";\n\n"); + } + writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n"); + writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n"); + writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n"); + writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n"); + writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n"); + writer.write("import java.util.HashMap;\n"); + writer.write("import java.util.Map;\n\n"); + writer.write("/** This class is generated by EventBus, do not edit. */\n"); + writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n"); + writer.write(" private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;\n\n"); + writer.write(" static {\n"); + writer.write(" SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();\n\n"); + writeIndexLines(writer, myPackage); + writer.write(" }\n\n"); + writer.write(" private static void putIndex(SubscriberInfo info) {\n"); + writer.write(" SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n"); + writer.write(" }\n\n"); + writer.write(" @Override\n"); + writer.write(" public SubscriberInfo getSubscriberInfo(Class subscriberClass) {\n"); + writer.write(" SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n"); + writer.write(" if (info != null) {\n"); + writer.write(" return info;\n"); + writer.write(" } else {\n"); + writer.write(" return null;\n"); + writer.write(" }\n"); + writer.write(" }\n"); + writer.write("}\n"); + } catch (IOException e) { + throw new RuntimeException("Could not write source for " + index, e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + //Silent + } + } + } + } + + private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException { + for (TypeElement subscriberTypeElement : methodsByClass.keySet()) { + if (classesToSkip.contains(subscriberTypeElement)) { + continue; + } + + String subscriberClass = getClassString(subscriberTypeElement, myPackage); + if (isVisible(myPackage, subscriberTypeElement)) { + writeLine(writer, 2, + "putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,", + "true,", "new SubscriberMethodInfo[] {"); + List methods = methodsByClass.get(subscriberTypeElement); + writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage); + writer.write(" }));\n\n"); + } else { + writer.write(" // Subscriber not visible to index: " + subscriberClass + "\n"); + } + } + } + + private boolean isVisible(String myPackage, TypeElement typeElement) { + Set modifiers = typeElement.getModifiers(); + boolean visible; + if (modifiers.contains(Modifier.PUBLIC)) { + visible = true; + } else if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.PROTECTED)) { + visible = false; + } else { + String subscriberPackage = getPackageElement(typeElement).getQualifiedName().toString(); + if (myPackage == null) { + visible = subscriberPackage.length() == 0; + } else { + visible = myPackage.equals(subscriberPackage); + } + } + return visible; + } + + private void writeLine(BufferedWriter writer, int indentLevel, String... parts) throws IOException { + writeLine(writer, indentLevel, 2, parts); + } + + private void writeLine(BufferedWriter writer, int indentLevel, int indentLevelIncrease, String... parts) + throws IOException { + writeIndent(writer, indentLevel); + int len = indentLevel * 4; + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + if (i != 0) { + if (len + part.length() > 118) { + writer.write("\n"); + if (indentLevel < 12) { + indentLevel += indentLevelIncrease; + } + writeIndent(writer, indentLevel); + len = indentLevel * 4; + } else { + writer.write(" "); + } + } + writer.write(part); + len += part.length(); + } + writer.write("\n"); + } + + private void writeIndent(BufferedWriter writer, int indentLevel) throws IOException { + for (int i = 0; i < indentLevel; i++) { + writer.write(" "); + } + } +} diff --git a/EventBusPerformance/.classpath b/EventBusPerformance/.classpath deleted file mode 100644 index 7bc01d9a..00000000 --- a/EventBusPerformance/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/EventBusPerformance/.project b/EventBusPerformance/.project deleted file mode 100644 index b29334b1..00000000 --- a/EventBusPerformance/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - EventBusPerformance - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/EventBusPerformance/.settings/org.eclipse.jdt.core.prefs b/EventBusPerformance/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index f77b31c2..00000000 --- a/EventBusPerformance/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.source=1.5 diff --git a/EventBusPerformance/AndroidManifest.xml b/EventBusPerformance/AndroidManifest.xml index 4967dc56..530cafeb 100644 --- a/EventBusPerformance/AndroidManifest.xml +++ b/EventBusPerformance/AndroidManifest.xml @@ -1,12 +1,6 @@ - - + package="org.greenrobot.eventbusperf"> - diff --git a/EventBusPerformance/build.gradle b/EventBusPerformance/build.gradle index cd15599a..37a32249 100644 --- a/EventBusPerformance/build.gradle +++ b/EventBusPerformance/build.gradle @@ -1,27 +1,25 @@ buildscript { repositories { + google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.13.3' + // Note: IntelliJ IDEA 2021.1 only supports up to version 4.1 + classpath 'com.android.tools.build:gradle:4.1.3' } } apply plugin: 'com.android.application' -repositories { - mavenCentral() -} - dependencies { - compile project(':EventBus') - compile 'com.squareup:otto:1.3.5' + implementation project(':eventbus-android') + annotationProcessor project(':eventbus-annotation-processor') + implementation 'com.squareup:otto:1.3.8' } android { - buildToolsVersion '20.0.0' - compileSdkVersion 19 + compileSdkVersion _compileSdkVersion sourceSets { main { @@ -29,7 +27,22 @@ android { java.srcDirs = ['src'] res.srcDirs = ['res'] } + } + defaultConfig { + minSdkVersion 7 + targetSdkVersion 26 + versionCode 1 + versionName "2.0.0" + javaCompileOptions { + annotationProcessorOptions { + arguments = [eventBusIndex: 'org.greenrobot.eventbusperf.MyEventBusIndex'] + } + } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } diff --git a/EventBusPerformance/res/layout/activity_setuptests.xml b/EventBusPerformance/res/layout/activity_setuptests.xml index da77afe6..27064758 100644 --- a/EventBusPerformance/res/layout/activity_setuptests.xml +++ b/EventBusPerformance/res/layout/activity_setuptests.xml @@ -51,6 +51,14 @@ android:layout_marginLeft="48dp" android:text="@string/test_eventBusEventHierarchy" /> + + - Event Performance + EventBus Performance EventBus Event Inheritance + Ignore generated index OttoBus Broadcast Local Broadcast @@ -18,10 +19,11 @@ Register Subscribers, 1. time - PostThread - MainThread - BackgroundThread - Async + POSTING + MAIN + MAIN_ORDERED + BACKGROUND + ASYNC Test Is \nRunning! diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestEvent.java b/EventBusPerformance/src/de/greenrobot/eventperf/TestEvent.java deleted file mode 100644 index b90be773..00000000 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestEvent.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.greenrobot.eventperf; - -/** Used by otto and EventBus */ -public class TestEvent { - -} diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestFinishedEvent.java b/EventBusPerformance/src/de/greenrobot/eventperf/TestFinishedEvent.java deleted file mode 100644 index 1e301c9b..00000000 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestFinishedEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.greenrobot.eventperf; - -public class TestFinishedEvent { - - public final Test test; - public final boolean isLastEvent; - - public TestFinishedEvent(Test test, boolean isLastEvent) { - this.test = test; - this.isLastEvent = isLastEvent; - } -} diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/Test.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/Test.java similarity index 64% rename from EventBusPerformance/src/de/greenrobot/eventperf/Test.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/Test.java index 8c52d9aa..ee2d405d 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/Test.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/Test.java @@ -1,13 +1,29 @@ -package de.greenrobot.eventperf; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import java.util.concurrent.atomic.AtomicLong; +package org.greenrobot.eventbusperf; import android.content.Context; +import java.util.concurrent.atomic.AtomicLong; + public abstract class Test { protected final Context context; protected final TestParams params; - protected AtomicLong eventsReceivedCount = new AtomicLong(); + public final AtomicLong eventsReceivedCount = new AtomicLong(); protected long primaryResultMicros; protected int primaryResultCount; protected String otherTestResults; diff --git a/EventBusPerformance/src/org/greenrobot/eventbusperf/TestEvent.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestEvent.java new file mode 100644 index 00000000..8db0db9f --- /dev/null +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestEvent.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; + +/** Used by otto and EventBus */ +public class TestEvent { + +} diff --git a/EventBusPerformance/src/org/greenrobot/eventbusperf/TestFinishedEvent.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestFinishedEvent.java new file mode 100644 index 00000000..9e1a5594 --- /dev/null +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestFinishedEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; + +public class TestFinishedEvent { + + public final Test test; + public final boolean isLastEvent; + + public TestFinishedEvent(Test test, boolean isLastEvent) { + this.test = test; + this.isLastEvent = isLastEvent; + } +} diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestParams.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestParams.java similarity index 63% rename from EventBusPerformance/src/de/greenrobot/eventperf/TestParams.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/TestParams.java index 1cebfec2..85ee4f13 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestParams.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestParams.java @@ -1,10 +1,26 @@ -package de.greenrobot.eventperf; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; + +import org.greenrobot.eventbus.ThreadMode; import java.io.Serializable; import java.util.ArrayList; -import de.greenrobot.event.ThreadMode; - public class TestParams implements Serializable { private static final long serialVersionUID = -2739435088947740809L; @@ -13,6 +29,7 @@ public class TestParams implements Serializable { private int publisherCount; private ThreadMode threadMode; private boolean eventInheritance; + private boolean ignoreGeneratedIndex; private int testNumber; private ArrayList> testClasses; @@ -56,6 +73,14 @@ public void setEventInheritance(boolean eventInheritance) { this.eventInheritance = eventInheritance; } + public boolean isIgnoreGeneratedIndex() { + return ignoreGeneratedIndex; + } + + public void setIgnoreGeneratedIndex(boolean ignoreGeneratedIndex) { + this.ignoreGeneratedIndex = ignoreGeneratedIndex; + } + public ArrayList> getTestClasses() { return testClasses; } diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestRunner.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunner.java similarity index 72% rename from EventBusPerformance/src/de/greenrobot/eventperf/TestRunner.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunner.java index e593c8e3..4c0f941f 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestRunner.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunner.java @@ -1,12 +1,29 @@ -package de.greenrobot.eventperf; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; + +import android.content.Context; + +import org.greenrobot.eventbus.EventBus; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; -import android.content.Context; -import de.greenrobot.event.EventBus; - /** * This thread initialize all selected tests and runs them through. Also the thread skips the tests, when it is canceled */ diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestRunnerActivity.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunnerActivity.java similarity index 74% rename from EventBusPerformance/src/de/greenrobot/eventperf/TestRunnerActivity.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunnerActivity.java index 127d528b..b21efabf 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestRunnerActivity.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunnerActivity.java @@ -1,4 +1,20 @@ -package de.greenrobot.eventperf; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; import android.app.Activity; import android.os.Bundle; @@ -6,7 +22,10 @@ import android.text.Html; import android.view.View; import android.widget.TextView; -import de.greenrobot.event.EventBus; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * This activity gets the information from the activity before, sets up the test and starts the test. After it watchs @@ -23,7 +42,7 @@ public class TestRunnerActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_runtests); - textViewResult = (TextView) findViewById(R.id.textViewResult); + textViewResult = findViewById(R.id.textViewResult); controlBus = new EventBus(); controlBus.register(this); } @@ -43,6 +62,7 @@ protected void onResume() { } } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(TestFinishedEvent event) { Test test = event.test; String text = "" + test.getDisplayName() + "
" + // diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/TestSetupActivity.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestSetupActivity.java similarity index 65% rename from EventBusPerformance/src/de/greenrobot/eventperf/TestSetupActivity.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/TestSetupActivity.java index 2b018b41..626f8a0b 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/TestSetupActivity.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/TestSetupActivity.java @@ -1,4 +1,20 @@ -package de.greenrobot.eventperf; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf; import android.app.Activity; import android.content.Intent; @@ -8,12 +24,14 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.Spinner; -import de.greenrobot.event.ThreadMode; -import de.greenrobot.eventperf.testsubject.PerfTestEventBus; -import de.greenrobot.eventperf.testsubject.PerfTestOtto; + +import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; +import org.greenrobot.eventbusperf.testsubject.PerfTestEventBus; +import org.greenrobot.eventbusperf.testsubject.PerfTestOtto; + public class TestSetupActivity extends Activity { @SuppressWarnings("rawtypes") @@ -32,7 +50,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_setuptests); - Spinner spinnerRun = (Spinner) findViewById(R.id.spinnerTestToRun); + Spinner spinnerRun = findViewById(R.id.spinnerTestToRun); spinnerRun.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView adapter, View v, int pos, long lng) { @@ -47,28 +65,29 @@ public void onNothingSelected(AdapterView arg0) { } public void checkEventBus(View v) { - Spinner spinnerThread = (Spinner) findViewById(R.id.spinnerThread); - CheckBox checkBoxEventBus = (CheckBox) findViewById(R.id.checkBoxEventBus); + Spinner spinnerThread = findViewById(R.id.spinnerThread); + CheckBox checkBoxEventBus = findViewById(R.id.checkBoxEventBus); int visibility = checkBoxEventBus.isChecked() ? View.VISIBLE : View.GONE; spinnerThread.setVisibility(visibility); } public void startClick(View v) { TestParams params = new TestParams(); - Spinner spinnerThread = (Spinner) findViewById(R.id.spinnerThread); + Spinner spinnerThread = findViewById(R.id.spinnerThread); String threadModeStr = spinnerThread.getSelectedItem().toString(); ThreadMode threadMode = ThreadMode.valueOf(threadModeStr); params.setThreadMode(threadMode); params.setEventInheritance(((CheckBox) findViewById(R.id.checkBoxEventBusEventHierarchy)).isChecked()); + params.setIgnoreGeneratedIndex(((CheckBox) findViewById(R.id.checkBoxEventBusIgnoreGeneratedIndex)).isChecked()); - EditText editTextEvent = (EditText) findViewById(R.id.editTextEvent); + EditText editTextEvent = findViewById(R.id.editTextEvent); params.setEventCount(Integer.parseInt(editTextEvent.getText().toString())); - EditText editTextSubscriber = (EditText) findViewById(R.id.editTextSubscribe); + EditText editTextSubscriber = findViewById(R.id.editTextSubscribe); params.setSubscriberCount(Integer.parseInt(editTextSubscriber.getText().toString())); - Spinner spinnerTestToRun = (Spinner) findViewById(R.id.spinnerTestToRun); + Spinner spinnerTestToRun = findViewById(R.id.spinnerTestToRun); int testPos = spinnerTestToRun.getSelectedItemPosition(); params.setTestNumber(testPos + 1); ArrayList> testClasses = initTestClasses(testPos); @@ -84,10 +103,10 @@ public void startClick(View v) { private ArrayList> initTestClasses(int testPos) { ArrayList> testClasses = new ArrayList>(); // the attributes are putted in the intent (eventbus, otto, broadcast, local broadcast) - final CheckBox checkBoxEventBus = (CheckBox) findViewById(R.id.checkBoxEventBus); - final CheckBox checkBoxOtto = (CheckBox) findViewById(R.id.checkBoxOtto); - final CheckBox checkBoxBroadcast = (CheckBox) findViewById(R.id.checkBoxBroadcast); - final CheckBox checkBoxLocalBroadcast = (CheckBox) findViewById(R.id.checkBoxLocalBroadcast); + final CheckBox checkBoxEventBus = findViewById(R.id.checkBoxEventBus); + final CheckBox checkBoxOtto = findViewById(R.id.checkBoxOtto); + final CheckBox checkBoxBroadcast = findViewById(R.id.checkBoxBroadcast); + final CheckBox checkBoxLocalBroadcast = findViewById(R.id.checkBoxLocalBroadcast); if (checkBoxEventBus.isChecked()) { testClasses.add(TEST_CLASSES_EVENTBUS[testPos]); } diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestEventBus.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestEventBus.java similarity index 75% rename from EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestEventBus.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestEventBus.java index 16920dc3..f0a5e2f8 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestEventBus.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestEventBus.java @@ -1,14 +1,35 @@ -package de.greenrobot.eventperf.testsubject; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf.testsubject; + +import android.content.Context; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; -import android.content.Context; -import de.greenrobot.event.EventBus; -import de.greenrobot.eventperf.Test; -import de.greenrobot.eventperf.TestEvent; -import de.greenrobot.eventperf.TestParams; +import org.greenrobot.eventbusperf.MyEventBusIndex; +import org.greenrobot.eventbusperf.Test; +import org.greenrobot.eventbusperf.TestEvent; +import org.greenrobot.eventbusperf.TestParams; public abstract class PerfTestEventBus extends Test { @@ -20,7 +41,8 @@ public abstract class PerfTestEventBus extends Test { public PerfTestEventBus(Context context, TestParams params) { super(context, params); - eventBus = EventBus.builder().eventInheritance(params.isEventInheritance()).build(); + eventBus = EventBus.builder().eventInheritance(params.isEventInheritance()).addIndex(new MyEventBusIndex()) + .ignoreGeneratedIndex(params.isIgnoreGeneratedIndex()).build(); subscribers = new ArrayList(); eventCount = params.getEventCount(); expectedEventCount = eventCount * params.getSubscriberCount(); @@ -42,19 +64,28 @@ public void prepareTest() { private Class getSubscriberClassForThreadMode() { switch (params.getThreadMode()) { - case MainThread: + case MAIN: return SubscribeClassEventBusMain.class; - case BackgroundThread: + case MAIN_ORDERED: + return SubscribeClassEventBusMainOrdered.class; + case BACKGROUND: return SubscribeClassEventBusBackground.class; - case Async: + case ASYNC: return SubscriberClassEventBusAsync.class; - case PostThread: + case POSTING: return SubscribeClassEventBusDefault.class; default: throw new RuntimeException("Unknown: " + params.getThreadMode()); } } + private static String getDisplayModifier(TestParams params) { + String inheritance = params.isEventInheritance() ? "" : ", no event inheritance"; + String ignoreIndex = params.isIgnoreGeneratedIndex() ? ", ignore index" : ""; + return inheritance + ignoreIndex; + } + + public static class Post extends PerfTestEventBus { public Post(Context context, TestParams params) { super(context, params); @@ -89,9 +120,9 @@ public void runTest() { @Override public String getDisplayName() { - String inheritance = params.isEventInheritance() ? ", event inheritance" : ", no event inheritance"; - return "EventBus Post Events, " + params.getThreadMode() + inheritance; + return "EventBus Post Events, " + params.getThreadMode() + getDisplayModifier(params); } + } public static class RegisterAll extends PerfTestEventBus { @@ -108,7 +139,7 @@ public void runTest() { @Override public String getDisplayName() { - return "EventBus Register, no unregister"; + return "EventBus Register, no unregister" + getDisplayModifier(params); } } @@ -135,7 +166,10 @@ public void runTest() { } long beforeRegister = System.nanoTime(); super.eventBus.register(subscriber); - long timeRegister = System.nanoTime() - beforeRegister; + long afterRegister = System.nanoTime(); + long end = System.nanoTime(); + long timeMeasureOverhead = (end - afterRegister) * 2; + long timeRegister = end - beforeRegister - timeMeasureOverhead; time += timeRegister; super.eventBus.unregister(subscriber); if (canceled) { @@ -149,7 +183,7 @@ public void runTest() { @Override public String getDisplayName() { - return "EventBus Register"; + return "EventBus Register" + getDisplayModifier(params); } } @@ -158,7 +192,7 @@ public static class RegisterFirstTime extends RegisterOneByOne { public RegisterFirstTime(Context context, TestParams params) { super(context, params); try { - Class clazz = Class.forName("de.greenrobot.event.SubscriberMethodFinder"); + Class clazz = Class.forName("org.greenrobot.eventbus.SubscriberMethodFinder"); clearCachesMethod = clazz.getDeclaredMethod("clearCaches"); clearCachesMethod.setAccessible(true); } catch (Exception e) { @@ -168,13 +202,14 @@ public RegisterFirstTime(Context context, TestParams params) { @Override public String getDisplayName() { - return "EventBus Register, first time"; + return "EventBus Register, first time"+ getDisplayModifier(params); } } - public class SubscribeClassEventBusDefault { - public void onEvent(TestEvent event) { + public class SubscribeClassEventBusMain { + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(TestEvent event) { eventsReceivedCount.incrementAndGet(); } @@ -194,8 +229,9 @@ public void dummy5() { } } - public class SubscribeClassEventBusMain { - public void onEventMainThread(TestEvent event) { + public class SubscribeClassEventBusMainOrdered { + @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) + public void onEvent(TestEvent event) { eventsReceivedCount.incrementAndGet(); } @@ -216,6 +252,7 @@ public void dummy5() { } public class SubscribeClassEventBusBackground { + @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onEventBackgroundThread(TestEvent event) { eventsReceivedCount.incrementAndGet(); } @@ -237,6 +274,7 @@ public void dummy5() { } public class SubscriberClassEventBusAsync { + @Subscribe(threadMode = ThreadMode.ASYNC) public void onEventAsync(TestEvent event) { eventsReceivedCount.incrementAndGet(); } diff --git a/EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestOtto.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestOtto.java similarity index 83% rename from EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestOtto.java rename to EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestOtto.java index 8e68adde..646256ed 100644 --- a/EventBusPerformance/src/de/greenrobot/eventperf/testsubject/PerfTestOtto.java +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/PerfTestOtto.java @@ -1,9 +1,20 @@ -package de.greenrobot.eventperf.testsubject; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf.testsubject; import android.app.Activity; import android.content.Context; @@ -13,9 +24,14 @@ import com.squareup.otto.Subscribe; import com.squareup.otto.ThreadEnforcer; -import de.greenrobot.eventperf.Test; -import de.greenrobot.eventperf.TestEvent; -import de.greenrobot.eventperf.TestParams; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +import org.greenrobot.eventbusperf.Test; +import org.greenrobot.eventbusperf.TestEvent; +import org.greenrobot.eventbusperf.TestParams; public abstract class PerfTestOtto extends Test { @@ -117,14 +133,18 @@ public void runTest() { for (Object subscriber : super.subscribers) { if (cacheField != null) { try { - cacheField.set(null, new HashMap()); + cacheField.set(null, new ConcurrentHashMap()); } catch (Exception e) { throw new RuntimeException(e); } } long beforeRegister = System.nanoTime(); super.eventBus.register(subscriber); - long timeRegister = System.nanoTime() - beforeRegister; + + long afterRegister = System.nanoTime(); + long end = System.nanoTime(); + long timeMeasureOverhead = (end - afterRegister) * 2; + long timeRegister = end - beforeRegister - timeMeasureOverhead; time += timeRegister; super.eventBus.unregister(subscriber); if (canceled) { diff --git a/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/SubscribeClassEventBusDefault.java b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/SubscribeClassEventBusDefault.java new file mode 100644 index 00000000..54a11423 --- /dev/null +++ b/EventBusPerformance/src/org/greenrobot/eventbusperf/testsubject/SubscribeClassEventBusDefault.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbusperf.testsubject; + +import org.greenrobot.eventbus.Subscribe; + +import org.greenrobot.eventbusperf.TestEvent; + +public class SubscribeClassEventBusDefault { + private PerfTestEventBus perfTestEventBus; + + public SubscribeClassEventBusDefault(PerfTestEventBus perfTestEventBus) { + this.perfTestEventBus = perfTestEventBus; + } + + @Subscribe + public void onEvent(TestEvent event) { + perfTestEventBus.eventsReceivedCount.incrementAndGet(); + } + + public void dummy() { + } + + public void dummy2() { + } + + public void dummy3() { + } + + public void dummy4() { + } + + public void dummy5() { + } +} diff --git a/EventBusTest/.classpath b/EventBusTest/.classpath deleted file mode 100644 index 2b16fe8e..00000000 --- a/EventBusTest/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/EventBusTest/.project b/EventBusTest/.project deleted file mode 100644 index 49b077d3..00000000 --- a/EventBusTest/.project +++ /dev/null @@ -1,34 +0,0 @@ - - - EventBusTest - - - greenBus - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/EventBusTest/AndroidManifest.xml b/EventBusTest/AndroidManifest.xml index 9695813a..657ad878 100644 --- a/EventBusTest/AndroidManifest.xml +++ b/EventBusTest/AndroidManifest.xml @@ -1,20 +1,14 @@ + xmlns:tools="http://schemas.android.com/tools" + package="org.greenrobot.eventbus"> - - - - + android:label="EventBus Test" + tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon"> \ No newline at end of file diff --git a/EventBusTest/build.gradle b/EventBusTest/build.gradle index 169fa718..2ca34312 100644 --- a/EventBusTest/build.gradle +++ b/EventBusTest/build.gradle @@ -1,26 +1,35 @@ buildscript { repositories { + google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.13.3' + // Note: IntelliJ IDEA 2021.1 only supports up to version 4.1 + classpath 'com.android.tools.build:gradle:4.1.3' } } apply plugin: 'com.android.application' -repositories { - mavenCentral() -} - dependencies { - androidTestCompile project(':EventBus') + androidTestImplementation project(':eventbus-android') + androidTestImplementation project(':EventBusTestJava') + androidTestAnnotationProcessor project(':eventbus-annotation-processor') + // Trying to repro bug: +// androidTestAnnotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.0' + implementation fileTree(dir: 'libs', include: '*.jar') + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test:rules:1.0.2' } android { - buildToolsVersion '20.0.0' - compileSdkVersion 19 + compileSdkVersion _compileSdkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 + } sourceSets { main { @@ -33,7 +42,28 @@ android { } defaultConfig { + minSdkVersion 9 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testApplicationId "de.greenrobot.event.test" - testInstrumentationRunner "android.test.InstrumentationTestRunner" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + javaCompileOptions { + annotationProcessorOptions { + arguments = [ eventBusIndex : 'org.greenrobot.eventbus.EventBusTestsIndex' ] + } + } + } + + useLibrary 'android.test.base' + + lintOptions { + // To see problems right away, also nice for Travis CI + textOutput 'stdout' + + // TODO FIXME: Travis only error + abortOnError false } } diff --git a/EventBusTest/libs/EventBusTestSubscriberInJar-3.0.0.jar b/EventBusTest/libs/EventBusTestSubscriberInJar-3.0.0.jar new file mode 100644 index 00000000..47b80ef0 Binary files /dev/null and b/EventBusTest/libs/EventBusTestSubscriberInJar-3.0.0.jar differ diff --git a/EventBusTest/project.properties b/EventBusTest/project.properties deleted file mode 100644 index 4655e969..00000000 --- a/EventBusTest/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-17 -android.library.reference.1=../EventBus diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadTest.java b/EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadTest.java deleted file mode 100644 index 41b6302b..00000000 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.greenrobot.event.test; - -import java.util.ArrayList; -import java.util.List; - -import android.os.Looper; - -/** - * @author Markus Junginger, greenrobot - */ -public class EventBusMainThreadTest extends AbstractEventBusTest { - - private BackgroundPoster backgroundPoster; - - protected void setUp() throws Exception { - super.setUp(); - backgroundPoster = new BackgroundPoster(); - backgroundPoster.start(); - } - - @Override - protected void tearDown() throws Exception { - backgroundPoster.shutdown(); - backgroundPoster.join(); - super.tearDown(); - } - - public void testPost() throws InterruptedException { - eventBus.register(this); - eventBus.post("Hello"); - waitForEventCount(1, 1000); - - assertEquals("Hello", lastEvent); - assertEquals(Looper.getMainLooper().getThread(), lastThread); - } - - public void testPostInBackgroundThread() throws InterruptedException { - eventBus.register(this); - backgroundPoster.post("Hello"); - waitForEventCount(1, 1000); - assertEquals("Hello", lastEvent); - assertEquals(Looper.getMainLooper().getThread(), lastThread); - } - - public void onEventMainThread(String event) { - trackEvent(event); - } - - class BackgroundPoster extends Thread { - volatile boolean running = true; - private final List eventQ = new ArrayList(); - private final List eventsDone = new ArrayList(); - - public BackgroundPoster() { - super("BackgroundPoster"); - } - - @Override - public void run() { - while (running) { - Object event = pollEvent(); - if (event != null) { - eventBus.post(event); - synchronized (eventsDone) { - eventsDone.add(event); - eventsDone.notifyAll(); - } - } - } - } - - private synchronized Object pollEvent() { - Object event = null; - synchronized (eventQ) { - if (eventQ.isEmpty()) { - try { - eventQ.wait(1000); - } catch (InterruptedException e) { - } - } - if(!eventQ.isEmpty()) { - event = eventQ.remove(0); - } - } - return event; - } - - void shutdown() { - running = false; - synchronized (eventQ) { - eventQ.notifyAll(); - } - } - - void post(Object event) { - synchronized (eventQ) { - eventQ.add(event); - eventQ.notifyAll(); - } - synchronized (eventsDone) { - while (eventsDone.remove(event)) { - try { - eventsDone.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - } - - } - -} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusOrderedSubscriptionsTest.java b/EventBusTest/src/de/greenrobot/event/test/EventBusOrderedSubscriptionsTest.java deleted file mode 100644 index 0cb794ac..00000000 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusOrderedSubscriptionsTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2013 Markus Junginger, greenrobot (http://greenrobot.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.greenrobot.event.test; - -import java.util.ArrayList; -import java.util.List; - -import de.greenrobot.event.EventBus; -import android.util.Log; - -/** - * @author Markus Junginger, greenrobot - */ -public class EventBusOrderedSubscriptionsTest extends AbstractEventBusTest { - - int lastPrio = Integer.MAX_VALUE; - final List registered = new ArrayList(); - private String fail; - - public void testOrdered() { - runTestOrdered("42", false); - } - - public void testOrderedMainThread() { - runTestOrdered(new IntTestEvent(42), false); - } - - public void testOrderedBackgroundThread() { - runTestOrdered(Integer.valueOf(42), false); - } - - public void testOrderedSticky() { - runTestOrdered("42", true); - } - - public void testOrderedMainThreadSticky() { - runTestOrdered(new IntTestEvent(42), true); - } - - public void testOrderedBackgroundThreadSticky() { - runTestOrdered(Integer.valueOf(42), true); - } - - protected void runTestOrdered(Object event, boolean sticky) { - register(1, sticky); - register(-1, sticky); - register(10, sticky); - register(0, sticky); - register(-100, sticky); - assertEquals(5, registered.size()); - - eventBus.post(event); - - waitForEventCount(5, 10000); - assertEquals(null, fail); - - unregisterAll(); - } - - private void unregisterAll() { - for (PrioSubscriber subscriber : registered) { - eventBus.unregister(subscriber); - } - } - - protected PrioSubscriber register(int priority, boolean sticky) { - PrioSubscriber subscriber = new PrioSubscriber(priority); - if (sticky) { - eventBus.registerSticky(subscriber, priority); - } else { - eventBus.register(subscriber, priority); - } - registered.add(subscriber); - return subscriber; - } - - private final class PrioSubscriber { - - final int prio; - - public PrioSubscriber(int prio) { - this.prio = prio; - // TODO Auto-generated constructor stub - } - - public void onEvent(String event) { - handleEvent(event); - } - - public void onEventMainThread(IntTestEvent event) { - handleEvent(event); - } - - public void onEventBackgroundThread(Integer event) { - handleEvent(event); - } - - protected void handleEvent(Object event) { - if (prio > lastPrio) { - fail = "Called prio " + prio + " after " + lastPrio; - } - lastPrio = prio; - - Log.d(EventBus.TAG, "Subscriber " + prio + " got: " + event); - trackEvent(event); - } - - } - -} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberLegalTest.java b/EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberLegalTest.java deleted file mode 100644 index ee435219..00000000 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberLegalTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 Markus Junginger, greenrobot (http://greenrobot.de) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.greenrobot.event.test; - -import de.greenrobot.event.EventBusException; - -/** - * @author Markus Junginger, greenrobot - */ -public class EventBusSubscriberLegalTest extends AbstractEventBusTest { - - public void testSubscriberLegal() { - eventBus.register(this); - eventBus.post("42"); - eventBus.unregister(this); - assertEquals(1, eventCount.intValue()); - } - - public void testSubscriberNotPublic() { - try { - eventBus.register(new NotPublic()); - fail("Registration of ilegal subscriber successful"); - } catch (EventBusException e) { - // Expected - } - } - - public void testSubscriberStatic() { - try { - eventBus.register(new Static()); - fail("Registration of ilegal subscriber successful"); - } catch (EventBusException e) { - // Expected - } - } - - public void testSubscriberLegalAbstract() { - eventBus.register(new Abstract() { - - @Override - public void onEvent(String event) { - trackEvent(event); - } - - }); - - eventBus.post("42"); - assertEquals(1, eventCount.intValue()); - } - - public void onEvent(String event) { - trackEvent(event); - } - - static class NotPublic { - void onEvent(String event) { - } - } - - static abstract class Abstract { - public abstract void onEvent(String event); - } - - static class Static { - public static void onEvent(String event) { - } - } - -} diff --git a/EventBusTest/src/org/greenrobot/eventbus/AbstractAndroidEventBusTest.java b/EventBusTest/src/org/greenrobot/eventbus/AbstractAndroidEventBusTest.java new file mode 100644 index 00000000..13178206 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/AbstractAndroidEventBusTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import android.annotation.SuppressLint; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.runner.RunWith; + + +import static org.junit.Assert.assertFalse; + +/** + * @author Markus Junginger, greenrobot + */ +@RunWith(AndroidJUnit4.class) +public abstract class AbstractAndroidEventBusTest extends AbstractEventBusTest { + private EventPostHandler mainPoster; + + public AbstractAndroidEventBusTest() { + this(false); + } + + public AbstractAndroidEventBusTest(boolean collectEventsReceived) { + super(collectEventsReceived); + } + + @Before + public void setUpAndroid() throws Exception { + mainPoster = new EventPostHandler(Looper.getMainLooper()); + assertFalse(Looper.getMainLooper().getThread().equals(Thread.currentThread())); + } + + protected void postInMainThread(Object event) { + mainPoster.post(event); + } + + @SuppressLint("HandlerLeak") + class EventPostHandler extends Handler { + public EventPostHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + eventBus.post(msg.obj); + } + + void post(Object event) { + sendMessage(obtainMessage(0, event)); + } + + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/AndroidComponentsAvailabilityTest.java b/EventBusTest/src/org/greenrobot/eventbus/AndroidComponentsAvailabilityTest.java new file mode 100644 index 00000000..15ae3d80 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/AndroidComponentsAvailabilityTest.java @@ -0,0 +1,16 @@ +package org.greenrobot.eventbus; + +import org.greenrobot.eventbus.android.AndroidComponents; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class AndroidComponentsAvailabilityTest { + + @Test + public void shouldBeAvailable() { + assertTrue(AndroidComponents.areAvailable()); + assertNotNull(AndroidComponents.get()); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/ClassMapPerfTest.java b/EventBusTest/src/org/greenrobot/eventbus/ClassMapPerfTest.java new file mode 100644 index 00000000..6c1e9b51 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/ClassMapPerfTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * Just to verify testHashMapClassObject is fastest. Ignore this test. + */ +public class ClassMapPerfTest /* extends TestCase */ { + + static final int COUNT = 10000000; + static final Class CLAZZ = ClassMapPerfTest.class; + + public void testHashMapClassObject() { + Map map = new HashMap(); + for (int i = 0; i < COUNT; i++) { + Class oldValue = map.put(CLAZZ, CLAZZ); + Class value = map.get(CLAZZ); + } + } + + public void testIdentityHashMapClassObject() { + Map map = new IdentityHashMap(); + for (int i = 0; i < COUNT; i++) { + Class oldValue = map.put(CLAZZ, CLAZZ); + Class value = map.get(CLAZZ); + } + } + + public void testHashMapClassName() { + Map map = new HashMap(); + for (int i = 0; i < COUNT; i++) { + Class oldValue = map.put(CLAZZ.getName(), CLAZZ); + Class value = map.get(CLAZZ.getName()); + } + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidActivityTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidActivityTest.java new file mode 100644 index 00000000..364a3936 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidActivityTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import android.app.Activity; +import android.support.test.annotation.UiThreadTest; +import android.support.test.rule.UiThreadTestRule; +import android.util.Log; + +import org.junit.Rule; +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; + +/** + * @author Markus Junginger, greenrobot + */ +// Do not extend from AbstractAndroidEventBusTest, because it asserts test may not be in main thread +public class EventBusAndroidActivityTest extends AbstractEventBusTest { + + public static class WithIndex extends EventBusBasicTest { + @Test + public void dummy() { + } + + } + + @Rule + public final UiThreadTestRule uiThreadTestRule = new UiThreadTestRule(); + + @Test + @UiThreadTest + public void testRegisterAndPost() { + // Use an activity to test real life performance + TestActivity testActivity = new TestActivity(); + String event = "Hello"; + + long start = System.currentTimeMillis(); + eventBus.register(testActivity); + long time = System.currentTimeMillis() - start; + Log.d(EventBus.TAG, "Registered in " + time + "ms"); + + eventBus.post(event); + + assertEquals(event, testActivity.lastStringEvent); + } + + public static class TestActivity extends Activity { + public String lastStringEvent; + + @Subscribe + public void onEvent(String event) { + lastStringEvent = event; + } + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidCancelEventDeliveryTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidCancelEventDeliveryTest.java new file mode 100644 index 00000000..781ceda2 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidCancelEventDeliveryTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.support.test.runner.AndroidJUnit4; +import android.test.UiThreadTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +public class EventBusAndroidCancelEventDeliveryTest extends EventBusCancelEventDeliveryTest { + + @UiThreadTest + @Test + public void testCancelInMainThread() { + SubscriberMainThread subscriber = new SubscriberMainThread(); + eventBus.register(subscriber); + eventBus.post("42"); + awaitLatch(subscriber.done, 10); + assertEquals(0, eventCount.intValue()); + assertNotNull(failed); + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidMultithreadedTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidMultithreadedTest.java new file mode 100644 index 00000000..8be31cc1 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidMultithreadedTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.os.Looper; +import android.support.test.runner.AndroidJUnit4; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +@RunWith(AndroidJUnit4.class) +public class EventBusAndroidMultithreadedTest extends EventBusMultithreadedTest { + + @Test + public void testSubscribeUnSubscribeAndPostMixedEventType() throws InterruptedException { + List threads = new ArrayList(); + + // Debug.startMethodTracing("testSubscribeUnSubscribeAndPostMixedEventType"); + for (int i = 0; i < 5; i++) { + SubscribeUnsubscribeThread thread = new SubscribeUnsubscribeThread(); + thread.start(); + threads.add(thread); + } + // This test takes a bit longer, so just use fraction the regular count + runThreadsMixedEventType(COUNT / 4, 5); + for (SubscribeUnsubscribeThread thread : threads) { + thread.shutdown(); + } + for (SubscribeUnsubscribeThread thread : threads) { + thread.join(); + } + // Debug.stopMethodTracing(); + } + + public class SubscribeUnsubscribeThread extends Thread { + boolean running = true; + + public void shutdown() { + running = false; + } + + @Override + public void run() { + try { + while (running) { + eventBus.register(this); + double random = Math.random(); + if (random > 0.6d) { + Thread.sleep(0, (int) (1000000 * Math.random())); + } else if (random > 0.3d) { + Thread.yield(); + } + eventBus.unregister(this); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(String event) { + assertSame(Looper.getMainLooper(), Looper.myLooper()); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onEventBackgroundThread(Integer event) { + assertNotSame(Looper.getMainLooper(), Looper.myLooper()); + } + + @Subscribe + public void onEvent(Object event) { + assertNotSame(Looper.getMainLooper(), Looper.myLooper()); + } + + @Subscribe(threadMode = ThreadMode.ASYNC) + public void onEventAsync(Object event) { + assertNotSame(Looper.getMainLooper(), Looper.myLooper()); + } + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidOrderTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidOrderTest.java new file mode 100644 index 00000000..ff348e1d --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidOrderTest.java @@ -0,0 +1,91 @@ +package org.greenrobot.eventbus; + +import android.os.Handler; +import android.os.Looper; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; + +public class EventBusAndroidOrderTest extends AbstractAndroidEventBusTest { + + private TestBackgroundPoster backgroundPoster; + private Handler handler; + + @Before + public void setUp() throws Exception { + handler = new Handler(Looper.getMainLooper()); + backgroundPoster = new TestBackgroundPoster(eventBus); + backgroundPoster.start(); + } + + @After + public void tearDown() throws Exception { + backgroundPoster.shutdown(); + backgroundPoster.join(); + } + + @Test + public void backgroundAndMainUnordered() { + eventBus.register(this); + + handler.post(new Runnable() { + @Override + public void run() { + // post from non-main thread + backgroundPoster.post("non-main"); + // post from main thread + eventBus.post("main"); + } + }); + + waitForEventCount(2, 1000); + + // observe that event from *main* thread is posted FIRST + // NOT in posting order + assertEquals("non-main", lastEvent); + } + + @Test + public void backgroundAndMainOrdered() { + eventBus.register(this); + + handler.post(new Runnable() { + @Override + public void run() { + // post from non-main thread + backgroundPoster.post(new OrderedEvent("non-main")); + // post from main thread + eventBus.post(new OrderedEvent("main")); + } + }); + + waitForEventCount(2, 1000); + + // observe that event from *main* thread is posted LAST + // IN posting order + assertEquals("main", ((OrderedEvent) lastEvent).thread); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEvent(String event) { + trackEvent(event); + } + + @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) + public void onEvent(OrderedEvent event) { + trackEvent(event); + } + + static class OrderedEvent { + String thread; + + OrderedEvent(String thread) { + this.thread = thread; + } + } + +} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusBackgroundThreadTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusBackgroundThreadTest.java similarity index 78% rename from EventBusTest/src/de/greenrobot/event/test/EventBusBackgroundThreadTest.java rename to EventBusTest/src/org/greenrobot/eventbus/EventBusBackgroundThreadTest.java index 888b37d5..6ccb6025 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusBackgroundThreadTest.java +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusBackgroundThreadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; import android.os.Looper; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + /** * @author Markus Junginger, greenrobot */ -public class EventBusBackgroundThreadTest extends AbstractEventBusTest { +public class EventBusBackgroundThreadTest extends AbstractAndroidEventBusTest { + @Test public void testPostInCurrentThread() throws InterruptedException { eventBus.register(this); eventBus.post("Hello"); @@ -31,6 +37,7 @@ public void testPostInCurrentThread() throws InterruptedException { assertEquals(Thread.currentThread(), lastThread); } + @Test public void testPostFromMain() throws InterruptedException { eventBus.register(this); postInMainThread("Hello"); @@ -40,6 +47,7 @@ public void testPostFromMain() throws InterruptedException { assertFalse(lastThread.equals(Looper.getMainLooper().getThread())); } + @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onEventBackgroundThread(String event) { trackEvent(event); } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadRacingTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadRacingTest.java similarity index 92% rename from EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadRacingTest.java rename to EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadRacingTest.java index 0b3ac3a8..8ed637e8 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusMainThreadRacingTest.java +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadRacingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; - -import java.util.Random; -import java.util.concurrent.CountDownLatch; +package org.greenrobot.eventbus; import android.os.Handler; import android.os.Looper; +import org.junit.Test; + +import java.util.Random; +import java.util.concurrent.CountDownLatch; + /** * @author Markus Junginger, greenrobot */ -public class EventBusMainThreadRacingTest extends AbstractEventBusTest { +public class EventBusMainThreadRacingTest extends AbstractAndroidEventBusTest { private static final int ITERATIONS = LONG_TESTS ? 100000 : 1000; @@ -32,6 +34,7 @@ public class EventBusMainThreadRacingTest extends AbstractEventBusTest { private CountDownLatch startLatch; private volatile RuntimeException failed; + @Test public void testRacingThreads() throws InterruptedException { Runnable register = new Runnable() { @Override @@ -84,6 +87,7 @@ public void run() { awaitLatch(doneLatch, 10); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(String event) { trackEvent(event); if (unregistered) { diff --git a/EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadTest.java new file mode 100644 index 00000000..34c29ee6 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusMainThreadTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import android.os.Looper; + +import org.junit.Test; + + +import static org.junit.Assert.assertEquals; + +/** + * @author Markus Junginger, greenrobot + */ +public class EventBusMainThreadTest extends AbstractAndroidEventBusTest { + + @Test + public void testPost() throws InterruptedException { + eventBus.register(this); + eventBus.post("Hello"); + waitForEventCount(1, 1000); + + assertEquals("Hello", lastEvent); + assertEquals(Looper.getMainLooper().getThread(), lastThread); + } + + @Test + public void testPostInBackgroundThread() throws InterruptedException { + TestBackgroundPoster backgroundPoster = new TestBackgroundPoster(eventBus); + backgroundPoster.start(); + + eventBus.register(this); + backgroundPoster.post("Hello"); + waitForEventCount(1, 1000); + assertEquals("Hello", lastEvent); + assertEquals(Looper.getMainLooper().getThread(), lastThread); + + backgroundPoster.shutdown(); + backgroundPoster.join(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(String event) { + trackEvent(event); + } + +} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusMethodModifiersTest.java b/EventBusTest/src/org/greenrobot/eventbus/EventBusMethodModifiersTest.java similarity index 59% rename from EventBusTest/src/de/greenrobot/event/test/EventBusMethodModifiersTest.java rename to EventBusTest/src/org/greenrobot/eventbus/EventBusMethodModifiersTest.java index 6d664b9c..2818d85d 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusMethodModifiersTest.java +++ b/EventBusTest/src/org/greenrobot/eventbus/EventBusMethodModifiersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; import android.os.Looper; -import de.greenrobot.event.EventBus; -import de.greenrobot.event.EventBusException; + +import org.junit.Test; + +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; /** * @author Markus Junginger, greenrobot */ -public class EventBusMethodModifiersTest extends AbstractEventBusTest { +public class EventBusMethodModifiersTest extends AbstractAndroidEventBusTest { + @Test public void testRegisterForEventTypeAndPost() throws InterruptedException { eventBus.register(this); String event = "Hello"; @@ -31,47 +35,28 @@ public void testRegisterForEventTypeAndPost() throws InterruptedException { waitForEventCount(4, 1000); } - public void testIllegalMethodNameThrow() { - try { - eventBus.register(new IllegalEventMethodName()); - fail("Illegal name registered"); - } catch (EventBusException ex) { - // OK, expected - } - } - - public void testIllegalMethodNameSkip() { - eventBus=EventBus.builder().skipMethodVerificationFor(IllegalEventMethodName.class).build(); - eventBus.register(new IllegalEventMethodName()); - eventBus.post(new Object()); - } - + @Subscribe public void onEvent(String event) { trackEvent(event); assertNotSame(Looper.getMainLooper(), Looper.myLooper()); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(String event) { trackEvent(event); assertSame(Looper.getMainLooper(), Looper.myLooper()); } + @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onEventBackgroundThread(String event) { trackEvent(event); assertNotSame(Looper.getMainLooper(), Looper.myLooper()); } + @Subscribe(threadMode = ThreadMode.ASYNC) public void onEventAsync(String event) { trackEvent(event); assertNotSame(Looper.getMainLooper(), Looper.myLooper()); } - public static class IllegalEventMethodName { - public void onEventIllegalName(Object event) { - fail("onEventIllegalName got called"); - } - - public void onEvent(IntTestEvent event) { - } - } } diff --git a/EventBusTest/src/org/greenrobot/eventbus/TestBackgroundPoster.java b/EventBusTest/src/org/greenrobot/eventbus/TestBackgroundPoster.java new file mode 100644 index 00000000..a40eefd9 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/TestBackgroundPoster.java @@ -0,0 +1,70 @@ +package org.greenrobot.eventbus; + +import java.util.ArrayList; +import java.util.List; + +public class TestBackgroundPoster extends Thread { + private final EventBus eventBus; + volatile boolean running = true; + private final List eventQ = new ArrayList<>(); + private final List eventsDone = new ArrayList<>(); + + TestBackgroundPoster(EventBus eventBus) { + super("BackgroundPoster"); + this.eventBus = eventBus; + } + + @Override + public void run() { + while (running) { + Object event = pollEvent(); + if (event != null) { + eventBus.post(event); + synchronized (eventsDone) { + eventsDone.add(event); + eventsDone.notifyAll(); + } + } + } + } + + private synchronized Object pollEvent() { + Object event = null; + synchronized (eventQ) { + if (eventQ.isEmpty()) { + try { + eventQ.wait(1000); + } catch (InterruptedException ignored) { + } + } + if(!eventQ.isEmpty()) { + event = eventQ.remove(0); + } + } + return event; + } + + void shutdown() { + running = false; + synchronized (eventQ) { + eventQ.notifyAll(); + } + } + + void post(Object event) { + synchronized (eventQ) { + eventQ.add(event); + eventQ.notifyAll(); + } + synchronized (eventsDone) { + while (!eventsDone.remove(event)) { + try { + eventsDone.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusAndroidOrderTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusAndroidOrderTestWithIndex.java new file mode 100644 index 00000000..9e1f2318 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusAndroidOrderTestWithIndex.java @@ -0,0 +1,13 @@ +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusAndroidOrderTest; + +public class EventBusAndroidOrderTestWithIndex extends EventBusAndroidOrderTest { + + @Override + public void setUp() throws Exception { + eventBus = Indexed.build(); + super.setUp(); + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBackgroundThreadTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBackgroundThreadTestWithIndex.java new file mode 100644 index 00000000..b1f316a1 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBackgroundThreadTestWithIndex.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusBackgroundThreadTest; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class EventBusBackgroundThreadTestWithIndex extends EventBusBackgroundThreadTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } + + @Test + public void testIndex() { + assertTrue(eventBus.toString().contains("indexCount=2")); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBasicTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBasicTestWithIndex.java new file mode 100644 index 00000000..662a70b8 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusBasicTestWithIndex.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusBasicTest; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class EventBusBasicTestWithIndex extends EventBusBasicTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } + + @Test + public void testIndex() { + assertTrue(eventBus.toString().contains("indexCount=2")); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusCancelEventDeliveryTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusCancelEventDeliveryTestWithIndex.java new file mode 100644 index 00000000..76418689 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusCancelEventDeliveryTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusCancelEventDeliveryTest; +import org.junit.Before; + +public class EventBusCancelEventDeliveryTestWithIndex extends EventBusCancelEventDeliveryTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusFallbackToReflectionTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusFallbackToReflectionTestWithIndex.java new file mode 100644 index 00000000..d6a2df27 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusFallbackToReflectionTestWithIndex.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusFallbackToReflectionTest; + +public class EventBusFallbackToReflectionTestWithIndex extends EventBusFallbackToReflectionTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } + +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusGenericsTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusGenericsTestWithIndex.java new file mode 100644 index 00000000..ca74fdfa --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusGenericsTestWithIndex.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusGenericsTest; + +public class EventBusGenericsTestWithIndex extends EventBusGenericsTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceDisabledTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceDisabledTestWithIndex.java new file mode 100644 index 00000000..2443e9e1 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceDisabledTestWithIndex.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.EventBusTestsIndex; +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusInheritanceDisabledTest; + +public class EventBusInheritanceDisabledTestWithIndex extends EventBusInheritanceDisabledTest { + @Before + public void setUp() throws Exception { + eventBus = EventBus.builder().eventInheritance(false).addIndex(new EventBusTestsIndex()).build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceTestWithIndex.java new file mode 100644 index 00000000..151195a0 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusInheritanceTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusInheritanceTest; +import org.junit.Before; + +public class EventBusInheritanceTestWithIndex extends EventBusInheritanceTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadRacingTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadRacingTestWithIndex.java new file mode 100644 index 00000000..b73c2d85 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadRacingTestWithIndex.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusMainThreadRacingTest; + +public class EventBusMainThreadRacingTestWithIndex extends EventBusMainThreadRacingTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadTestWithIndex.java new file mode 100644 index 00000000..630d1d72 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMainThreadTestWithIndex.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusMainThreadTest; + +public class EventBusMainThreadTestWithIndex extends EventBusMainThreadTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMethodModifiersTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMethodModifiersTestWithIndex.java new file mode 100644 index 00000000..6034354f --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMethodModifiersTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusMethodModifiersTest; +import org.junit.Before; + +public class EventBusMethodModifiersTestWithIndex extends EventBusMethodModifiersTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMultithreadedTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMultithreadedTestWithIndex.java new file mode 100644 index 00000000..e56a8020 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusMultithreadedTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusMultithreadedTest; +import org.junit.Before; + +public class EventBusMultithreadedTestWithIndex extends EventBusMultithreadedTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusNoSubscriberEventTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusNoSubscriberEventTestWithIndex.java new file mode 100644 index 00000000..20b0d08e --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusNoSubscriberEventTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusNoSubscriberEventTest; +import org.junit.Before; + +public class EventBusNoSubscriberEventTestWithIndex extends EventBusNoSubscriberEventTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusOrderedSubscriptionsTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusOrderedSubscriptionsTestWithIndex.java new file mode 100644 index 00000000..840e8bad --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusOrderedSubscriptionsTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusOrderedSubscriptionsTest; +import org.junit.Before; + +public class EventBusOrderedSubscriptionsTestWithIndex extends EventBusOrderedSubscriptionsTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusRegistrationRacingTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusRegistrationRacingTestWithIndex.java new file mode 100644 index 00000000..55c43bfa --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusRegistrationRacingTestWithIndex.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusRegistrationRacingTest; + +public class EventBusRegistrationRacingTestWithIndex extends EventBusRegistrationRacingTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusStickyEventTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusStickyEventTestWithIndex.java new file mode 100644 index 00000000..4e5c63d5 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusStickyEventTestWithIndex.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBusStickyEventTest; +import org.junit.Before; + +public class EventBusStickyEventTestWithIndex extends EventBusStickyEventTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberExceptionTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberExceptionTestWithIndex.java new file mode 100644 index 00000000..2b9e3a17 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberExceptionTestWithIndex.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.junit.Before; + +import org.greenrobot.eventbus.EventBusSubscriberExceptionTest; + +public class EventBusSubscriberExceptionTestWithIndex extends EventBusSubscriberExceptionTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = Indexed.build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberInJarTestWithIndex.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberInJarTestWithIndex.java new file mode 100644 index 00000000..9df9c7dd --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/EventBusSubscriberInJarTestWithIndex.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.EventBusSubscriberInJarTest; +import org.greenrobot.eventbus.InJarIndex; +import org.junit.Before; + +public class EventBusSubscriberInJarTestWithIndex extends EventBusSubscriberInJarTest { + @Before + public void overwriteEventBus() throws Exception { + eventBus = EventBus.builder().addIndex(new InJarIndex()).build(); + } +} diff --git a/EventBusTest/src/org/greenrobot/eventbus/indexed/Indexed.java b/EventBusTest/src/org/greenrobot/eventbus/indexed/Indexed.java new file mode 100644 index 00000000..4aec1ef6 --- /dev/null +++ b/EventBusTest/src/org/greenrobot/eventbus/indexed/Indexed.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus.indexed; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.EventBusJavaTestsIndex; +import org.greenrobot.eventbus.EventBusTestsIndex; + +public class Indexed { + static EventBus build() { + return EventBus.builder() + .addIndex(new EventBusTestsIndex()) + .addIndex(new EventBusJavaTestsIndex()) + .build(); + } +} diff --git a/EventBusTestJava/build.gradle b/EventBusTestJava/build.gradle new file mode 100644 index 00000000..9e2bdad1 --- /dev/null +++ b/EventBusTestJava/build.gradle @@ -0,0 +1,25 @@ +apply plugin: "java-library" + +java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.targetCompatibility = JavaVersion.VERSION_1_8 + +// we have tests in the main source set so they can be shared with the Android test module +// to make Gradle pick them up, add the dir to the test source set +sourceSets { + test { + java { + srcDirs += ["src/main/java"] + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: "*.jar") + implementation(project(":eventbus-java")) + annotationProcessor project(":eventbus-annotation-processor") + implementation "junit:junit:4.13.2" +} + +tasks.withType(JavaCompile) { + options.compilerArgs += [ "-AeventBusIndex=org.greenrobot.eventbus.EventBusJavaTestsIndex" ] +} diff --git a/EventBusTestJava/libs/EventBusTestSubscriberInJar-3.0.0.jar b/EventBusTestJava/libs/EventBusTestSubscriberInJar-3.0.0.jar new file mode 100644 index 00000000..47b80ef0 Binary files /dev/null and b/EventBusTestJava/libs/EventBusTestSubscriberInJar-3.0.0.jar differ diff --git a/EventBusTest/src/de/greenrobot/event/test/AbstractEventBusTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/AbstractEventBusTest.java similarity index 74% rename from EventBusTest/src/de/greenrobot/event/test/AbstractEventBusTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/AbstractEventBusTest.java index 2d997620..fbbcf265 100644 --- a/EventBusTest/src/de/greenrobot/event/test/AbstractEventBusTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/AbstractEventBusTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,26 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; + +import org.junit.Before; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + -import junit.framework.TestCase; -import android.annotation.SuppressLint; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import de.greenrobot.event.EventBus; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Markus Junginger, greenrobot */ -public class AbstractEventBusTest extends TestCase { - /** Activates long(er) running tests e.g. testing multi-threading more throughly. */ +public abstract class AbstractEventBusTest { + /** Activates long(er) running tests e.g. testing multi-threading more thoroughly. */ protected static final boolean LONG_TESTS = false; protected EventBus eventBus; @@ -43,8 +44,6 @@ public class AbstractEventBusTest extends TestCase { protected volatile Object lastEvent; protected volatile Thread lastThread; - private EventPostHandler mainPoster; - public AbstractEventBusTest() { this(false); } @@ -57,16 +56,10 @@ public AbstractEventBusTest(boolean collectEventsReceived) { } } - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUpBase() throws Exception { EventBus.clearCaches(); eventBus = new EventBus(); - mainPoster = new EventPostHandler(Looper.getMainLooper()); - assertFalse(Looper.getMainLooper().getThread().equals(Thread.currentThread())); - } - - protected void postInMainThread(Object event) { - mainPoster.post(event); } protected void waitForEventCount(int expectedCount, int maxMillis) { @@ -98,23 +91,6 @@ protected void trackEvent(Object event) { eventCount.incrementAndGet(); } - @SuppressLint("HandlerLeak") - class EventPostHandler extends Handler { - public EventPostHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - eventBus.post(msg.obj); - } - - void post(Object event) { - sendMessage(obtainMessage(0, event)); - } - - } - protected void assertEventCount(int expectedEventCount) { assertEquals(expectedEventCount, eventCount.intValue()); } @@ -132,4 +108,12 @@ protected void awaitLatch(CountDownLatch latch, long seconds) { } } + protected void log(String msg) { + eventBus.getLogger().log(Level.FINE, msg); + } + + protected void log(String msg, Throwable e) { + eventBus.getLogger().log(Level.FINE, msg, e); + } + } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusBasicTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBasicTest.java similarity index 70% rename from EventBusTest/src/de/greenrobot/event/test/EventBusBasicTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBasicTest.java index c34feb38..c00b04bd 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusBasicTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBasicTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,79 +13,81 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; -import android.app.Activity; -import android.util.Log; -import de.greenrobot.event.EventBus; -import junit.framework.TestCase; +import org.junit.Test; -import java.lang.ref.WeakReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * @author Markus Junginger, greenrobot */ -public class EventBusBasicTest extends TestCase { +@SuppressWarnings({"WeakerAccess", "UnusedParameters", "unused"}) +public class EventBusBasicTest extends AbstractEventBusTest { + + public static class WithIndex extends EventBusBasicTest { + @Test + public void dummy() { + } + + } - private EventBus eventBus; private String lastStringEvent; private int countStringEvent; private int countIntEvent; private int lastIntEvent; private int countMyEventExtended; private int countMyEvent; + private int countMyEvent2; - protected void setUp() throws Exception { - super.setUp(); - eventBus = new EventBus(); - } - + @Test public void testRegisterAndPost() { // Use an activity to test real life performance - TestActivity testActivity = new TestActivity(); + StringEventSubscriber stringEventSubscriber = new StringEventSubscriber(); String event = "Hello"; long start = System.currentTimeMillis(); - eventBus.register(testActivity); + eventBus.register(stringEventSubscriber); long time = System.currentTimeMillis() - start; - Log.d(EventBus.TAG, "Registered in " + time + "ms"); + log("Registered in " + time + "ms"); eventBus.post(event); - assertEquals(event, testActivity.lastStringEvent); + assertEquals(event, stringEventSubscriber.lastStringEvent); } + @Test public void testPostWithoutSubscriber() { eventBus.post("Hello"); } + @Test public void testUnregisterWithoutRegister() { // Results in a warning without throwing eventBus.unregister(this); } + // This will throw "out of memory" if subscribers are leaked + @Test public void testUnregisterNotLeaking() { - EventBusBasicTest subscriber = new EventBusBasicTest(); - eventBus.register(subscriber); - eventBus.unregister(subscriber); - - WeakReference ref = new WeakReference(subscriber); - subscriber = null; - assertSubscriberNotReferenced(ref); - } - - private void assertSubscriberNotReferenced(WeakReference ref) { - EventBusBasicTest subscriberTest = new EventBusBasicTest(); - WeakReference refTest = new WeakReference(subscriberTest); - subscriberTest = null; - - // Yeah, in theory is is questionable (in practice just fine so far...) - System.gc(); - - assertNull(refTest.get()); - assertNull(ref.get()); + int heapMBytes = (int) (Runtime.getRuntime().maxMemory() / (1024L * 1024L)); + for (int i = 0; i < heapMBytes * 2; i++) { + @SuppressWarnings("unused") + EventBusBasicTest subscriber = new EventBusBasicTest() { + byte[] expensiveObject = new byte[1024 * 1024]; + }; + eventBus.register(subscriber); + eventBus.unregister(subscriber); + log("Iteration " + i + " / max heap: " + heapMBytes); + } } + @Test public void testRegisterTwice() { eventBus.register(this); try { @@ -96,6 +98,7 @@ public void testRegisterTwice() { } } + @Test public void testIsRegistered() { assertFalse(eventBus.isRegistered(this)); eventBus.register(this); @@ -104,6 +107,7 @@ public void testIsRegistered() { assertFalse(eventBus.isRegistered(this)); } + @Test public void testPostWithTwoSubscriber() { EventBusBasicTest test2 = new EventBusBasicTest(); eventBus.register(this); @@ -114,6 +118,7 @@ public void testPostWithTwoSubscriber() { assertEquals(event, test2.lastStringEvent); } + @Test public void testPostMultipleTimes() { eventBus.register(this); MyEvent event = new MyEvent(); @@ -125,10 +130,20 @@ public void testPostMultipleTimes() { } // Debug.stopMethodTracing(); long time = System.currentTimeMillis() - start; - Log.d(EventBus.TAG, "Posted " + count + " events in " + time + "ms"); + log("Posted " + count + " events in " + time + "ms"); assertEquals(count, countMyEvent); } + @Test + public void testMultipleSubscribeMethodsForEvent() { + eventBus.register(this); + MyEvent event = new MyEvent(); + eventBus.post(event); + assertEquals(1, countMyEvent); + assertEquals(1, countMyEvent2); + } + + @Test public void testPostAfterUnregister() { eventBus.register(this); eventBus.unregister(this); @@ -136,6 +151,7 @@ public void testPostAfterUnregister() { assertNull(lastStringEvent); } + @Test public void testRegisterAndPostTwoTypes() { eventBus.register(this); eventBus.post(42); @@ -146,6 +162,7 @@ public void testRegisterAndPostTwoTypes() { assertEquals("Hello", lastStringEvent); } + @Test public void testRegisterUnregisterAndPostTwoTypes() { eventBus.register(this); eventBus.unregister(this); @@ -156,12 +173,14 @@ public void testRegisterUnregisterAndPostTwoTypes() { assertEquals(0, countStringEvent); } + @Test public void testPostOnDifferentEventBus() { eventBus.register(this); new EventBus().post("Hello"); assertEquals(0, countStringEvent); } + @Test public void testPostInEventHandler() { RepostInteger reposter = new RepostInteger(); eventBus.register(reposter); @@ -173,6 +192,7 @@ public void testPostInEventHandler() { assertEquals(10, reposter.lastEvent); } + @Test public void testHasSubscriberForEvent() { assertFalse(eventBus.hasSubscriberForEvent(String.class)); @@ -183,13 +203,11 @@ public void testHasSubscriberForEvent() { assertFalse(eventBus.hasSubscriberForEvent(String.class)); } + @Test public void testHasSubscriberForEventSuperclass() { assertFalse(eventBus.hasSubscriberForEvent(String.class)); - Object subscriber = new Object() { - public void onEvent(Object event) { - } - }; + Object subscriber = new ObjectSubscriber(); eventBus.register(subscriber); assertTrue(eventBus.hasSubscriberForEvent(String.class)); @@ -197,13 +215,11 @@ public void onEvent(Object event) { assertFalse(eventBus.hasSubscriberForEvent(String.class)); } + @Test public void testHasSubscriberForEventImplementedInterface() { assertFalse(eventBus.hasSubscriberForEvent(String.class)); - Object subscriber = new Object() { - public void onEvent(CharSequence event) { - } - }; + Object subscriber = new CharSequenceSubscriber(); eventBus.register(subscriber); assertTrue(eventBus.hasSubscriberForEvent(CharSequence.class)); assertTrue(eventBus.hasSubscriberForEvent(String.class)); @@ -213,42 +229,65 @@ public void onEvent(CharSequence event) { assertFalse(eventBus.hasSubscriberForEvent(String.class)); } + @Subscribe public void onEvent(String event) { lastStringEvent = event; countStringEvent++; } + @Subscribe public void onEvent(Integer event) { lastIntEvent = event; countIntEvent++; } + @Subscribe public void onEvent(MyEvent event) { countMyEvent++; } + @Subscribe + public void onEvent2(MyEvent event) { + countMyEvent2++; + } + + @Subscribe public void onEvent(MyEventExtended event) { countMyEventExtended++; } - static class TestActivity extends Activity { + public static class StringEventSubscriber { public String lastStringEvent; + @Subscribe public void onEvent(String event) { lastStringEvent = event; } } - class MyEvent { + public static class CharSequenceSubscriber { + @Subscribe + public void onEvent(CharSequence event) { + } + } + + public static class ObjectSubscriber { + @Subscribe + public void onEvent(Object event) { + } + } + + public class MyEvent { } - class MyEventExtended extends MyEvent { + public class MyEventExtended extends MyEvent { } - class RepostInteger { + public class RepostInteger { public int lastEvent; public int countEvent; + @Subscribe public void onEvent(Integer event) { lastEvent = event; countEvent++; diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusBuilderTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBuilderTest.java similarity index 84% rename from EventBusTest/src/de/greenrobot/event/test/EventBusBuilderTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBuilderTest.java index 58b6bc8d..16f13255 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusBuilderTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; -import de.greenrobot.event.EventBus; -import de.greenrobot.event.EventBusBuilder; -import de.greenrobot.event.EventBusException; -import de.greenrobot.event.NoSubscriberEvent; -import de.greenrobot.event.SubscriberExceptionEvent; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.fail; /** * @author Markus Junginger, greenrobot */ public class EventBusBuilderTest extends AbstractEventBusTest { + @Test public void testThrowSubscriberException() { eventBus = EventBus.builder().throwSubscriberException(true).build(); eventBus.register(new SubscriberExceptionEventTracker()); @@ -38,6 +38,7 @@ public void testThrowSubscriberException() { } } + @Test public void testDoNotSendSubscriberExceptionEvent() { eventBus = EventBus.builder().logSubscriberExceptions(false).sendSubscriberExceptionEvent(false).build(); eventBus.register(new SubscriberExceptionEventTracker()); @@ -46,6 +47,7 @@ public void testDoNotSendSubscriberExceptionEvent() { assertEventCount(0); } + @Test public void testDoNotSendNoSubscriberEvent() { eventBus = EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).build(); eventBus.register(new NoSubscriberEventTracker()); @@ -53,12 +55,13 @@ public void testDoNotSendNoSubscriberEvent() { assertEventCount(0); } + @Test public void testInstallDefaultEventBus() { EventBusBuilder builder = EventBus.builder(); try { // Either this should throw when another unit test got the default event bus... eventBus = builder.installDefaultEventBus(); - assertEquals(eventBus, EventBus.getDefault()); + Assert.assertEquals(eventBus, EventBus.getDefault()); // ...or this should throw eventBus = builder.installDefaultEventBus(); @@ -68,25 +71,29 @@ public void testInstallDefaultEventBus() { } } + @Test public void testEventInheritance() { eventBus = EventBus.builder().eventInheritance(false).build(); eventBus.register(new ThrowingSubscriber()); eventBus.post("Foo"); } - class SubscriberExceptionEventTracker { + public class SubscriberExceptionEventTracker { + @Subscribe public void onEvent(SubscriberExceptionEvent event) { trackEvent(event); } } - class NoSubscriberEventTracker { + public class NoSubscriberEventTracker { + @Subscribe public void onEvent(NoSubscriberEvent event) { trackEvent(event); } } - class ThrowingSubscriber { + public class ThrowingSubscriber { + @Subscribe public void onEvent(Object event) { throw new RuntimeException(); } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusCancelEventDeliveryTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusCancelEventDeliveryTest.java similarity index 57% rename from EventBusTest/src/de/greenrobot/event/test/EventBusCancelEventDeliveryTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusCancelEventDeliveryTest.java index 027d74f9..aeed8fdd 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusCancelEventDeliveryTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusCancelEventDeliveryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,25 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; + +import org.junit.Test; import java.util.concurrent.CountDownLatch; -import android.test.UiThreadTest; -import de.greenrobot.event.EventBusException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; -/** - * @author Markus Junginger, greenrobot - */ public class EventBusCancelEventDeliveryTest extends AbstractEventBusTest { - private Throwable failed; + Throwable failed; + @Test public void testCancel() { - Subscriber canceler = new Subscriber(true); - eventBus.register(new Subscriber(false)); - eventBus.register(canceler, 1); - eventBus.register(new Subscriber(false)); + Subscriber canceler = new Subscriber(1, true); + eventBus.register(new Subscriber(0, false)); + eventBus.register(canceler); + eventBus.register(new Subscriber(0, false)); eventBus.post("42"); assertEquals(1, eventCount.intValue()); @@ -40,15 +41,16 @@ public void testCancel() { assertEquals(1 + 2, eventCount.intValue()); } + @Test public void testCancelInBetween() { - Subscriber canceler = new Subscriber(true); - eventBus.register(canceler, 2); - eventBus.register(new Subscriber(false), 1); - eventBus.register(new Subscriber(false), 3); + eventBus.register(new Subscriber(2, true)); + eventBus.register(new Subscriber(1, false)); + eventBus.register(new Subscriber(3, false)); eventBus.post("42"); assertEquals(2, eventCount.intValue()); } + @Test public void testCancelOutsideEventHandler() { try { eventBus.cancelEventDelivery(this); @@ -58,6 +60,7 @@ public void testCancelOutsideEventHandler() { } } + @Test public void testCancelWrongEvent() { eventBus.register(new SubscriberCancelOtherEvent()); eventBus.post("42"); @@ -65,32 +68,47 @@ public void testCancelWrongEvent() { assertNotNull(failed); } - @UiThreadTest - public void testCancelInMainThread() { - SubscriberMainThread subscriber = new SubscriberMainThread(); - eventBus.register(subscriber); - eventBus.post("42"); - awaitLatch(subscriber.done, 10); - assertEquals(0, eventCount.intValue()); - assertNotNull(failed); - } - - class Subscriber { + public class Subscriber { + private final int prio; private final boolean cancel; - public Subscriber(boolean cancel) { + public Subscriber(int prio, boolean cancel) { + this.prio = prio; this.cancel = cancel; } + @Subscribe public void onEvent(String event) { - trackEvent(event); - if (cancel) { - eventBus.cancelEventDelivery(event); + handleEvent(event, 0); + } + + @Subscribe(priority = 1) + public void onEvent1(String event) { + handleEvent(event, 1); + } + + @Subscribe(priority = 2) + public void onEvent2(String event) { + handleEvent(event, 2); + } + + @Subscribe(priority = 3) + public void onEvent3(String event) { + handleEvent(event, 3); + } + + private void handleEvent(String event, int prio) { + if(this.prio == prio) { + trackEvent(event); + if (cancel) { + eventBus.cancelEventDelivery(event); + } } } } - class SubscriberCancelOtherEvent { + public class SubscriberCancelOtherEvent { + @Subscribe public void onEvent(String event) { try { eventBus.cancelEventDelivery(this); @@ -100,9 +118,10 @@ public void onEvent(String event) { } } - class SubscriberMainThread { + public class SubscriberMainThread { final CountDownLatch done = new CountDownLatch(1); + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(String event) { try { eventBus.cancelEventDelivery(event); diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusFallbackToReflectionTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusFallbackToReflectionTest.java new file mode 100644 index 00000000..4b884ef4 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusFallbackToReflectionTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EventBusFallbackToReflectionTest extends AbstractEventBusTest { + private class PrivateEvent { + } + + public class PublicClass { + @Subscribe + public void onEvent(Object any) { + trackEvent(any); + } + } + + private class PrivateClass { + @Subscribe + public void onEvent(Object any) { + trackEvent(any); + } + } + + public class PublicWithPrivateSuperClass extends PrivateClass { + @Subscribe + public void onEvent(String any) { + trackEvent(any); + } + } + + public class PublicClassWithPrivateEvent { + @Subscribe + public void onEvent(PrivateEvent any) { + trackEvent(any); + } + } + + public class PublicClassWithPublicAndPrivateEvent { + @Subscribe + public void onEvent(String any) { + trackEvent(any); + } + + @Subscribe + public void onEvent(PrivateEvent any) { + trackEvent(any); + } + } + + public class PublicWithPrivateEventInSuperclass extends PublicClassWithPrivateEvent { + @Subscribe + public void onEvent(Object any) { + trackEvent(any); + } + } + + public EventBusFallbackToReflectionTest() { + super(true); + } + + @Test + public void testAnonymousSubscriberClass() { + Object subscriber = new Object() { + @Subscribe + public void onEvent(String event) { + trackEvent(event); + } + }; + eventBus.register(subscriber); + + eventBus.post("Hello"); + assertEquals("Hello", lastEvent); + assertEquals(1, eventsReceived.size()); + } + + @Test + public void testAnonymousSubscriberClassWithPublicSuperclass() { + Object subscriber = new PublicClass() { + @Subscribe + public void onEvent(String event) { + trackEvent(event); + } + }; + eventBus.register(subscriber); + + eventBus.post("Hello"); + assertEquals("Hello", lastEvent); + assertEquals(2, eventsReceived.size()); + } + + @Test + public void testAnonymousSubscriberClassWithPrivateSuperclass() { + eventBus.register(new PublicWithPrivateSuperClass()); + eventBus.post("Hello"); + assertEquals("Hello", lastEvent); + assertEquals(2, eventsReceived.size()); + } + + @Test + public void testSubscriberClassWithPrivateEvent() { + eventBus.register(new PublicClassWithPrivateEvent()); + PrivateEvent privateEvent = new PrivateEvent(); + eventBus.post(privateEvent); + assertEquals(privateEvent, lastEvent); + assertEquals(1, eventsReceived.size()); + } + + @Test + public void testSubscriberClassWithPublicAndPrivateEvent() { + eventBus.register(new PublicClassWithPublicAndPrivateEvent()); + + eventBus.post("Hello"); + assertEquals("Hello", lastEvent); + assertEquals(1, eventsReceived.size()); + + PrivateEvent privateEvent = new PrivateEvent(); + eventBus.post(privateEvent); + assertEquals(privateEvent, lastEvent); + assertEquals(2, eventsReceived.size()); + } + + @Test + public void testSubscriberExtendingClassWithPrivateEvent() { + eventBus.register(new PublicWithPrivateEventInSuperclass()); + PrivateEvent privateEvent = new PrivateEvent(); + eventBus.post(privateEvent); + assertEquals(privateEvent, lastEvent); + assertEquals(2, eventsReceived.size()); + } + +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusGenericsTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusGenericsTest.java new file mode 100644 index 00000000..59f8a37b --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusGenericsTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + +import org.junit.Test; + +public class EventBusGenericsTest extends AbstractEventBusTest { + public static class GenericEvent { + T value; + } + + public class GenericEventSubscriber { + @Subscribe + public void onGenericEvent(GenericEvent event) { + trackEvent(event); + } + } + + public class FullGenericEventSubscriber { + @Subscribe + public void onGenericEvent(T event) { + trackEvent(event); + } + } + + public class GenericNumberEventSubscriber { + @Subscribe + public void onGenericEvent(T event) { + trackEvent(event); + } + } + + public class GenericFloatEventSubscriber extends GenericNumberEventSubscriber { + } + + @Test + public void testGenericEventAndSubscriber() { + GenericEventSubscriber genericSubscriber = new GenericEventSubscriber(); + eventBus.register(genericSubscriber); + eventBus.post(new GenericEvent()); + assertEventCount(1); + } + + @Test + public void testGenericEventAndSubscriber_TypeErasure() { + FullGenericEventSubscriber genericSubscriber = new FullGenericEventSubscriber(); + eventBus.register(genericSubscriber); + eventBus.post(new IntTestEvent(42)); + eventBus.post("Type erasure!"); + assertEventCount(2); + } + + @Test + public void testGenericEventAndSubscriber_BaseType() { + GenericNumberEventSubscriber genericSubscriber = new GenericNumberEventSubscriber<>(); + eventBus.register(genericSubscriber); + eventBus.post(new Float(42)); + eventBus.post(new Double(23)); + assertEventCount(2); + eventBus.post("Not the same base type"); + assertEventCount(2); + } + + @Test + public void testGenericEventAndSubscriber_Subclass() { + GenericFloatEventSubscriber genericSubscriber = new GenericFloatEventSubscriber(); + eventBus.register(genericSubscriber); + eventBus.post(new Float(42)); + eventBus.post(new Double(77)); + assertEventCount(2); + eventBus.post("Not the same base type"); + assertEventCount(2); + } +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusIndexTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusIndexTest.java new file mode 100644 index 00000000..c3923346 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusIndexTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + +import org.greenrobot.eventbus.meta.SimpleSubscriberInfo; +import org.greenrobot.eventbus.meta.SubscriberInfo; +import org.greenrobot.eventbus.meta.SubscriberInfoIndex; +import org.greenrobot.eventbus.meta.SubscriberMethodInfo; +import org.junit.Assert; +import org.junit.Test; + +public class EventBusIndexTest { + private String value; + + /** Ensures the index is actually used and no reflection fall-back kicks in. */ + @Test + public void testManualIndexWithoutAnnotation() { + SubscriberInfoIndex index = new SubscriberInfoIndex() { + + @Override + public SubscriberInfo getSubscriberInfo(Class subscriberClass) { + Assert.assertEquals(EventBusIndexTest.class, subscriberClass); + SubscriberMethodInfo[] methodInfos = { + new SubscriberMethodInfo("someMethodWithoutAnnotation", String.class) + }; + return new SimpleSubscriberInfo(EventBusIndexTest.class, false, methodInfos); + } + }; + + EventBus eventBus = EventBus.builder().addIndex(index).build(); + eventBus.register(this); + eventBus.post("Yepp"); + eventBus.unregister(this); + Assert.assertEquals("Yepp", value); + } + + public void someMethodWithoutAnnotation(String value) { + this.value = value; + } +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassNoMethod.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassNoMethod.java new file mode 100644 index 00000000..0fcc73cf --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassNoMethod.java @@ -0,0 +1,5 @@ +package org.greenrobot.eventbus; + +// Need to use upper class or Android test runner does not pick it up +public class EventBusInheritanceDisabledSubclassNoMethod extends EventBusInheritanceDisabledTest { +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassTest.java new file mode 100644 index 00000000..6ab86d23 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledSubclassTest.java @@ -0,0 +1,20 @@ +package org.greenrobot.eventbus; + +import org.junit.Ignore; + +// Need to use upper class or Android test runner does not pick it up +public class EventBusInheritanceDisabledSubclassTest extends EventBusInheritanceDisabledTest { + + int countMyEventOverwritten; + + @Subscribe + public void onEvent(MyEvent event) { + countMyEventOverwritten++; + } + + @Override + @Ignore + public void testEventClassHierarchy() { + // TODO fix test in super, then remove this + } +} \ No newline at end of file diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledTest.java new file mode 100644 index 00000000..4b026719 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceDisabledTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.junit.Before; +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * @author Markus Junginger, greenrobot + */ +public class EventBusInheritanceDisabledTest { + + protected EventBus eventBus; + + protected int countMyEventExtended; + protected int countMyEvent; + protected int countObjectEvent; + private int countMyEventInterface; + private int countMyEventInterfaceExtended; + + @Before + public void setUp() throws Exception { + eventBus = EventBus.builder().eventInheritance(false).build(); + } + + @Test + public void testEventClassHierarchy() { + eventBus.register(this); + + eventBus.post("Hello"); + assertEquals(0, countObjectEvent); + + eventBus.post(new MyEvent()); + assertEquals(0, countObjectEvent); + assertEquals(1, countMyEvent); + + eventBus.post(new MyEventExtended()); + assertEquals(0, countObjectEvent); + assertEquals(1, countMyEvent); + assertEquals(1, countMyEventExtended); + } + + @Test + public void testEventClassHierarchySticky() { + eventBus.postSticky("Hello"); + eventBus.postSticky(new MyEvent()); + eventBus.postSticky(new MyEventExtended()); + eventBus.register(new StickySubscriber()); + assertEquals(1, countMyEventExtended); + assertEquals(1, countMyEvent); + assertEquals(0, countObjectEvent); + } + + @Test + public void testEventInterfaceHierarchy() { + eventBus.register(this); + + eventBus.post(new MyEvent()); + assertEquals(0, countMyEventInterface); + + eventBus.post(new MyEventExtended()); + assertEquals(0, countMyEventInterface); + assertEquals(0, countMyEventInterfaceExtended); + } + + @Test + public void testEventSuperInterfaceHierarchy() { + eventBus.register(this); + + eventBus.post(new MyEventInterfaceExtended() { + }); + assertEquals(0, countMyEventInterface); + assertEquals(0, countMyEventInterfaceExtended); + } + + @Test + public void testSubscriberClassHierarchy() { + EventBusInheritanceDisabledSubclassTest + subscriber = new EventBusInheritanceDisabledSubclassTest(); + eventBus.register(subscriber); + + eventBus.post("Hello"); + assertEquals(0, subscriber.countObjectEvent); + + eventBus.post(new MyEvent()); + assertEquals(0, subscriber.countObjectEvent); + assertEquals(0, subscriber.countMyEvent); + assertEquals(1, subscriber.countMyEventOverwritten); + + eventBus.post(new MyEventExtended()); + assertEquals(0, subscriber.countObjectEvent); + assertEquals(0, subscriber.countMyEvent); + assertEquals(1, subscriber.countMyEventExtended); + assertEquals(1, subscriber.countMyEventOverwritten); + } + + @Test + public void testSubscriberClassHierarchyWithoutNewSubscriberMethod() { + EventBusInheritanceDisabledSubclassNoMethod + subscriber = new EventBusInheritanceDisabledSubclassNoMethod(); + eventBus.register(subscriber); + + eventBus.post("Hello"); + assertEquals(0, subscriber.countObjectEvent); + + eventBus.post(new MyEvent()); + assertEquals(0, subscriber.countObjectEvent); + assertEquals(1, subscriber.countMyEvent); + + eventBus.post(new MyEventExtended()); + assertEquals(0, subscriber.countObjectEvent); + assertEquals(1, subscriber.countMyEvent); + assertEquals(1, subscriber.countMyEventExtended); + } + + @Subscribe + public void onEvent(Object event) { + countObjectEvent++; + } + + @Subscribe + public void onEvent(MyEvent event) { + countMyEvent++; + } + + @Subscribe + public void onEvent(MyEventExtended event) { + countMyEventExtended++; + } + + @Subscribe + public void onEvent(MyEventInterface event) { + countMyEventInterface++; + } + + @Subscribe + public void onEvent(MyEventInterfaceExtended event) { + countMyEventInterfaceExtended++; + } + + public static interface MyEventInterface { + } + + public static class MyEvent implements MyEventInterface { + } + + public static interface MyEventInterfaceExtended extends MyEventInterface { + } + + public static class MyEventExtended extends MyEvent implements MyEventInterfaceExtended { + } + + public class StickySubscriber { + @Subscribe(sticky = true) + public void onEvent(Object event) { + countObjectEvent++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEvent event) { + countMyEvent++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventExtended event) { + countMyEventExtended++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventInterface event) { + countMyEventInterface++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventInterfaceExtended event) { + countMyEventInterfaceExtended++; + } + } + +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassNoMethodTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassNoMethodTest.java new file mode 100644 index 00000000..a9f6da47 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassNoMethodTest.java @@ -0,0 +1,5 @@ +package org.greenrobot.eventbus; + +// Need to use upper class or Android test runner does not pick it up +public class EventBusInheritanceSubclassNoMethodTest extends EventBusInheritanceTest { +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassTest.java new file mode 100644 index 00000000..8b3d4e4d --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceSubclassTest.java @@ -0,0 +1,19 @@ +package org.greenrobot.eventbus; + +import org.junit.Ignore; + +// Need to use upper class or Android test runner does not pick it up +public class EventBusInheritanceSubclassTest extends EventBusInheritanceTest { + int countMyEventOverwritten; + + @Subscribe + public void onEvent(MyEvent event) { + countMyEventOverwritten++; + } + + @Override + @Ignore + public void testEventClassHierarchy() { + // TODO fix test in super, then remove this + } +} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusInheritanceTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceTest.java similarity index 54% rename from EventBusTest/src/de/greenrobot/event/test/EventBusInheritanceTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceTest.java index 27e3d7b7..9fd52288 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusInheritanceTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusInheritanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; -import junit.framework.TestCase; -import de.greenrobot.event.EventBus; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; /** * @author Markus Junginger, greenrobot */ -public class EventBusInheritanceTest extends TestCase { +public class EventBusInheritanceTest { - private EventBus eventBus; + protected EventBus eventBus; protected int countMyEventExtended; protected int countMyEvent; @@ -31,11 +33,12 @@ public class EventBusInheritanceTest extends TestCase { private int countMyEventInterface; private int countMyEventInterfaceExtended; - protected void setUp() throws Exception { - super.setUp(); + @Before + public void setUp() throws Exception { eventBus = new EventBus(); } + @Test public void testEventClassHierarchy() { eventBus.register(this); @@ -52,6 +55,18 @@ public void testEventClassHierarchy() { assertEquals(1, countMyEventExtended); } + @Test + public void testEventClassHierarchySticky() { + eventBus.postSticky("Hello"); + eventBus.postSticky(new MyEvent()); + eventBus.postSticky(new MyEventExtended()); + eventBus.register(new StickySubscriber()); + assertEquals(1, countMyEventExtended); + assertEquals(2, countMyEvent); + assertEquals(3, countObjectEvent); + } + + @Test public void testEventInterfaceHierarchy() { eventBus.register(this); @@ -63,6 +78,7 @@ public void testEventInterfaceHierarchy() { assertEquals(1, countMyEventInterfaceExtended); } + @Test public void testEventSuperInterfaceHierarchy() { eventBus.register(this); @@ -72,8 +88,9 @@ public void testEventSuperInterfaceHierarchy() { assertEquals(1, countMyEventInterfaceExtended); } + @Test public void testSubscriberClassHierarchy() { - SubscriberExtended subscriber = new SubscriberExtended(); + EventBusInheritanceSubclassTest subscriber = new EventBusInheritanceSubclassTest(); eventBus.register(subscriber); eventBus.post("Hello"); @@ -91,43 +108,86 @@ public void testSubscriberClassHierarchy() { assertEquals(2, subscriber.countMyEventOverwritten); } + @Test + public void testSubscriberClassHierarchyWithoutNewSubscriberMethod() { + EventBusInheritanceSubclassNoMethodTest + subscriber = new EventBusInheritanceSubclassNoMethodTest(); + eventBus.register(subscriber); + + eventBus.post("Hello"); + assertEquals(1, subscriber.countObjectEvent); + + eventBus.post(new MyEvent()); + assertEquals(2, subscriber.countObjectEvent); + assertEquals(1, subscriber.countMyEvent); + + eventBus.post(new MyEventExtended()); + assertEquals(3, subscriber.countObjectEvent); + assertEquals(2, subscriber.countMyEvent); + assertEquals(1, subscriber.countMyEventExtended); + } + + @Subscribe public void onEvent(Object event) { countObjectEvent++; } + @Subscribe public void onEvent(MyEvent event) { countMyEvent++; } + @Subscribe public void onEvent(MyEventExtended event) { countMyEventExtended++; } + @Subscribe public void onEvent(MyEventInterface event) { countMyEventInterface++; } + @Subscribe public void onEvent(MyEventInterfaceExtended event) { countMyEventInterfaceExtended++; } - static interface MyEventInterface { + public static interface MyEventInterface { } - static class MyEvent implements MyEventInterface { + public static class MyEvent implements MyEventInterface { } - static interface MyEventInterfaceExtended extends MyEventInterface { + public static interface MyEventInterfaceExtended extends MyEventInterface { } - static class MyEventExtended extends MyEvent implements MyEventInterfaceExtended { + public static class MyEventExtended extends MyEvent implements MyEventInterfaceExtended { } - static class SubscriberExtended extends EventBusInheritanceTest { - private int countMyEventOverwritten; + public class StickySubscriber { + @Subscribe(sticky = true) + public void onEvent(Object event) { + countObjectEvent++; + } + @Subscribe(sticky = true) public void onEvent(MyEvent event) { - countMyEventOverwritten++; + countMyEvent++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventExtended event) { + countMyEventExtended++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventInterface event) { + countMyEventInterface++; + } + + @Subscribe(sticky = true) + public void onEvent(MyEventInterfaceExtended event) { + countMyEventInterfaceExtended++; } } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusMultithreadedTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusMultithreadedTest.java similarity index 65% rename from EventBusTest/src/de/greenrobot/event/test/EventBusMultithreadedTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusMultithreadedTest.java index be68fd12..e1c1576c 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusMultithreadedTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusMultithreadedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,78 +13,61 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; + +import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; -import android.os.Looper; -import android.util.Log; -import de.greenrobot.event.EventBus; +import static org.junit.Assert.assertEquals; -/** - * @author Markus Junginger, greenrobot - */ public class EventBusMultithreadedTest extends AbstractEventBusTest { - private static final int COUNT = LONG_TESTS ? 100000 : 1000; + static final int COUNT = LONG_TESTS ? 100000 : 1000; - private final AtomicInteger countStringEvent = new AtomicInteger(); - private final AtomicInteger countIntegerEvent = new AtomicInteger(); - private final AtomicInteger countObjectEvent = new AtomicInteger(); - private final AtomicInteger countIntTestEvent = new AtomicInteger(); + final AtomicInteger countStringEvent = new AtomicInteger(); + final AtomicInteger countIntegerEvent = new AtomicInteger(); + final AtomicInteger countObjectEvent = new AtomicInteger(); + final AtomicInteger countIntTestEvent = new AtomicInteger(); - private String lastStringEvent; - private Integer lastIntegerEvent; + String lastStringEvent; + Integer lastIntegerEvent; - private IntTestEvent lastIntTestEvent; + IntTestEvent lastIntTestEvent; + @Test public void testPost01Thread() throws InterruptedException { runThreadsSingleEventType(1); } + @Test public void testPost04Threads() throws InterruptedException { runThreadsSingleEventType(4); } + @Test public void testPost40Threads() throws InterruptedException { runThreadsSingleEventType(40); } + @Test public void testPostMixedEventType01Thread() throws InterruptedException { runThreadsMixedEventType(1); } + @Test public void testPostMixedEventType04Threads() throws InterruptedException { runThreadsMixedEventType(4); } + @Test public void testPostMixedEventType40Threads() throws InterruptedException { runThreadsMixedEventType(40); } - public void testSubscribeUnSubscribeAndPostMixedEventType() throws InterruptedException { - List threads = new ArrayList(); - - // Debug.startMethodTracing("testSubscribeUnSubscribeAndPostMixedEventType"); - for (int i = 0; i < 5; i++) { - SubscribeUnsubscribeThread thread = new SubscribeUnsubscribeThread(); - thread.start(); - threads.add(thread); - } - // This test takes a bit longer, so just use fraction the regular count - runThreadsMixedEventType(COUNT / 4, 5); - for (SubscribeUnsubscribeThread thread : threads) { - thread.shutdown(); - } - for (SubscribeUnsubscribeThread thread : threads) { - thread.join(); - } - // Debug.stopMethodTracing(); - } - private void runThreadsSingleEventType(int threadCount) throws InterruptedException { int iterations = COUNT / threadCount; eventBus.register(this); @@ -93,7 +76,7 @@ private void runThreadsSingleEventType(int threadCount) throws InterruptedExcept List threads = startThreads(latch, threadCount, iterations, "Hello"); long time = triggerAndWaitForThreads(threads, latch); - Log.d(EventBus.TAG, threadCount + " threads posted " + iterations + " events each in " + time + "ms"); + log(threadCount + " threads posted " + iterations + " events each in " + time + "ms"); waitForEventCount(COUNT * 2, 5000); @@ -107,7 +90,7 @@ private void runThreadsMixedEventType(int threadCount) throws InterruptedExcepti runThreadsMixedEventType(COUNT, threadCount); } - private void runThreadsMixedEventType(int count, int threadCount) throws InterruptedException { + void runThreadsMixedEventType(int count, int threadCount) throws InterruptedException { eventBus.register(this); int eventTypeCount = 3; int iterations = count / threadCount / eventTypeCount; @@ -123,7 +106,7 @@ private void runThreadsMixedEventType(int count, int threadCount) throws Interru threads.addAll(threadsIntTestEvent); long time = triggerAndWaitForThreads(threads, latch); - Log.d(EventBus.TAG, threadCount * eventTypeCount + " mixed threads posted " + iterations + " events each in " + log(threadCount * eventTypeCount + " mixed threads posted " + iterations + " events each in " + time + "ms"); int expectedCountEach = threadCount * iterations; @@ -164,24 +147,28 @@ private List startThreads(CountDownLatch latch, int threadCount, i return threads; } + @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onEventBackgroundThread(String event) { lastStringEvent = event; countStringEvent.incrementAndGet(); trackEvent(event); } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(Integer event) { lastIntegerEvent = event; countIntegerEvent.incrementAndGet(); trackEvent(event); } + @Subscribe(threadMode = ThreadMode.ASYNC) public void onEventAsync(IntTestEvent event) { countIntTestEvent.incrementAndGet(); lastIntTestEvent = event; trackEvent(event); } + @Subscribe public void onEvent(Object event) { countObjectEvent.incrementAndGet(); trackEvent(event); @@ -205,7 +192,7 @@ public void run() { try { startLatch.await(); } catch (InterruptedException e) { - Log.w(EventBus.TAG, "Unexpeced interrupt", e); + log("Unexpected interrupt", e); } for (int i = 0; i < iterations; i++) { @@ -214,46 +201,4 @@ public void run() { } } - class SubscribeUnsubscribeThread extends Thread { - boolean running = true; - - public void shutdown() { - running = false; - } - - @Override - public void run() { - try { - while (running) { - eventBus.register(this); - double random = Math.random(); - if (random > 0.6d) { - Thread.sleep(0, (int) (1000000 * Math.random())); - } else if (random > 0.3d) { - Thread.yield(); - } - eventBus.unregister(this); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - public void onEventMainThread(String event) { - assertSame(Looper.getMainLooper(), Looper.myLooper()); - } - - public void onEventBackgroundThread(Integer event) { - assertNotSame(Looper.getMainLooper(), Looper.myLooper()); - } - - public void onEvent(Object event) { - assertNotSame(Looper.getMainLooper(), Looper.myLooper()); - } - - public void onEventAsync(Object event) { - assertNotSame(Looper.getMainLooper(), Looper.myLooper()); - } - } - } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusNoSubscriberEventTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusNoSubscriberEventTest.java similarity index 78% rename from EventBusTest/src/de/greenrobot/event/test/EventBusNoSubscriberEventTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusNoSubscriberEventTest.java index ffe48f10..b358f8c3 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusNoSubscriberEventTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusNoSubscriberEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; -import de.greenrobot.event.EventBus; -import de.greenrobot.event.NoSubscriberEvent; -import de.greenrobot.event.SubscriberExceptionEvent; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; /** * @author Markus Junginger, greenrobot */ public class EventBusNoSubscriberEventTest extends AbstractEventBusTest { + @Test public void testNoSubscriberEvent() { eventBus.register(this); eventBus.post("Foo"); @@ -34,17 +36,15 @@ public void testNoSubscriberEvent() { assertSame(eventBus, noSub.eventBus); } + @Test public void testNoSubscriberEventAfterUnregister() { - Object subscriber = new Object() { - @SuppressWarnings("unused") - public void onEvent(String dummy) { - } - }; + Object subscriber = new DummySubscriber(); eventBus.register(subscriber); eventBus.unregister(subscriber); testNoSubscriberEvent(); } + @Test public void testBadNoSubscriberSubscriber() { eventBus = EventBus.builder().logNoSubscriberMessages(false).build(); eventBus.register(this); @@ -57,15 +57,25 @@ public void testBadNoSubscriberSubscriber() { assertEquals("Foo", noSub.originalEvent); } + @Subscribe public void onEvent(NoSubscriberEvent event) { trackEvent(event); } + @Subscribe public void onEvent(SubscriberExceptionEvent event) { trackEvent(event); } - class BadNoSubscriberSubscriber { + public static class DummySubscriber { + @SuppressWarnings("unused") + @Subscribe + public void onEvent(String dummy) { + } + } + + public class BadNoSubscriberSubscriber { + @Subscribe public void onEvent(NoSubscriberEvent event) { throw new RuntimeException("I'm bad"); } diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusOrderedSubscriptionsTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusOrderedSubscriptionsTest.java new file mode 100644 index 00000000..e8d0d796 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusOrderedSubscriptionsTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author Markus Junginger, greenrobot + */ +public class EventBusOrderedSubscriptionsTest extends AbstractEventBusTest { + + int lastPrio = Integer.MAX_VALUE; + final List registered = new ArrayList(); + private String fail; + + @Test + public void testOrdered() { + runTestOrdered("42", false, 5); + } + + @Test + public void testOrderedMainThread() { + runTestOrdered(new IntTestEvent(42), false, 3); + } + + @Test + public void testOrderedBackgroundThread() { + runTestOrdered(Integer.valueOf(42), false, 3); + } + + @Test + public void testOrderedSticky() { + runTestOrdered("42", true, 5); + } + + @Test + public void testOrderedMainThreadSticky() { + runTestOrdered(new IntTestEvent(42), true, 3); + } + + @Test + public void testOrderedBackgroundThreadSticky() { + runTestOrdered(Integer.valueOf(42), true, 3); + } + + protected void runTestOrdered(Object event, boolean sticky, int expectedEventCount) { + Object subscriber = sticky ? new PrioSubscriberSticky() : new PrioSubscriber(); + eventBus.register(subscriber); + eventBus.post(event); + + waitForEventCount(expectedEventCount, 10000); + assertEquals(null, fail); + + eventBus.unregister(subscriber); + } + + public final class PrioSubscriber { + @Subscribe(priority = 1) + public void onEventP1(String event) { + handleEvent(1, event); + } + + @Subscribe(priority = -1) + public void onEventM1(String event) { + handleEvent(-1, event); + } + + @Subscribe(priority = 0) + public void onEventP0(String event) { + handleEvent(0, event); + } + + @Subscribe(priority = 10) + public void onEventP10(String event) { + handleEvent(10, event); + } + + @Subscribe(priority = -100) + public void onEventM100(String event) { + handleEvent(-100, event); + } + + + @Subscribe(threadMode = ThreadMode.MAIN, priority = -1) + public void onEventMainThreadM1(IntTestEvent event) { + handleEvent(-1, event); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThreadP0(IntTestEvent event) { + handleEvent(0, event); + } + + @Subscribe(threadMode = ThreadMode.MAIN, priority = 1) + public void onEventMainThreadP1(IntTestEvent event) { + handleEvent(1, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND, priority = 1) + public void onEventBackgroundThreadP1(Integer event) { + handleEvent(1, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onEventBackgroundThreadP0(Integer event) { + handleEvent(0, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND, priority = -1) + public void onEventBackgroundThreadM1(Integer event) { + handleEvent(-1, event); + } + + protected void handleEvent(int prio, Object event) { + if (prio > lastPrio) { + fail = "Called prio " + prio + " after " + lastPrio; + } + lastPrio = prio; + + log("Subscriber " + prio + " got: " + event); + trackEvent(event); + } + + } + + public final class PrioSubscriberSticky { + @Subscribe(priority = 1, sticky = true) + public void onEventP1(String event) { + handleEvent(1, event); + } + + + @Subscribe(priority = -1, sticky = true) + public void onEventM1(String event) { + handleEvent(-1, event); + } + + @Subscribe(priority = 0, sticky = true) + public void onEventP0(String event) { + handleEvent(0, event); + } + + @Subscribe(priority = 10, sticky = true) + public void onEventP10(String event) { + handleEvent(10, event); + } + + @Subscribe(priority = -100, sticky = true) + public void onEventM100(String event) { + handleEvent(-100, event); + } + + @Subscribe(threadMode = ThreadMode.MAIN, priority = -1, sticky = true) + public void onEventMainThreadM1(IntTestEvent event) { + handleEvent(-1, event); + } + + @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) + public void onEventMainThreadP0(IntTestEvent event) { + handleEvent(0, event); + } + + @Subscribe(threadMode = ThreadMode.MAIN, priority = 1, sticky = true) + public void onEventMainThreadP1(IntTestEvent event) { + handleEvent(1, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND, priority = 1, sticky = true) + public void onEventBackgroundThreadP1(Integer event) { + handleEvent(1, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true) + public void onEventBackgroundThreadP0(Integer event) { + handleEvent(0, event); + } + + @Subscribe(threadMode = ThreadMode.BACKGROUND, priority = -1, sticky = true) + public void onEventBackgroundThreadM1(Integer event) { + handleEvent(-1, event); + } + + protected void handleEvent(int prio, Object event) { + if (prio > lastPrio) { + fail = "Called prio " + prio + " after " + lastPrio; + } + lastPrio = prio; + + log("Subscriber " + prio + " got: " + event); + trackEvent(event); + } + + } + +} diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusRegistrationRacingTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusRegistrationRacingTest.java similarity index 92% rename from EventBusTest/src/de/greenrobot/event/test/EventBusRegistrationRacingTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusRegistrationRacingTest.java index 41808dc7..7d09d7ad 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusRegistrationRacingTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusRegistrationRacingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; + +import org.junit.Test; import java.util.ArrayList; import java.util.List; @@ -21,6 +23,8 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import static org.junit.Assert.fail; + /** * @author Markus Junginger, greenrobot */ @@ -38,6 +42,7 @@ public class EventBusRegistrationRacingTest extends AbstractEventBusTest { final Executor threadPool = Executors.newCachedThreadPool(); + @Test public void testRacingRegistrations() throws InterruptedException { for (int i = 0; i < ITERATIONS; i++) { startLatch = new CountDownLatch(THREAD_COUNT); @@ -70,7 +75,7 @@ private List startThreads() { return threads; } - class SubscriberThread implements Runnable { + public class SubscriberThread implements Runnable { volatile int eventCount; @Override @@ -87,6 +92,7 @@ public void run() { unregisteredLatch.countDown(); } + @Subscribe public void onEvent(String event) { eventCount++; } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusStickyEventTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusStickyEventTest.java similarity index 67% rename from EventBusTest/src/de/greenrobot/event/test/EventBusStickyEventTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusStickyEventTest.java index 04be4d91..ec426745 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusStickyEventTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusStickyEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,62 +13,89 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; + +import org.junit.Test; + +import static org.junit.Assert.*; /** * @author Markus Junginger, greenrobot */ public class EventBusStickyEventTest extends AbstractEventBusTest { + @Test public void testPostSticky() throws InterruptedException { eventBus.postSticky("Sticky"); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals("Sticky", lastEvent); assertEquals(Thread.currentThread(), lastThread); } + @Test public void testPostStickyTwoEvents() throws InterruptedException { eventBus.postSticky("Sticky"); eventBus.postSticky(new IntTestEvent(7)); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals(2, eventCount.intValue()); } - public void testPostStickyRegisterNonSticky() throws InterruptedException { + @Test + public void testPostStickyTwoSubscribers() throws InterruptedException { eventBus.postSticky("Sticky"); + eventBus.postSticky(new IntTestEvent(7)); eventBus.register(this); + StickyIntTestSubscriber subscriber2 = new StickyIntTestSubscriber(); + eventBus.register(subscriber2); + assertEquals(3, eventCount.intValue()); + + eventBus.postSticky("Sticky"); + assertEquals(4, eventCount.intValue()); + + eventBus.postSticky(new IntTestEvent(8)); + assertEquals(6, eventCount.intValue()); + } + + @Test + public void testPostStickyRegisterNonSticky() throws InterruptedException { + eventBus.postSticky("Sticky"); + eventBus.register(new NonStickySubscriber()); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); } + @Test public void testPostNonStickyRegisterSticky() throws InterruptedException { eventBus.post("NonSticky"); - eventBus.registerSticky(this); + eventBus.register(this); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); } + @Test public void testPostStickyTwice() throws InterruptedException { eventBus.postSticky("Sticky"); eventBus.postSticky("NewSticky"); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals("NewSticky", lastEvent); } + @Test public void testPostStickyThenPostNormal() throws InterruptedException { eventBus.postSticky("Sticky"); eventBus.post("NonSticky"); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals("Sticky", lastEvent); } + @Test public void testPostStickyWithRegisterAndUnregister() throws InterruptedException { - eventBus.registerSticky(this); + eventBus.register(this); eventBus.postSticky("Sticky"); assertEquals("Sticky", lastEvent); eventBus.unregister(this); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals("Sticky", lastEvent); assertEquals(2, eventCount.intValue()); @@ -77,65 +104,93 @@ public void testPostStickyWithRegisterAndUnregister() throws InterruptedExceptio assertEquals("NewSticky", lastEvent); eventBus.unregister(this); - eventBus.registerSticky(this); + eventBus.register(this); assertEquals(4, eventCount.intValue()); assertEquals("NewSticky", lastEvent); } + @Test public void testPostStickyAndGet() throws InterruptedException { eventBus.postSticky("Sticky"); assertEquals("Sticky", eventBus.getStickyEvent(String.class)); } + @Test public void testPostStickyRemoveClass() throws InterruptedException { eventBus.postSticky("Sticky"); eventBus.removeStickyEvent(String.class); assertNull(eventBus.getStickyEvent(String.class)); - eventBus.registerSticky(this); + eventBus.register(this); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); } + @Test public void testPostStickyRemoveEvent() throws InterruptedException { eventBus.postSticky("Sticky"); assertTrue(eventBus.removeStickyEvent("Sticky")); assertNull(eventBus.getStickyEvent(String.class)); - eventBus.registerSticky(this); + eventBus.register(this); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); } + @Test public void testPostStickyRemoveAll() throws InterruptedException { eventBus.postSticky("Sticky"); eventBus.postSticky(new IntTestEvent(77)); eventBus.removeAllStickyEvents(); assertNull(eventBus.getStickyEvent(String.class)); assertNull(eventBus.getStickyEvent(IntTestEvent.class)); - eventBus.registerSticky(this); + eventBus.register(this); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); } + @Test public void testRemoveStickyEventInSubscriber() throws InterruptedException { - eventBus.registerSticky(new Object() { - @SuppressWarnings("unused") - public void onEvent(String event) { - eventBus.removeStickyEvent(event); - } - }); + eventBus.register(new RemoveStickySubscriber()); eventBus.postSticky("Sticky"); - eventBus.registerSticky(this); + eventBus.register(this); assertNull(lastEvent); assertEquals(0, eventCount.intValue()); assertNull(eventBus.getStickyEvent(String.class)); } + @Subscribe(sticky = true) public void onEvent(String event) { trackEvent(event); } + @Subscribe(sticky = true) public void onEvent(IntTestEvent event) { trackEvent(event); } + public class RemoveStickySubscriber { + @SuppressWarnings("unused") + @Subscribe(sticky = true) + public void onEvent(String event) { + eventBus.removeStickyEvent(event); + } + } + + public class NonStickySubscriber { + @Subscribe + public void onEvent(String event) { + trackEvent(event); + } + + @Subscribe + public void onEvent(IntTestEvent event) { + trackEvent(event); + } + } + + public class StickyIntTestSubscriber { + @Subscribe(sticky = true) + public void onEvent(IntTestEvent event) { + trackEvent(event); + } + } } diff --git a/EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberExceptionTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberExceptionTest.java similarity index 84% rename from EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberExceptionTest.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberExceptionTest.java index f9acaa7e..f0dea680 100644 --- a/EventBusTest/src/de/greenrobot/event/test/EventBusSubscriberExceptionTest.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; -import de.greenrobot.event.EventBus; -import de.greenrobot.event.SubscriberExceptionEvent; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; /** * @author Markus Junginger, greenrobot */ public class EventBusSubscriberExceptionTest extends AbstractEventBusTest { + @Test public void testSubscriberExceptionEvent() { eventBus = EventBus.builder().logSubscriberExceptions(false).build(); eventBus.register(this); @@ -35,6 +38,7 @@ public void testSubscriberExceptionEvent() { assertEquals("Bar", exEvent.throwable.getMessage()); } + @Test public void testBadExceptionSubscriber() { eventBus = EventBus.builder().logSubscriberExceptions(false).build(); eventBus.register(this); @@ -43,15 +47,18 @@ public void testBadExceptionSubscriber() { assertEventCount(1); } + @Subscribe public void onEvent(String event) { throw new RuntimeException("Bar"); } + @Subscribe public void onEvent(SubscriberExceptionEvent event) { trackEvent(event); } - class BadExceptionSubscriber { + public class BadExceptionSubscriber { + @Subscribe public void onEvent(SubscriberExceptionEvent event) { throw new RuntimeException("Bad"); } diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberInJarTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberInJarTest.java new file mode 100644 index 00000000..a553e7bc --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberInJarTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.greenrobot.eventbus; + +import org.junit.Assert; +import org.junit.Test; + +public class EventBusSubscriberInJarTest { + protected EventBus eventBus = EventBus.builder().build(); + + @Test + public void testSubscriberInJar() { + SubscriberInJar subscriber = new SubscriberInJar(); + eventBus.register(subscriber); + eventBus.post("Hi Jar"); + eventBus.post(42); + Assert.assertEquals(1, subscriber.getCollectedStrings().size()); + Assert.assertEquals("Hi Jar", subscriber.getCollectedStrings().get(0)); + } +} diff --git a/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberLegalTest.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberLegalTest.java new file mode 100644 index 00000000..c362e900 --- /dev/null +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusSubscriberLegalTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Markus Junginger, greenrobot + */ +public class EventBusSubscriberLegalTest extends AbstractEventBusTest { + + @Test + public void testSubscriberLegal() { + eventBus.register(this); + eventBus.post("42"); + eventBus.unregister(this); + assertEquals(1, eventCount.intValue()); + } + + // With build time verification, some of these tests are obsolete (and cause problems during build) +// public void testSubscriberNotPublic() { +// try { +// eventBus.register(new NotPublic()); +// fail("Registration of ilegal subscriber successful"); +// } catch (EventBusException e) { +// // Expected +// } +// } + +// public void testSubscriberStatic() { +// try { +// eventBus.register(new Static()); +// fail("Registration of ilegal subscriber successful"); +// } catch (EventBusException e) { +// // Expected +// } +// } + + public void testSubscriberLegalAbstract() { + eventBus.register(new AbstractImpl()); + + eventBus.post("42"); + assertEquals(1, eventCount.intValue()); + } + + @Subscribe + public void onEvent(String event) { + trackEvent(event); + } + +// public static class NotPublic { +// @Subscribe +// void onEvent(String event) { +// } +// } + + public static abstract class Abstract { + @Subscribe + public abstract void onEvent(String event); + } + + public class AbstractImpl extends Abstract { + + @Override + @Subscribe + public void onEvent(String event) { + trackEvent(event); + } + + } + +// public static class Static { +// @Subscribe +// public static void onEvent(String event) { +// } +// } + +} diff --git a/EventBusTest/src/de/greenrobot/event/test/IntTestEvent.java b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/IntTestEvent.java similarity index 87% rename from EventBusTest/src/de/greenrobot/event/test/IntTestEvent.java rename to EventBusTestJava/src/main/java/org/greenrobot/eventbus/IntTestEvent.java index dc829b89..db6fa83f 100644 --- a/EventBusTest/src/de/greenrobot/event/test/IntTestEvent.java +++ b/EventBusTestJava/src/main/java/org/greenrobot/eventbus/IntTestEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ /** * Simple event storing an int value. More efficient than Integer because of the its flat hierarchy. */ -package de.greenrobot.event.test; +package org.greenrobot.eventbus; public class IntTestEvent { public final int value; diff --git a/EventBusTestSubscriberInJar/build.gradle b/EventBusTestSubscriberInJar/build.gradle new file mode 100644 index 00000000..21096be3 --- /dev/null +++ b/EventBusTestSubscriberInJar/build.gradle @@ -0,0 +1,29 @@ +apply plugin: "java-library" + +group = "de.greenrobot" +version = "3.0.0" +java.sourceCompatibility = JavaVersion.VERSION_1_8 +java.targetCompatibility = JavaVersion.VERSION_1_8 + +configurations { + provided +} + +dependencies { + implementation project(":eventbus-java") + annotationProcessor project(":eventbus-annotation-processor") +} + +sourceSets { + main { + compileClasspath += configurations.provided + java { + srcDir "src" + } + } +} + +compileJava { + options.compilerArgs << "-AeventBusIndex=org.greenrobot.eventbus.InJarIndex" + options.fork = true +} diff --git a/EventBusTestSubscriberInJar/src/org/greenrobot/eventbus/SubscriberInJar.java b/EventBusTestSubscriberInJar/src/org/greenrobot/eventbus/SubscriberInJar.java new file mode 100644 index 00000000..9d185207 --- /dev/null +++ b/EventBusTestSubscriberInJar/src/org/greenrobot/eventbus/SubscriberInJar.java @@ -0,0 +1,18 @@ +package org.greenrobot.eventbus; + +import java.util.ArrayList; +import java.util.List; + +/** Helper class used by test inside a jar. */ +public class SubscriberInJar { + List collectedStrings = new ArrayList(); + + @Subscribe + public void collectString(String string) { + collectedStrings.add(string); + } + + public List getCollectedStrings() { + return collectedStrings; + } +} diff --git a/HOWTO.md b/HOWTO.md deleted file mode 100644 index ab0729ca..00000000 --- a/HOWTO.md +++ /dev/null @@ -1,245 +0,0 @@ -EventBus How-To -=============== -In the [README file](README.md), you got to know EventBus, and some of its basic principles. You also saw how to add EventBus to your project using Maven Central. Great, now let's dive deeper! - -General usage and API ---------------------- -Here we pick up on the 3 steps of the README and expand a bit on the code. -### 1: Define events ### -Events are POJO (plain old Java object) without any specific requirements. - -```java -public class MessageEvent { - public final String message; - - public MessageEvent(String message) { - this.message = message; - } -} -``` -### 2: Prepare subscribers ### - -Subscribers implement event handling `onEvent` methods that will be called when an event is received. They also need to register and unregister themselves to the bus. - -```java - @Override - public void onStart() { - super.onStart(); - EventBus.getDefault().register(this); - } - - @Override - public void onStop() { - EventBus.getDefault().unregister(this); - super.onStop(); - } - - // This method will be called when a MessageEvent is posted - public void onEvent(MessageEvent event){ - Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show(); - } - - // This method will be called when a SomeOtherEvent is posted - public void onEvent(SomeOtherEvent event){ - doSomethingWith(event); - } - -``` -### 3: Post events ### -Post an event from any part of your code. All subscribers matching the event type will receive it. - -```java - EventBus.getDefault().post(new MessageEvent("Hello everyone!")); -``` - -Delivery threads and ThreadModes --------------------------------- -EventBus can handle threading for you: events can be posted in threads different from the posting thread. - -A common use case is dealing with UI changes. In Android, UI changes must be done in the UI (main) thread. On the other hand, networking, or any time consuming task, must not run on the main thread. EventBus helps you to deal with those tasks and synchronize with the UI thread (without having to delve into thread transitions, using AsyncTask, etc). - -In EventBus, you may define the thread that will call the event handling method `onEvent` by using a **ThreadMode**: -* **PostThread:** Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers using this mode should return quickly to avoid blocking the posting thread, which may be the main thread. -Example: -```java - // Called in the same thread (default) - public void onEvent(MessageEvent event) { - log(event.message); - } -``` -* **MainThread:** Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly. Event handlers using this mode must return quickly to avoid blocking the main thread. -Example: -```java - // Called in Android UI's main thread - public void onEventMainThread(MessageEvent event) { - textField.setText(event.message); - } -``` -* **BackgroundThread:** Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread. -```java - // Called in the background thread - public void onEventBackgroundThread(MessageEvent event){ - saveToDisk(event.message); - } -``` -* **Async:** Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications. -```java - // Called in a separate thread - public void onEventAsync(MessageEvent event){ - backend.send(event.message); - } -``` - -*Note:* EventBus takes care of calling the `onEvent` method in the proper thread depending on its name (onEvent, onEventAsync, etc.). - -Subscriber priorities and ordered event delivery ------------------------------------------------- -You may change the order of event delivery by providing a priority to the subscriber during registration. - -```java - int priority = 1; - EventBus.getDefault().register(this, priority); -``` - -Within the same delivery thread (ThreadMode), higher priority subscribers will receive events before others with a lower priority. The default priority is 0. - -*Note*: the priority does *NOT* affect the order of delivery among subscribers with different [ThreadModes](#delivery-threads-and-threadmodes)! - -Configure EventBus using EventBusBuilder ----------------------------------------- -EventBus 2.3 added EventBusBuilder to configure various aspects of EventBus. For example, here's how to build an EventBus that keeps quiet in case a posted event has no subscribers: - -```java - EventBus eventBus = EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).build(); -``` - -Another example is to fail when a subscriber throws an exception. Note: by default, EventBus catches exceptions thrown from onEvent methods and sends an SubscriberExceptionEvent that may but do not have to be handled. - -```java - EventBus eventBus = EventBus.builder().throwSubscriberException(true).build(); -``` - -Check the EventBusBuilder class and its JavaDoc for all possible configuration possibilities. - -### Configure the default EventBus instance ### -Using EventBus.getDefault() is a simple way to get an shared EventBus instance. EventBusBuilder also allows to configure this default instance using the method installDefaultEventBus(). - -For example, it's possible to configure the default EventBus instance to rethrow exceptions, which occurred in onEvent methods. But let's to this only for DEBUG builds, because this will likely crash the app on exceptions: - -```java -EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus(); -``` - -Note: this can be done only once before the the default EventBus instance is used the first time. This ensures consistent behavior in your app. Your Application class is a good place to configure the default EventBus instance before its used. - -Cancelling event delivery -------------------------- -You may cancel the event delivery process by calling `cancelEventDelivery(Object event)` from a subscriber's event handling method. -Any further event delivery will be cancelled: subsequent subscribers won't receive the event. -```java - // Called in the same thread (default) - public void onEvent(MessageEvent event){ - // Process the event - ... - - EventBus.getDefault().cancelEventDelivery(event) ; - } -``` - -Events are usually cancelled by higher priority subscribers. Cancelling is restricted to event handling methods running in posting thread [ThreadMode.PostThread](#delivery-threads-and-threadmodes). - -Sticky Events -------------- -Some events carry information that is of interest after the event is posted. For example, this could be an event signalizing that some initialization is complete. Or if you have some sensor or location data and you want to hold on the most recent values. Instead of implementing your own caching, you can use sticky events. EventBus keeps the last sticky event of a certain type in memory. The sticky event can be delivered to subscribers or queried explicitly. Thus, you don't need any special logic to consider already available data. - -Let's say, an sticky event was posted some time ago: -```java - EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!")); -``` - -After that, a new Activity gets started. During registration using registerSticky, it will immediately get the previously posted sticky event: -```java - @Override - public void onStart() { - super.onStart(); - EventBus.getDefault().registerSticky(this); - } - - public void onEventMainThread(MessageEvent event) { - textField.setText(event.message); - } - - @Override - public void onStop() { - EventBus.getDefault().unregister(this); - super.onStop(); - } -``` - -You may also get the last sticky event of a certain type with: -```java - EventBus.getDefault().getStickyEvent(Class eventType) -``` - -It's also possible to remove previously posted sticky events using one of the removeStickyEvent methods. They take either a concrete event object, or an event class. Like this it's possible to create consumable events. Keep in mind though that that only the last event of an event type is kept. - -ProGuard configuration ----------------------- -ProGuard obfuscates method names. However, the onEvent methods must not renamed because they are accessed using reflection. Use the following snip in your ProGuard configuration file (proguard.cfg): -
-keepclassmembers class ** {
-    public void onEvent*(**);
-}
-
-# Only required if you use AsyncExecutor
--keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
-    (java.lang.Throwable);
-}
-
- - -AsyncExecutor -------------- -_Disclaimer:_ AsyncExecutor is a non-core utility class. It might save you some code with error handling in background threads, but it's not a core EventBus class. - -AsyncExecutor is like a thread pool, but with failure handling. Failures are thrown exceptions, which get are wrapped inside an event, which is posted automatically by AsyncExecutor. - -Usually, you call AsyncExecutor.create() to create an instance and keep it in Application scope. To execute something, implement the RunnableEx interface and pass it to the execute method of the AsyncExecutor. Unlike Runnable, RunnableEx may throw an Exception. - -If the RunnableEx implementation throws an exception, it will be catched and wrapped into a ThrowableFailureEvent, which will be posted. - -Code example for execution: - -```java -AsyncExecutor.create().execute( - new RunnableEx { - public void run throws LoginException { - remote.login(); - EventBus.getDefault().postSticky(new LoggedInEvent()); - // No need to catch Exception - } - } -} -``` - -Code example for the receiving part: - -```java -public void onEventMainThread(LoggedInEvent event) { - // Change some UI -} - -public void onEventMainThread(ThrowableFailureEvent event) { - // Show error in UI -} -``` - -AsyncExecutor Builder ---------------------- -If you want to customize your AsyncExecutor instance, call the static method AsyncExecutor.builder(). It will return a builder which lets you customize the EventBus instance, the thread pool, and the class of the failure event. - -Another customization options is the execution scope, which gives failure events context information. For example, an failure event may be relevant only to a specific Activity instance or class. If your custom failure event class implements the HasExecutionScope interface, AsyncExecutor will set the execution scope automatically. Like this, your subscriber can query the failure event for its execution scope and react depending on it. - - -Comparison with Square's Otto ------------------------------ -Check the [COMPARISON.md](COMPARISON.md) \ No newline at end of file diff --git a/README.md b/README.md index e7c6975b..75ecf629 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ EventBus ======== -EventBus is publish/subscribe event bus optimized for Android.
+[EventBus](https://greenrobot.org/eventbus/) is a publish/subscribe event bus for Android and Java.
+[![Build Status](https://github.com/greenrobot/EventBus/actions/workflows/gradle.yml/badge.svg)](https://github.com/greenrobot/EventBus/actions) +[![Follow greenrobot on Twitter](https://img.shields.io/twitter/follow/greenrobot_de.svg?style=flat-square&logo=twitter)](https://twitter.com/greenrobot_de) + EventBus... * simplifies the communication between components @@ -11,75 +14,104 @@ EventBus... * avoids complex and error-prone dependencies and life cycle issues * makes your code simpler * is fast - * is tiny (<50k jar) - * is proven in practice by apps with 100,000,000+ installs + * is tiny (~60k jar) + * is proven in practice by apps with 1,000,000,000+ installs * has advanced features like delivery threads, subscriber priorities, etc. - [![Build Status](https://travis-ci.org/greenrobot/EventBus.svg?branch=master)](https://travis-ci.org/greenrobot/EventBus) - EventBus in 3 steps ------------------- -1. Define events:
-public class MessageEvent { /* Additional fields if needed */ }

-2. Prepare subscribers:
-eventBus.register(this);
-public void onEvent(AnyEventType event) {/* Do something */};

-3. Post events:
-eventBus.post(event); +1. Define events: + + ```java + public static class MessageEvent { /* Additional fields if needed */ } + ``` + +2. Prepare subscribers: + Declare and annotate your subscribing method, optionally specify a [thread mode](https://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/): + + ```java + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageEvent(MessageEvent event) { + // Do something + } + ``` + Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle: + + ```java + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); + } + ``` + +3. Post events: + + ```java + EventBus.getDefault().post(new MessageEvent()); + ``` + +Read the full [getting started guide](https://greenrobot.org/eventbus/documentation/how-to-get-started/). + +There are also some [examples](https://github.com/greenrobot-team/greenrobot-examples). + +**Note:** we highly recommend the [EventBus annotation processor with its subscriber index](https://greenrobot.org/eventbus/documentation/subscriber-index/). +This will avoid some reflection related problems seen in the wild. Add EventBus to your project ---------------------------- -EventBus is available on Maven Central. Please ensure that you are using the latest version by [checking here](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.greenrobot%22%20AND%20a%3A%22eventbus%22) + -Gradle: -``` - compile 'de.greenrobot:eventbus:2.4.0' +Available on Maven Central. + +Android projects: +```groovy +implementation("org.greenrobot:eventbus:3.3.1") ``` -Maven: +Java projects: +```groovy +implementation("org.greenrobot:eventbus-java:3.3.1") ``` +```xml - de.greenrobot - eventbus - 2.4.0 + org.greenrobot + eventbus-java + 3.3.1 ``` -[Or download EventBus from Maven Central](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.greenrobot%22%20AND%20a%3A%22eventbus%22) +R8, ProGuard +------------ -How-to, Developer Documentation -------------------------------- -Details on EventBus and its API are available in the [HOWTO document](HOWTO.md). +If your project uses R8 or ProGuard this library ships [with embedded rules](/eventbus-android/consumer-rules.pro). -How does EventBus compare to other solutions, like Otto from Square? Check this [comparison](COMPARISON.md). +Homepage, Documentation, Links +------------------------------ +For more details please check the [EventBus website](https://greenrobot.org/eventbus). Here are some direct links you may find useful: -Additional Features and Notes ------------------------------ +[Features](https://greenrobot.org/eventbus/features/) -* **NOT based on annotations:** Querying annotations are slow on Android, especially before Android 4.0. Have a look at this [Android bug report](http://code.google.com/p/android/issues/detail?id=7811). -* **Based on conventions:** Event handling methods are called "onEvent". -* **Performance optimized:** It's probably the fastest event bus for Android. -* **Convenience singleton:** You can get a process wide event bus instance by calling EventBus.getDefault(). You can still call new EventBus() to create any number of local busses. -* **Subscriber and event inheritance:** Event handler methods may be defined in super classes, and events are posted to handlers of the event's super classes including any implemented interfaces. For example, subscriber may register to events of the type Object to receive all events posted on the event bus. +[Documentation](https://greenrobot.org/eventbus/documentation/) -FAQ ---- -**Q:** How is EventBus different to Android's BroadcastReceiver/Intent system?
-**A:** Unlike Android's BroadcastReceiver/Intent system, EventBus uses standard Java classes as events and offers a more convenient API. EventBus is intended for a lot more uses cases where you wouldn't want to go through the hassle of setting up Intents, preparing Intent extras, implementing broadcast receivers, and extracting Intent extras again. Also, EventBus comes with a much lower overhead. +[Changelog](https://github.com/greenrobot/EventBus/releases) - **Q:** How to do pull requests?
- **A:** Ensure good code quality and consistent formatting. EventBus has a good test coverage: if you propose a new feature or fix a bug, please add a unit test. +[FAQ](https://greenrobot.org/eventbus/documentation/faq/) -Release History, License ------------------------- -[CHANGELOG](CHANGELOG.md) +License +------- +Copyright (C) 2012-2021 Markus Junginger, greenrobot (https://greenrobot.org) EventBus binaries and source code can be used according to the [Apache License, Version 2.0](LICENSE). -More Open Source by greenrobot -============================== -[__greenrobot-common__](https://github.com/greenrobot/greenrobot-common) is a set of utility classes and hash functions for Android & Java projects. - -[__greenDAO__](https://github.com/greenrobot/greenDAO) is an ORM optimized for Android: it maps database tables to Java objects and uses code generation for optimal speed. +Other projects by greenrobot +============================ +[__ObjectBox__](https://objectbox.io/) ([GitHub](https://github.com/objectbox/objectbox-java)) is a new superfast object-oriented database. -[Follow us on Google+](https://plus.google.com/b/114381455741141514652/+GreenrobotDe/posts) to stay up to date. \ No newline at end of file +[__Essentials__](https://github.com/greenrobot/essentials) is a set of utility classes and hash functions for Android & Java projects. diff --git a/build.gradle b/build.gradle index e69de29b..aaeccfeb 100644 --- a/build.gradle +++ b/build.gradle @@ -0,0 +1,52 @@ +buildscript { + ext { + _compileSdkVersion = 30 // Android 11 (R) + } + repositories { + mavenCentral() + maven { url "https://plugins.gradle.org/m2/" } + } + dependencies { + classpath "io.github.gradle-nexus:publish-plugin:1.1.0" + } +} + +// Set group and version in root build.gradle so publish-plugin can detect them. +group = "org.greenrobot" +version = "3.3.1" + +allprojects { + repositories { + mavenCentral() + google() + } +} + +if (JavaVersion.current().isJava8Compatible()) { + allprojects { + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + } +} + +wrapper { + distributionType = Wrapper.DistributionType.ALL +} + +// Plugin to publish to Central https://github.com/gradle-nexus/publish-plugin/ +// This plugin ensures a separate, named staging repo is created for each build when publishing. +apply plugin: "io.github.gradle-nexus.publish-plugin" +nexusPublishing { + repositories { + sonatype { + if (project.hasProperty("sonatypeUsername") && project.hasProperty("sonatypePassword")) { + println('nexusPublishing credentials supplied.') + username = sonatypeUsername + password = sonatypePassword + } else { + println('nexusPublishing credentials NOT supplied.') + } + } + } +} diff --git a/eventbus-android/.gitignore b/eventbus-android/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/eventbus-android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/eventbus-android/README.md b/eventbus-android/README.md new file mode 100644 index 00000000..126c7ba9 --- /dev/null +++ b/eventbus-android/README.md @@ -0,0 +1,7 @@ +# EventBus for Android + +Despite its name this module is actually published as `org.greenrobot:eventbus` as an Android library (AAR). + +It has a dependency on the Java-only artifact `org.greenrobot:eventbus-java` (JAR) previously available under the `eventbus` name. + +Provides an `AndroidComponents` implementation to the Java library if it detects `AndroidComponentsImpl` on the classpath via reflection. diff --git a/eventbus-android/build.gradle b/eventbus-android/build.gradle new file mode 100644 index 00000000..c1992ce4 --- /dev/null +++ b/eventbus-android/build.gradle @@ -0,0 +1,62 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + // Note: IntelliJ IDEA 2021.1 only supports up to version 4.1 + classpath 'com.android.tools.build:gradle:4.1.3' + } +} + +apply plugin: 'com.android.library' + +group = rootProject.group +version = rootProject.version + +android { + compileSdkVersion _compileSdkVersion + + defaultConfig { + minSdkVersion 7 + targetSdkVersion 30 // Android 11 (R) + + consumerProguardFiles "consumer-rules.pro" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + api project(":eventbus-java") +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + archiveClassifier.set("sources") +} + +apply from: rootProject.file("gradle/publish.gradle") +// Set project-specific properties +// https://developer.android.com/studio/build/maven-publish-plugin +// Because the Android components are created only during the afterEvaluate phase, you must +// configure your publications using the afterEvaluate() lifecycle method. +afterEvaluate { + publishing.publications { + mavenJava(MavenPublication) { + artifactId = "eventbus" + + from components.release + artifact sourcesJar + + pom { + name = "EventBus" + description = "EventBus is a publish/subscribe event bus optimized for Android." + packaging = "aar" + } + } + } +} diff --git a/eventbus-android/consumer-rules.pro b/eventbus-android/consumer-rules.pro new file mode 100644 index 00000000..4646fb1e --- /dev/null +++ b/eventbus-android/consumer-rules.pro @@ -0,0 +1,14 @@ +-keepattributes *Annotation* +-keepclassmembers class * { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode { *; } + +# If using AsyncExecutord, keep required constructor of default event used. +# Adjust the class name if a custom failure event type is used. +-keepclassmembers class org.greenrobot.eventbus.util.ThrowableFailureEvent { + (java.lang.Throwable); +} + +# Accessed via reflection, avoid renaming or removal +-keep class org.greenrobot.eventbus.android.AndroidComponentsImpl diff --git a/eventbus-android/proguard-rules.pro b/eventbus-android/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/eventbus-android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/eventbus-android/src/main/AndroidManifest.xml b/eventbus-android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e4f0b52b --- /dev/null +++ b/eventbus-android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/EventBus/src/de/greenrobot/event/HandlerPoster.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java similarity index 88% rename from EventBus/src/de/greenrobot/event/HandlerPoster.java rename to eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java index c1c44608..71158e2d 100644 --- a/EventBus/src/de/greenrobot/event/HandlerPoster.java +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Markus Junginger, greenrobot (http://greenrobot.de) + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,28 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.greenrobot.event; +package org.greenrobot.eventbus; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; -final class HandlerPoster extends Handler { +public class HandlerPoster extends Handler implements Poster { private final PendingPostQueue queue; private final int maxMillisInsideHandleMessage; private final EventBus eventBus; private boolean handlerActive; - HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { + public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.eventBus = eventBus; this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; queue = new PendingPostQueue(); } - void enqueue(Subscription subscription, Object event) { + public void enqueue(Subscription subscription, Object event) { PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event); synchronized (this) { queue.enqueue(pendingPost); diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java new file mode 100644 index 00000000..20f35b18 --- /dev/null +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidComponentsImpl.java @@ -0,0 +1,11 @@ +package org.greenrobot.eventbus.android; + +/** + * Used via reflection in the Java library by {@link AndroidDependenciesDetector}. + */ +public class AndroidComponentsImpl extends AndroidComponents { + + public AndroidComponentsImpl() { + super(new AndroidLogger("EventBus"), new DefaultAndroidMainThreadSupport()); + } +} diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java new file mode 100644 index 00000000..04cc1cf3 --- /dev/null +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/AndroidLogger.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.greenrobot.eventbus.android; + +import android.util.Log; +import org.greenrobot.eventbus.Logger; + +import java.util.logging.Level; + +public class AndroidLogger implements Logger { + + private final String tag; + + public AndroidLogger(String tag) { + this.tag = tag; + } + + public void log(Level level, String msg) { + if (level != Level.OFF) { + Log.println(mapLevel(level), tag, msg); + } + } + + public void log(Level level, String msg, Throwable th) { + if (level != Level.OFF) { + // That's how Log does it internally + Log.println(mapLevel(level), tag, msg + "\n" + Log.getStackTraceString(th)); + } + } + + private int mapLevel(Level level) { + int value = level.intValue(); + if (value < 800) { // below INFO + if (value < 500) { // below FINE + return Log.VERBOSE; + } else { + return Log.DEBUG; + } + } else if (value < 900) { // below WARNING + return Log.INFO; + } else if (value < 1000) { // below ERROR + return Log.WARN; + } else { + return Log.ERROR; + } + } +} diff --git a/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java new file mode 100644 index 00000000..02b3f3af --- /dev/null +++ b/eventbus-android/src/main/java/org/greenrobot/eventbus/android/DefaultAndroidMainThreadSupport.java @@ -0,0 +1,20 @@ +package org.greenrobot.eventbus.android; + +import android.os.Looper; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.HandlerPoster; +import org.greenrobot.eventbus.MainThreadSupport; +import org.greenrobot.eventbus.Poster; + +public class DefaultAndroidMainThreadSupport implements MainThreadSupport { + + @Override + public boolean isMainThread() { + return Looper.getMainLooper() == Looper.myLooper(); + } + + @Override + public Poster createPoster(EventBus eventBus) { + return new HandlerPoster(eventBus, Looper.getMainLooper(), 10); + } +} diff --git a/gradle/publish.gradle b/gradle/publish.gradle new file mode 100644 index 00000000..1206dd62 --- /dev/null +++ b/gradle/publish.gradle @@ -0,0 +1,81 @@ +// Configures common publishing settings. + +apply plugin: "maven-publish" +apply plugin: "signing" + +publishing { + publications { + // Note: Sonatype repo created by publish-plugin, see root build.gradle. + mavenJava(MavenPublication) { + pom { + url = "https://greenrobot.org/eventbus/" + + scm { + connection = "scm:git@github.com:greenrobot/EventBus.git" + developerConnection = "scm:git@github.com:greenrobot/EventBus.git" + url = "https://github.com/greenrobot/EventBus" + } + + licenses { + license { + name = "The Apache Software License, Version 2.0" + url = "https://www.apache.org/licenses/LICENSE-2.0.txt" + distribution = "repo" + } + } + + developers { + developer { + id = "greenrobot" + name = "greenrobot" + } + } + + issueManagement { + system = "https://github.com/greenrobot/EventBus/issues" + url = "https://github.com/greenrobot/EventBus/issues" + } + + organization { + name = "greenrobot" + url = "https://greenrobot.org" + } + } + } + } +} + +// Note: ext to export to scripts applying this script. +ext { + // Signing: in-memory ascii-armored key (CI) or keyring file (dev machine), see https://docs.gradle.org/current/userguide/signing_plugin.html + hasSigningPropertiesKeyFile = { + return (project.hasProperty('signingKeyId') + && project.hasProperty('signingKeyFile') + && project.hasProperty('signingPassword')) + } + // Typically via ~/.gradle/gradle.properties; default properties for signing plugin. + hasSigningPropertiesKeyRing = { + return (project.hasProperty('signing.keyId') + && project.hasProperty('signing.secretKeyRingFile') + && project.hasProperty('signing.password')) + } + hasSigningProperties = { + return hasSigningPropertiesKeyFile() || hasSigningPropertiesKeyRing() + } +} + +signing { + if (hasSigningProperties()) { + if (hasSigningPropertiesKeyFile()) { + println "Configured signing to use key file." + String signingKey = new File(signingKeyFile).text + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + } else if (hasSigningPropertiesKeyRing()) { + println "Configured signing to use key ring." + // Note: using expected property names (see above), no need to configure anything. + } + sign publishing.publications.mavenJava + } else { + println "WARNING: signing properties NOT set, will not sign artifacts." + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3d0dee6e..e708b1c0 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2583d2d1..8cf6eb5a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat Nov 08 10:08:52 CET 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 91a7e269..4f906e0c --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,33 +75,14 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -90,7 +106,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -110,11 +126,13 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" @@ -138,27 +156,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282a..ac1b06f9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,90 +1,89 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/javadoc-style/background.gif b/javadoc-style/background.gif new file mode 100644 index 00000000..ec068a06 Binary files /dev/null and b/javadoc-style/background.gif differ diff --git a/javadoc-style/stylesheet.css b/javadoc-style/stylesheet.css new file mode 100644 index 00000000..c12603af --- /dev/null +++ b/javadoc-style/stylesheet.css @@ -0,0 +1,574 @@ +/* Javadoc style sheet */ +/* +Overall document style +*/ + +@import url('resources/fonts/dejavu.css'); + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a:hover, a:focus { + text-decoration:none; + color:#bb7a2a; +} +a:active { + text-decoration:none; + color:#4A6782; +} +a[name] { + color:#353833; +} +a[name]:hover { + text-decoration:none; + color:#353833; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#4D974D; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.topNav { + background-color:#4D974D; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D974D; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.subNavList li{ + list-style:none; + float:left; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader span{ + margin-right:15px; +} +.indexHeader h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Sans Mono',monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#F8981D; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#4D974D; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#F8981D; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#4D974D; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; + width:100%; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor { + background-color:#FFFFFF; +} +.rowColor { + background-color:#EEEEEF; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} + +td.colLast div { + padding-top:0px; +} + + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +div.contentContainer ul.blockList li.blockList h2{ + padding-bottom:0px; +} diff --git a/settings.gradle b/settings.gradle index 76c570b0..9e53b0e9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,10 @@ -include 'EventBus' -include 'EventBusTest' -include 'EventBusPerformance' +include ':EventBus' +include ':EventBusAnnotationProcessor' +include ':EventBusTestJava' +include ':EventBusTest' +include ':EventBusTestSubscriberInJar' +include ':EventBusPerformance' +include ':eventbus-android' + +project(":EventBus").name = "eventbus-java" +project(":EventBusAnnotationProcessor").name = "eventbus-annotation-processor"