genericSubscriber = new GenericNumberEventSubscriber<>();
70 | eventBus.register(genericSubscriber);
71 | eventBus.post(new Float(42));
72 | eventBus.post(new Double(23));
73 | assertEventCount(2);
74 | eventBus.post("Not the same base type");
75 | assertEventCount(2);
76 | }
77 |
78 | @Test
79 | public void testGenericEventAndSubscriber_Subclass() {
80 | GenericFloatEventSubscriber genericSubscriber = new GenericFloatEventSubscriber();
81 | eventBus.register(genericSubscriber);
82 | eventBus.post(new Float(42));
83 | eventBus.post(new Double(77));
84 | assertEventCount(2);
85 | eventBus.post("Not the same base type");
86 | assertEventCount(2);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | import android.os.Handler;
19 | import android.os.Looper;
20 | import android.os.Message;
21 | import android.os.SystemClock;
22 |
23 | public class HandlerPoster extends Handler implements Poster {
24 |
25 | private final PendingPostQueue queue;
26 | private final int maxMillisInsideHandleMessage;
27 | private final EventBus eventBus;
28 | private boolean handlerActive;
29 |
30 | public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
31 | super(looper);
32 | this.eventBus = eventBus;
33 | this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
34 | queue = new PendingPostQueue();
35 | }
36 |
37 | public void enqueue(Subscription subscription, Object event) {
38 | PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
39 | synchronized (this) {
40 | queue.enqueue(pendingPost);
41 | if (!handlerActive) {
42 | handlerActive = true;
43 | if (!sendMessage(obtainMessage())) {
44 | throw new EventBusException("Could not send handler message");
45 | }
46 | }
47 | }
48 | }
49 |
50 | @Override
51 | public void handleMessage(Message msg) {
52 | boolean rescheduled = false;
53 | try {
54 | long started = SystemClock.uptimeMillis();
55 | while (true) {
56 | PendingPost pendingPost = queue.poll();
57 | if (pendingPost == null) {
58 | synchronized (this) {
59 | // Check again, this time in synchronized
60 | pendingPost = queue.poll();
61 | if (pendingPost == null) {
62 | handlerActive = false;
63 | return;
64 | }
65 | }
66 | }
67 | eventBus.invokeSubscriber(pendingPost);
68 | long timeInMethod = SystemClock.uptimeMillis() - started;
69 | if (timeInMethod >= maxMillisInsideHandleMessage) {
70 | if (!sendMessage(obtainMessage())) {
71 | throw new EventBusException("Could not send handler message");
72 | }
73 | rescheduled = true;
74 | return;
75 | }
76 | }
77 | } finally {
78 | handlerActive = rescheduled;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/COMPARISON.md:
--------------------------------------------------------------------------------
1 | EventBus Comparison
2 | ===================
3 |
4 | Comparison with Square's Otto
5 | -----------------------------
6 | Otto is another event bus library for Android; actually it's a fork of Guava's EventBus. greenrobot's EventBus and Otto share some basic semantics (register, post, unregister, ...), but there are differences which the following table summarizes:
7 |
8 |
9 | |
10 | EventBus |
11 | Otto |
12 |
13 |
14 | | Declare event handling methods |
15 | Annotations (since 3.0, can be precompiled for best performance) |
16 | Annotations |
17 |
18 |
19 | | Event inheritance |
20 | Yes |
21 | Yes |
22 |
23 |
24 | | Subscriber inheritance |
25 | Yes |
26 | No |
27 |
28 |
29 | | Cache most recent events |
30 | Yes, sticky events |
31 | No |
32 |
33 |
34 | | Event producers (e.g. for coding cached events) |
35 | No |
36 | Yes |
37 |
38 |
39 | | Event delivery in posting thread |
40 | Yes (Default) |
41 | Yes |
42 |
43 |
44 | | Event delivery in main thread |
45 | Yes |
46 | No |
47 |
48 |
49 | | Event delivery in background thread |
50 | Yes |
51 | No |
52 |
53 |
54 | | Asynchronous event delivery |
55 | Yes |
56 | No |
57 |
58 |
59 |
60 | _**Note:** the following information is outdated, preprocessed annotations are much faster than EventBus 2.x, on which the following table is based._
61 |
62 | 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.
63 |
64 | TODO: Update for EventBus 3 with and without index.
65 |
66 | Benchmark results indicate that EventBus is significantly faster in almost every scenario:
67 |
68 |
69 | |
70 | EventBus |
71 | Otto |
72 |
73 |
74 | | Posting 1000 events, Android 2.3 emulator |
75 | ~70% faster |
76 | |
77 |
78 |
79 | | Posting 1000 events, S3 Android 4.0 |
80 | ~110% faster |
81 | |
82 |
83 |
84 | | Register 1000 subscribers, Android 2.3 emulator |
85 | ~10% faster |
86 | |
87 |
88 |
89 | | Register 1000 subscribers, S3 Android 4.0 |
90 | ~70% faster |
91 | |
92 |
93 |
94 | | Register subscribers cold start, Android 2.3 emulator |
95 | ~350% faster |
96 | |
97 |
98 |
99 | | Register subscribers cold start, S3 Android 4.0 |
100 | About the same |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/EventBus/src/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus.meta;
17 |
18 | import org.greenrobot.eventbus.EventBusException;
19 | import org.greenrobot.eventbus.SubscriberMethod;
20 | import org.greenrobot.eventbus.ThreadMode;
21 |
22 | import java.lang.reflect.Method;
23 |
24 | /** Base class for generated subscriber meta info classes created by annotation processing. */
25 | public abstract class AbstractSubscriberInfo implements SubscriberInfo {
26 | private final Class subscriberClass;
27 | private final Class extends SubscriberInfo> superSubscriberInfoClass;
28 | private final boolean shouldCheckSuperclass;
29 |
30 | protected AbstractSubscriberInfo(Class subscriberClass, Class extends SubscriberInfo> superSubscriberInfoClass,
31 | boolean shouldCheckSuperclass) {
32 | this.subscriberClass = subscriberClass;
33 | this.superSubscriberInfoClass = superSubscriberInfoClass;
34 | this.shouldCheckSuperclass = shouldCheckSuperclass;
35 | }
36 |
37 | @Override
38 | public Class getSubscriberClass() {
39 | return subscriberClass;
40 | }
41 |
42 | @Override
43 | public SubscriberInfo getSuperSubscriberInfo() {
44 | if(superSubscriberInfoClass == null) {
45 | return null;
46 | }
47 | try {
48 | return superSubscriberInfoClass.newInstance();
49 | } catch (InstantiationException e) {
50 | throw new RuntimeException(e);
51 | } catch (IllegalAccessException e) {
52 | throw new RuntimeException(e);
53 | }
54 | }
55 |
56 | @Override
57 | public boolean shouldCheckSuperclass() {
58 | return shouldCheckSuperclass;
59 | }
60 |
61 | protected SubscriberMethod createSubscriberMethod(String methodName, Class> eventType) {
62 | return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false);
63 | }
64 |
65 | protected SubscriberMethod createSubscriberMethod(String methodName, Class> eventType, ThreadMode threadMode) {
66 | return createSubscriberMethod(methodName, eventType, threadMode, 0, false);
67 | }
68 |
69 | protected SubscriberMethod createSubscriberMethod(String methodName, Class> eventType, ThreadMode threadMode,
70 | int priority, boolean sticky) {
71 | try {
72 | Method method = subscriberClass.getDeclaredMethod(methodName, eventType);
73 | return new SubscriberMethod(method, eventType, threadMode, priority, sticky);
74 | } catch (NoSuchMethodException e) {
75 | throw new EventBusException("Could not find subscriber method in " + subscriberClass +
76 | ". Maybe a missing ProGuard rule?", e);
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/EventBus/src/org/greenrobot/eventbus/ThreadMode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | /**
19 | * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus.
20 | * EventBus takes care of threading independently of the posting thread.
21 | *
22 | * @see EventBus#register(Object)
23 | */
24 | public enum ThreadMode {
25 | /**
26 | * This is the default. Subscriber will be called directly in the same thread, which is posting the event. Event delivery
27 | * implies the least overhead because it avoids thread switching completely. Thus, this is the recommended mode for
28 | * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
29 | * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
30 | */
31 | POSTING,
32 |
33 | /**
34 | * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
35 | * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
36 | * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
37 | *
38 | * If not on Android, behaves the same as {@link #POSTING}.
39 | */
40 | MAIN,
41 |
42 | /**
43 | * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
44 | * the event will always be queued for delivery. This ensures that the post call is non-blocking.
45 | *
46 | * If not on Android, behaves the same as {@link #POSTING}.
47 | */
48 | MAIN_ORDERED,
49 |
50 | /**
51 | * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
52 | * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
53 | * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
54 | * return quickly to avoid blocking the background thread.
55 | *
56 | * If not on Android, always uses a background thread.
57 | */
58 | BACKGROUND,
59 |
60 | /**
61 | * Subscriber will be called in a separate thread. This is always independent of the posting thread and the
62 | * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
63 | * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
64 | * of long-running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
65 | * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
66 | */
67 | ASYNC
68 | }
--------------------------------------------------------------------------------
/EventBus/src/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2020 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.greenrobot.eventbus.util;
18 |
19 | import org.greenrobot.eventbus.Logger;
20 |
21 | import java.util.HashMap;
22 | import java.util.Map;
23 | import java.util.Map.Entry;
24 | import java.util.Set;
25 | import java.util.logging.Level;
26 |
27 |
28 | /**
29 | * Maps throwables to texts for error dialogs. Use Config to configure the mapping.
30 | *
31 | * @author Markus
32 | */
33 | public class ExceptionToResourceMapping {
34 |
35 | public final Map, Integer> throwableToMsgIdMap;
36 |
37 | public ExceptionToResourceMapping() {
38 | throwableToMsgIdMap = new HashMap<>();
39 | }
40 |
41 | /** Looks at the exception and its causes trying to find an ID. */
42 | public Integer mapThrowable(final Throwable throwable) {
43 | Throwable throwableToCheck = throwable;
44 | int depthToGo = 20;
45 |
46 | while (true) {
47 | Integer resId = mapThrowableFlat(throwableToCheck);
48 | if (resId != null) {
49 | return resId;
50 | } else {
51 | throwableToCheck = throwableToCheck.getCause();
52 | depthToGo--;
53 | if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) {
54 | Logger logger = Logger.Default.get(); // No EventBus instance here
55 | logger.log(Level.FINE, "No specific message resource ID found for " + throwable);
56 | // return config.defaultErrorMsgId;
57 | return null;
58 | }
59 | }
60 | }
61 |
62 | }
63 |
64 | /** Mapping without checking the cause (done in mapThrowable). */
65 | protected Integer mapThrowableFlat(Throwable throwable) {
66 | Class extends Throwable> throwableClass = throwable.getClass();
67 | Integer resId = throwableToMsgIdMap.get(throwableClass);
68 | if (resId == null) {
69 | Class extends Throwable> closestClass = null;
70 | Set, Integer>> mappings = throwableToMsgIdMap.entrySet();
71 | for (Entry, Integer> mapping : mappings) {
72 | Class extends Throwable> candidate = mapping.getKey();
73 | if (candidate.isAssignableFrom(throwableClass)) {
74 | if (closestClass == null || closestClass.isAssignableFrom(candidate)) {
75 | closestClass = candidate;
76 | resId = mapping.getValue();
77 | }
78 | }
79 | }
80 |
81 | }
82 | return resId;
83 | }
84 |
85 | public ExceptionToResourceMapping addMapping(Class extends Throwable> clazz, int msgId) {
86 | throwableToMsgIdMap.put(clazz, msgId);
87 | return this;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusBuilderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | import org.junit.Assert;
19 | import org.junit.Test;
20 |
21 | import static org.junit.Assert.fail;
22 |
23 | /**
24 | * @author Markus Junginger, greenrobot
25 | */
26 | public class EventBusBuilderTest extends AbstractEventBusTest {
27 |
28 | @Test
29 | public void testThrowSubscriberException() {
30 | eventBus = EventBus.builder().throwSubscriberException(true).build();
31 | eventBus.register(new SubscriberExceptionEventTracker());
32 | eventBus.register(new ThrowingSubscriber());
33 | try {
34 | eventBus.post("Foo");
35 | fail("Should have thrown");
36 | } catch (EventBusException e) {
37 | // Expected
38 | }
39 | }
40 |
41 | @Test
42 | public void testDoNotSendSubscriberExceptionEvent() {
43 | eventBus = EventBus.builder().logSubscriberExceptions(false).sendSubscriberExceptionEvent(false).build();
44 | eventBus.register(new SubscriberExceptionEventTracker());
45 | eventBus.register(new ThrowingSubscriber());
46 | eventBus.post("Foo");
47 | assertEventCount(0);
48 | }
49 |
50 | @Test
51 | public void testDoNotSendNoSubscriberEvent() {
52 | eventBus = EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).build();
53 | eventBus.register(new NoSubscriberEventTracker());
54 | eventBus.post("Foo");
55 | assertEventCount(0);
56 | }
57 |
58 | @Test
59 | public void testInstallDefaultEventBus() {
60 | EventBusBuilder builder = EventBus.builder();
61 | try {
62 | // Either this should throw when another unit test got the default event bus...
63 | eventBus = builder.installDefaultEventBus();
64 | Assert.assertEquals(eventBus, EventBus.getDefault());
65 |
66 | // ...or this should throw
67 | eventBus = builder.installDefaultEventBus();
68 | fail("Should have thrown");
69 | } catch (EventBusException e) {
70 | // Expected
71 | }
72 | }
73 |
74 | @Test
75 | public void testEventInheritance() {
76 | eventBus = EventBus.builder().eventInheritance(false).build();
77 | eventBus.register(new ThrowingSubscriber());
78 | eventBus.post("Foo");
79 | }
80 |
81 | public class SubscriberExceptionEventTracker {
82 | @Subscribe
83 | public void onEvent(SubscriberExceptionEvent event) {
84 | trackEvent(event);
85 | }
86 | }
87 |
88 | public class NoSubscriberEventTracker {
89 | @Subscribe
90 | public void onEvent(NoSubscriberEvent event) {
91 | trackEvent(event);
92 | }
93 | }
94 |
95 | public class ThrowingSubscriber {
96 | @Subscribe
97 | public void onEvent(Object event) {
98 | throw new RuntimeException();
99 | }
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/EventBusTest/src/org/greenrobot/eventbus/EventBusAndroidMultithreadedTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | import android.os.Looper;
22 | import android.support.test.runner.AndroidJUnit4;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | import static org.junit.Assert.assertNotSame;
28 | import static org.junit.Assert.assertSame;
29 |
30 | @RunWith(AndroidJUnit4.class)
31 | public class EventBusAndroidMultithreadedTest extends EventBusMultithreadedTest {
32 |
33 | @Test
34 | public void testSubscribeUnSubscribeAndPostMixedEventType() throws InterruptedException {
35 | List threads = new ArrayList();
36 |
37 | // Debug.startMethodTracing("testSubscribeUnSubscribeAndPostMixedEventType");
38 | for (int i = 0; i < 5; i++) {
39 | SubscribeUnsubscribeThread thread = new SubscribeUnsubscribeThread();
40 | thread.start();
41 | threads.add(thread);
42 | }
43 | // This test takes a bit longer, so just use fraction the regular count
44 | runThreadsMixedEventType(COUNT / 4, 5);
45 | for (SubscribeUnsubscribeThread thread : threads) {
46 | thread.shutdown();
47 | }
48 | for (SubscribeUnsubscribeThread thread : threads) {
49 | thread.join();
50 | }
51 | // Debug.stopMethodTracing();
52 | }
53 |
54 | public class SubscribeUnsubscribeThread extends Thread {
55 | boolean running = true;
56 |
57 | public void shutdown() {
58 | running = false;
59 | }
60 |
61 | @Override
62 | public void run() {
63 | try {
64 | while (running) {
65 | eventBus.register(this);
66 | double random = Math.random();
67 | if (random > 0.6d) {
68 | Thread.sleep(0, (int) (1000000 * Math.random()));
69 | } else if (random > 0.3d) {
70 | Thread.yield();
71 | }
72 | eventBus.unregister(this);
73 | }
74 | } catch (InterruptedException e) {
75 | throw new RuntimeException(e);
76 | }
77 | }
78 |
79 | @Subscribe(threadMode = ThreadMode.MAIN)
80 | public void onEventMainThread(String event) {
81 | assertSame(Looper.getMainLooper(), Looper.myLooper());
82 | }
83 |
84 | @Subscribe(threadMode = ThreadMode.BACKGROUND)
85 | public void onEventBackgroundThread(Integer event) {
86 | assertNotSame(Looper.getMainLooper(), Looper.myLooper());
87 | }
88 |
89 | @Subscribe
90 | public void onEvent(Object event) {
91 | assertNotSame(Looper.getMainLooper(), Looper.myLooper());
92 | }
93 |
94 | @Subscribe(threadMode = ThreadMode.ASYNC)
95 | public void onEventAsync(Object event) {
96 | assertNotSame(Looper.getMainLooper(), Looper.myLooper());
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/EventBusTestJava/src/main/java/org/greenrobot/eventbus/EventBusRegistrationRacingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | import org.junit.Test;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 | import java.util.concurrent.CountDownLatch;
23 | import java.util.concurrent.Executor;
24 | import java.util.concurrent.Executors;
25 |
26 | import static org.junit.Assert.fail;
27 |
28 | /**
29 | * @author Markus Junginger, greenrobot
30 | */
31 | public class EventBusRegistrationRacingTest extends AbstractEventBusTest {
32 |
33 | // On a Nexus 5, bad synchronization always failed on the first iteration or went well completely.
34 | // So a high number of iterations do not guarantee a better probability of finding bugs.
35 | private static final int ITERATIONS = LONG_TESTS ? 1000 : 10;
36 | private static final int THREAD_COUNT = 16;
37 |
38 | volatile CountDownLatch startLatch;
39 | volatile CountDownLatch registeredLatch;
40 | volatile CountDownLatch canUnregisterLatch;
41 | volatile CountDownLatch unregisteredLatch;
42 |
43 | final Executor threadPool = Executors.newCachedThreadPool();
44 |
45 | @Test
46 | public void testRacingRegistrations() throws InterruptedException {
47 | for (int i = 0; i < ITERATIONS; i++) {
48 | startLatch = new CountDownLatch(THREAD_COUNT);
49 | registeredLatch = new CountDownLatch(THREAD_COUNT);
50 | canUnregisterLatch = new CountDownLatch(1);
51 | unregisteredLatch = new CountDownLatch(THREAD_COUNT);
52 |
53 | List threads = startThreads();
54 | registeredLatch.await();
55 | eventBus.post("42");
56 | canUnregisterLatch.countDown();
57 | for (int t = 0; t < THREAD_COUNT; t++) {
58 | int eventCount = threads.get(t).eventCount;
59 | if (eventCount != 1) {
60 | fail("Failed in iteration " + i + ": thread #" + t + " has event count of " + eventCount);
61 | }
62 | }
63 | // Wait for threads to be done
64 | unregisteredLatch.await();
65 | }
66 | }
67 |
68 | private List startThreads() {
69 | List threads = new ArrayList(THREAD_COUNT);
70 | for (int i = 0; i < THREAD_COUNT; i++) {
71 | SubscriberThread thread = new SubscriberThread();
72 | threadPool.execute(thread);
73 | threads.add(thread);
74 | }
75 | return threads;
76 | }
77 |
78 | public class SubscriberThread implements Runnable {
79 | volatile int eventCount;
80 |
81 | @Override
82 | public void run() {
83 | countDownAndAwaitLatch(startLatch, 10);
84 | eventBus.register(this);
85 | registeredLatch.countDown();
86 | try {
87 | canUnregisterLatch.await();
88 | } catch (InterruptedException e) {
89 | throw new RuntimeException(e);
90 | }
91 | eventBus.unregister(this);
92 | unregisteredLatch.countDown();
93 | }
94 |
95 | @Subscribe
96 | public void onEvent(String event) {
97 | eventCount++;
98 | }
99 |
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/EventBusPerformance/src/org/greenrobot/eventbusperf/TestRunnerActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.greenrobot.eventbusperf;
18 |
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.os.Process;
22 | import android.text.Html;
23 | import android.view.View;
24 | import android.widget.TextView;
25 |
26 | import org.greenrobot.eventbus.EventBus;
27 | import org.greenrobot.eventbus.Subscribe;
28 | import org.greenrobot.eventbus.ThreadMode;
29 |
30 | /**
31 | * This activity gets the information from the activity before, sets up the test and starts the test. After it watchs
32 | * after that, if a test is finished. When a test is finished, the activity appends it on the textview analyse. If all
33 | * test are finished, it cancels the timer.
34 | */
35 | public class TestRunnerActivity extends Activity {
36 |
37 | private TestRunner testRunner;
38 | private EventBus controlBus;
39 | private TextView textViewResult;
40 |
41 | @Override
42 | public void onCreate(Bundle savedInstanceState) {
43 | super.onCreate(savedInstanceState);
44 | setContentView(R.layout.activity_runtests);
45 | textViewResult = findViewById(R.id.textViewResult);
46 | controlBus = new EventBus();
47 | controlBus.register(this);
48 | }
49 |
50 | @Override
51 | protected void onResume() {
52 | super.onResume();
53 | if (testRunner == null) {
54 | TestParams testParams = (TestParams) getIntent().getSerializableExtra("params");
55 | testRunner = new TestRunner(getApplicationContext(), testParams, controlBus);
56 |
57 | if (testParams.getTestNumber() == 1) {
58 | textViewResult.append("Events: " + testParams.getEventCount() + "\n");
59 | }
60 | textViewResult.append("Subscribers: " + testParams.getSubscriberCount() + "\n\n");
61 | testRunner.start();
62 | }
63 | }
64 |
65 | @Subscribe(threadMode = ThreadMode.MAIN)
66 | public void onEventMainThread(TestFinishedEvent event) {
67 | Test test = event.test;
68 | String text = "" + test.getDisplayName() + "
" + //
69 | test.getPrimaryResultMicros() + " micro seconds
" + //
70 | ((int) test.getPrimaryResultRate()) + "/s
";
71 | if (test.getOtherTestResults() != null) {
72 | text += test.getOtherTestResults();
73 | }
74 | text += "
----------------
";
75 | textViewResult.append(Html.fromHtml(text));
76 | if (event.isLastEvent) {
77 | findViewById(R.id.buttonCancel).setVisibility(View.GONE);
78 | findViewById(R.id.textViewTestRunning).setVisibility(View.GONE);
79 | findViewById(R.id.buttonKillProcess).setVisibility(View.VISIBLE);
80 | }
81 | }
82 |
83 | public void onClickCancel(View view) {
84 | // Cancel asap
85 | if (testRunner != null) {
86 | testRunner.cancel();
87 | testRunner = null;
88 | }
89 | finish();
90 | }
91 |
92 | public void onClickKillProcess(View view) {
93 | Process.killProcess(Process.myPid());
94 | }
95 |
96 | public void onDestroy() {
97 | if (testRunner != null) {
98 | testRunner.cancel();
99 | }
100 | controlBus.unregister(this);
101 | super.onDestroy();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/EventBusTestJava/src/main/java/org/greenrobot/eventbus/AbstractEventBusTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.greenrobot.eventbus;
17 |
18 | import org.junit.Before;
19 |
20 | import java.util.List;
21 | import java.util.concurrent.CopyOnWriteArrayList;
22 | import java.util.concurrent.CountDownLatch;
23 | import java.util.concurrent.TimeUnit;
24 | import java.util.concurrent.atomic.AtomicInteger;
25 | import java.util.logging.Level;
26 |
27 |
28 | import static org.junit.Assert.assertEquals;
29 | import static org.junit.Assert.assertTrue;
30 | import static org.junit.Assert.fail;
31 |
32 | /**
33 | * @author Markus Junginger, greenrobot
34 | */
35 | public abstract class AbstractEventBusTest {
36 | /** Activates long(er) running tests e.g. testing multi-threading more thoroughly. */
37 | protected static final boolean LONG_TESTS = false;
38 |
39 | protected EventBus eventBus;
40 |
41 | protected final AtomicInteger eventCount = new AtomicInteger();
42 | protected final List