mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Added the EventBus library
This commit is contained in:
+2
-1
@@ -119,7 +119,8 @@ dependencies {
|
||||
implementation common.kotlin
|
||||
|
||||
// Local projects here
|
||||
implementation project(':build-tools:projects')
|
||||
implementation project (':build-tools:eventbus-android')
|
||||
implementation project (':build-tools:projects')
|
||||
implementation project (':build-tools:tooling-api')
|
||||
implementation project (':treeview')
|
||||
implementation project (':emulatorview')
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdk
|
||||
buildToolsVersion rootProject.ext.buildTools
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.minSdk
|
||||
targetSdkVersion rootProject.ext.targetSdk
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile ('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility rootProject.ext.javaSourceVersion
|
||||
targetCompatibility rootProject.ext.javaTargetVersion
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api project(path: ':build-tools:eventbus')
|
||||
}
|
||||
+21
@@ -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
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.itsaky.androidide.eventbus.android"/>
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
|
||||
public class HandlerPoster extends Handler implements Poster {
|
||||
|
||||
private final PendingPostQueue queue;
|
||||
private final int maxMillisInsideHandleMessage;
|
||||
private final EventBus eventBus;
|
||||
private boolean handlerActive;
|
||||
|
||||
public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
|
||||
super(looper);
|
||||
this.eventBus = eventBus;
|
||||
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
|
||||
queue = new PendingPostQueue();
|
||||
}
|
||||
|
||||
public void enqueue(Subscription subscription, Object event) {
|
||||
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
|
||||
synchronized (this) {
|
||||
queue.enqueue(pendingPost);
|
||||
if (!handlerActive) {
|
||||
handlerActive = true;
|
||||
if (!sendMessage(obtainMessage())) {
|
||||
throw new EventBusException("Could not send handler message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
boolean rescheduled = false;
|
||||
try {
|
||||
long started = SystemClock.uptimeMillis();
|
||||
while (true) {
|
||||
PendingPost pendingPost = queue.poll();
|
||||
if (pendingPost == null) {
|
||||
synchronized (this) {
|
||||
// Check again, this time in synchronized
|
||||
pendingPost = queue.poll();
|
||||
if (pendingPost == null) {
|
||||
handlerActive = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
eventBus.invokeSubscriber(pendingPost);
|
||||
long timeInMethod = SystemClock.uptimeMillis() - started;
|
||||
if (timeInMethod >= maxMillisInsideHandleMessage) {
|
||||
if (!sendMessage(obtainMessage())) {
|
||||
throw new EventBusException("Could not send handler message");
|
||||
}
|
||||
rescheduled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
handlerActive = rescheduled;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -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());
|
||||
}
|
||||
}
|
||||
+60
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = rootProject.ext.javaSourceVersion
|
||||
targetCompatibility = rootProject.ext.javaTargetVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation common.jkotlin
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
@@ -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.eventbus;
|
||||
|
||||
|
||||
/**
|
||||
* Posts events in background.
|
||||
*
|
||||
* @author Markus
|
||||
*/
|
||||
class AsyncPoster implements Runnable, Poster {
|
||||
|
||||
private final PendingPostQueue queue;
|
||||
private final EventBus eventBus;
|
||||
|
||||
AsyncPoster(EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
queue = new PendingPostQueue();
|
||||
}
|
||||
|
||||
public void enqueue(Subscription subscription, Object event) {
|
||||
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
|
||||
queue.enqueue(pendingPost);
|
||||
eventBus.getExecutorService().execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
PendingPost pendingPost = queue.poll();
|
||||
if(pendingPost == null) {
|
||||
throw new IllegalStateException("No pending post available");
|
||||
}
|
||||
eventBus.invokeSubscriber(pendingPost);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.logging.Level;
|
||||
|
||||
/**
|
||||
* Posts events in background.
|
||||
*
|
||||
* @author Markus
|
||||
*/
|
||||
final class BackgroundPoster implements Runnable, Poster {
|
||||
|
||||
private final PendingPostQueue queue;
|
||||
private final EventBus eventBus;
|
||||
|
||||
private volatile boolean executorRunning;
|
||||
|
||||
BackgroundPoster(EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
queue = new PendingPostQueue();
|
||||
}
|
||||
|
||||
public void enqueue(Subscription subscription, Object event) {
|
||||
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
|
||||
synchronized (this) {
|
||||
queue.enqueue(pendingPost);
|
||||
if (!executorRunning) {
|
||||
executorRunning = true;
|
||||
eventBus.getExecutorService().execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
try {
|
||||
while (true) {
|
||||
PendingPost pendingPost = queue.poll(1000);
|
||||
if (pendingPost == null) {
|
||||
synchronized (this) {
|
||||
// Check again, this time in synchronized
|
||||
pendingPost = queue.poll();
|
||||
if (pendingPost == null) {
|
||||
executorRunning = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
eventBus.invokeSubscriber(pendingPost);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interrupted", e);
|
||||
}
|
||||
} finally {
|
||||
executorRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* 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.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 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 = "EventBus";
|
||||
|
||||
static volatile EventBus defaultInstance;
|
||||
|
||||
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
|
||||
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
|
||||
|
||||
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
|
||||
private final Map<Object, List<Class<?>>> typesBySubscriber;
|
||||
private final Map<Class<?>, Object> stickyEvents;
|
||||
|
||||
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
|
||||
@Override
|
||||
protected PostingThreadState initialValue() {
|
||||
return new PostingThreadState();
|
||||
}
|
||||
};
|
||||
|
||||
// @Nullable
|
||||
private final MainThreadSupport mainThreadSupport;
|
||||
// @Nullable
|
||||
private final Poster mainThreadPoster;
|
||||
private final BackgroundPoster backgroundPoster;
|
||||
private final AsyncPoster asyncPoster;
|
||||
private final SubscriberMethodFinder subscriberMethodFinder;
|
||||
private final ExecutorService executorService;
|
||||
|
||||
private final boolean throwSubscriberException;
|
||||
private final boolean logSubscriberExceptions;
|
||||
private final boolean logNoSubscriberMessages;
|
||||
private final boolean sendSubscriberExceptionEvent;
|
||||
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() {
|
||||
EventBus instance = defaultInstance;
|
||||
if (instance == null) {
|
||||
synchronized (EventBus.class) {
|
||||
instance = EventBus.defaultInstance;
|
||||
if (instance == null) {
|
||||
instance = EventBus.defaultInstance = new EventBus();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static EventBusBuilder builder() {
|
||||
return new EventBusBuilder();
|
||||
}
|
||||
|
||||
/** For unit test primarily. */
|
||||
public static void clearCaches() {
|
||||
SubscriberMethodFinder.clearCaches();
|
||||
eventTypesCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
|
||||
* central bus, consider {@link #getDefault()}.
|
||||
*/
|
||||
public EventBus() {
|
||||
this(DEFAULT_BUILDER);
|
||||
}
|
||||
|
||||
EventBus(EventBusBuilder builder) {
|
||||
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);
|
||||
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;
|
||||
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
|
||||
throwSubscriberException = builder.throwSubscriberException;
|
||||
eventInheritance = builder.eventInheritance;
|
||||
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.
|
||||
* <p/>
|
||||
* 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) {
|
||||
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.");
|
||||
}
|
||||
|
||||
Class<?> subscriberClass = subscriber.getClass();
|
||||
List<SubscriberMethod> 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) {
|
||||
Class<?> eventType = subscriberMethod.eventType;
|
||||
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
|
||||
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
|
||||
if (subscriptions == null) {
|
||||
subscriptions = new CopyOnWriteArrayList<>();
|
||||
subscriptionsByEventType.put(eventType, subscriptions);
|
||||
} else {
|
||||
if (subscriptions.contains(newSubscription)) {
|
||||
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
|
||||
+ eventType);
|
||||
}
|
||||
}
|
||||
|
||||
int size = subscriptions.size();
|
||||
for (int i = 0; i <= size; i++) {
|
||||
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
|
||||
subscriptions.add(i, newSubscription);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
|
||||
if (subscribedEvents == null) {
|
||||
subscribedEvents = new ArrayList<>();
|
||||
typesBySubscriber.put(subscriber, subscribedEvents);
|
||||
}
|
||||
subscribedEvents.add(eventType);
|
||||
|
||||
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<Class>).
|
||||
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
|
||||
for (Map.Entry<Class<?>, 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 unsubscribeByEventType(Object subscriber, Class<?> eventType) {
|
||||
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
|
||||
if (subscriptions != null) {
|
||||
int size = subscriptions.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Subscription subscription = subscriptions.get(i);
|
||||
if (subscription.subscriber == subscriber) {
|
||||
subscription.active = false;
|
||||
subscriptions.remove(i);
|
||||
i--;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Unregisters the given subscriber from all event classes. */
|
||||
public synchronized void unregister(Object subscriber) {
|
||||
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
|
||||
if (subscribedTypes != null) {
|
||||
for (Class<?> eventType : subscribedTypes) {
|
||||
unsubscribeByEventType(subscriber, eventType);
|
||||
}
|
||||
typesBySubscriber.remove(subscriber);
|
||||
} else {
|
||||
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/** Posts the given event to the event bus. */
|
||||
public void post(Object event) {
|
||||
PostingThreadState postingState = currentPostingThreadState.get();
|
||||
List<Object> eventQueue = postingState.eventQueue;
|
||||
eventQueue.add(event);
|
||||
|
||||
if (!postingState.isPosting) {
|
||||
postingState.isMainThread = isMainThread();
|
||||
postingState.isPosting = true;
|
||||
if (postingState.canceled) {
|
||||
throw new EventBusException("Internal error. Abort state was not reset");
|
||||
}
|
||||
try {
|
||||
while (!eventQueue.isEmpty()) {
|
||||
postSingleEvent(eventQueue.remove(0), postingState);
|
||||
}
|
||||
} finally {
|
||||
postingState.isPosting = false;
|
||||
postingState.isMainThread = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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();
|
||||
if (!postingState.isPosting) {
|
||||
throw new EventBusException(
|
||||
"This method may only be called from inside event handling methods on the posting thread");
|
||||
} else if (event == null) {
|
||||
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.POSTING) {
|
||||
throw new EventBusException(" event handlers may only abort the incoming event");
|
||||
}
|
||||
|
||||
postingState.canceled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 by subscribers using {@link Subscribe#sticky()}.
|
||||
*/
|
||||
public void postSticky(Object event) {
|
||||
synchronized (stickyEvents) {
|
||||
stickyEvents.put(event.getClass(), event);
|
||||
}
|
||||
// Should be posted after it is putted, in case the subscriber wants to remove immediately
|
||||
post(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most recent sticky event for the given type.
|
||||
*
|
||||
* @see #postSticky(Object)
|
||||
*/
|
||||
public <T> T getStickyEvent(Class<T> eventType) {
|
||||
synchronized (stickyEvents) {
|
||||
return eventType.cast(stickyEvents.get(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and gets the recent sticky event for the given event type.
|
||||
*
|
||||
* @see #postSticky(Object)
|
||||
*/
|
||||
public <T> T removeStickyEvent(Class<T> eventType) {
|
||||
synchronized (stickyEvents) {
|
||||
return eventType.cast(stickyEvents.remove(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the sticky event if it equals to the given event.
|
||||
*
|
||||
* @return true if the events matched and the sticky event was removed.
|
||||
*/
|
||||
public boolean removeStickyEvent(Object event) {
|
||||
synchronized (stickyEvents) {
|
||||
Class<?> eventType = event.getClass();
|
||||
Object existingEvent = stickyEvents.get(eventType);
|
||||
if (event.equals(existingEvent)) {
|
||||
stickyEvents.remove(eventType);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all sticky events.
|
||||
*/
|
||||
public void removeAllStickyEvents() {
|
||||
synchronized (stickyEvents) {
|
||||
stickyEvents.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSubscriberForEvent(Class<?> eventClass) {
|
||||
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
|
||||
if (eventTypes != null) {
|
||||
int countTypes = eventTypes.size();
|
||||
for (int h = 0; h < countTypes; h++) {
|
||||
Class<?> clazz = eventTypes.get(h);
|
||||
CopyOnWriteArrayList<Subscription> subscriptions;
|
||||
synchronized (this) {
|
||||
subscriptions = subscriptionsByEventType.get(clazz);
|
||||
}
|
||||
if (subscriptions != null && !subscriptions.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
|
||||
Class<?> eventClass = event.getClass();
|
||||
boolean subscriptionFound = false;
|
||||
if (eventInheritance) {
|
||||
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
|
||||
int countTypes = eventTypes.size();
|
||||
for (int h = 0; h < countTypes; h++) {
|
||||
Class<?> clazz = eventTypes.get(h);
|
||||
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
|
||||
}
|
||||
} else {
|
||||
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
|
||||
}
|
||||
if (!subscriptionFound) {
|
||||
if (logNoSubscriberMessages) {
|
||||
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
|
||||
}
|
||||
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
|
||||
eventClass != SubscriberExceptionEvent.class) {
|
||||
post(new NoSubscriberEvent(this, event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
|
||||
CopyOnWriteArrayList<Subscription> subscriptions;
|
||||
synchronized (this) {
|
||||
subscriptions = subscriptionsByEventType.get(eventClass);
|
||||
}
|
||||
if (subscriptions != null && !subscriptions.isEmpty()) {
|
||||
for (Subscription subscription : subscriptions) {
|
||||
postingState.event = event;
|
||||
postingState.subscription = subscription;
|
||||
boolean aborted;
|
||||
try {
|
||||
postToSubscription(subscription, event, postingState.isMainThread);
|
||||
aborted = postingState.canceled;
|
||||
} finally {
|
||||
postingState.event = null;
|
||||
postingState.subscription = null;
|
||||
postingState.canceled = false;
|
||||
}
|
||||
if (aborted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
|
||||
switch (subscription.subscriberMethod.threadMode) {
|
||||
case POSTING:
|
||||
invokeSubscriber(subscription, event);
|
||||
break;
|
||||
case MAIN:
|
||||
if (isMainThread) {
|
||||
invokeSubscriber(subscription, event);
|
||||
} else {
|
||||
mainThreadPoster.enqueue(subscription, event);
|
||||
}
|
||||
break;
|
||||
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:
|
||||
asyncPoster.enqueue(subscription, event);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
|
||||
}
|
||||
}
|
||||
|
||||
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
|
||||
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
|
||||
synchronized (eventTypesCache) {
|
||||
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
|
||||
if (eventTypes == null) {
|
||||
eventTypes = new ArrayList<>();
|
||||
Class<?> clazz = eventClass;
|
||||
while (clazz != null) {
|
||||
eventTypes.add(clazz);
|
||||
addInterfaces(eventTypes, clazz.getInterfaces());
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
eventTypesCache.put(eventClass, eventTypes);
|
||||
}
|
||||
return eventTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/** Recurses through super interfaces. */
|
||||
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
|
||||
for (Class<?> interfaceClass : interfaces) {
|
||||
if (!eventTypes.contains(interfaceClass)) {
|
||||
eventTypes.add(interfaceClass);
|
||||
addInterfaces(eventTypes, interfaceClass.getInterfaces());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions
|
||||
* between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the
|
||||
* subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the
|
||||
* live cycle of an Activity or Fragment.
|
||||
*/
|
||||
void invokeSubscriber(PendingPost pendingPost) {
|
||||
Object event = pendingPost.event;
|
||||
Subscription subscription = pendingPost.subscription;
|
||||
PendingPost.releasePendingPost(pendingPost);
|
||||
if (subscription.active) {
|
||||
invokeSubscriber(subscription, event);
|
||||
}
|
||||
}
|
||||
|
||||
void invokeSubscriber(Subscription subscription, Object event) {
|
||||
try {
|
||||
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
|
||||
} catch (InvocationTargetException e) {
|
||||
handleSubscriberException(subscription, event, e.getCause());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Unexpected exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
|
||||
if (event instanceof SubscriberExceptionEvent) {
|
||||
if (logSubscriberExceptions) {
|
||||
// Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
|
||||
logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
|
||||
+ " threw an exception", cause);
|
||||
SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
|
||||
logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in "
|
||||
+ exEvent.causingSubscriber, exEvent.throwable);
|
||||
}
|
||||
} else {
|
||||
if (throwSubscriberException) {
|
||||
throw new EventBusException("Invoking subscriber failed", cause);
|
||||
}
|
||||
if (logSubscriberExceptions) {
|
||||
logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
|
||||
+ subscription.subscriber.getClass(), cause);
|
||||
}
|
||||
if (sendSubscriberExceptionEvent) {
|
||||
SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
|
||||
subscription.subscriber);
|
||||
post(exEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** For ThreadLocal, much faster to set (and get multiple values). */
|
||||
final static class PostingThreadState {
|
||||
final List<Object> eventQueue = new ArrayList<>();
|
||||
boolean isPosting;
|
||||
boolean isMainThread;
|
||||
Subscription subscription;
|
||||
Object event;
|
||||
boolean canceled;
|
||||
}
|
||||
|
||||
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<SubscriberExceptionEvent> exceptionEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EventBus[indexCount=" + indexCount + ", eventInheritance=" + eventInheritance + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 org.greenrobot.eventbus.meta.SubscriberInfoIndex;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
boolean logSubscriberExceptions = true;
|
||||
boolean logNoSubscriberMessages = true;
|
||||
boolean sendSubscriberExceptionEvent = true;
|
||||
boolean sendNoSubscriberEvent = true;
|
||||
boolean throwSubscriberException;
|
||||
boolean eventInheritance = true;
|
||||
boolean ignoreGeneratedIndex;
|
||||
boolean strictMethodVerification;
|
||||
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
|
||||
List<Class<?>> skipMethodVerificationForClasses;
|
||||
List<SubscriberInfoIndex> subscriberInfoIndexes;
|
||||
Logger logger;
|
||||
MainThreadSupport mainThreadSupport;
|
||||
|
||||
EventBusBuilder() {
|
||||
}
|
||||
|
||||
/** Default: true */
|
||||
public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
|
||||
this.logSubscriberExceptions = logSubscriberExceptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Default: true */
|
||||
public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
|
||||
this.logNoSubscriberMessages = logNoSubscriberMessages;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Default: true */
|
||||
public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
|
||||
this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Default: true */
|
||||
public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
|
||||
this.sendNoSubscriberEvent = sendNoSubscriberEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails if an subscriber throws an exception (default: false).
|
||||
* <p/>
|
||||
* Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss
|
||||
* exceptions during development.
|
||||
*/
|
||||
public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
|
||||
this.throwSubscriberException = throwSubscriberException;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* greater than 20%.
|
||||
* <p/>
|
||||
* 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.
|
||||
*/
|
||||
public EventBusBuilder eventInheritance(boolean eventInheritance) {
|
||||
this.eventInheritance = eventInheritance;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced
|
||||
* setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior.
|
||||
*/
|
||||
public EventBusBuilder executorService(ExecutorService executorService) {
|
||||
this.executorService = executorService;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method name verification is done for methods starting with onEvent to avoid typos; using this method you can
|
||||
* exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor
|
||||
* abstract).
|
||||
*/
|
||||
public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
|
||||
if (skipMethodVerificationForClasses == null) {
|
||||
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.
|
||||
* <p/>
|
||||
* 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.
|
||||
*
|
||||
* @throws EventBusException if there's already a default EventBus instance in place
|
||||
*/
|
||||
public EventBus installDefaultEventBus() {
|
||||
synchronized (EventBus.class) {
|
||||
if (EventBus.defaultInstance != null) {
|
||||
throw new EventBusException("Default instance already exists." +
|
||||
" It may be only set once before it's used the first time to ensure consistent behavior.");
|
||||
}
|
||||
EventBus.defaultInstance = build();
|
||||
return EventBus.defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds an EventBus based on the current configuration. */
|
||||
public EventBus build() {
|
||||
return new EventBus(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An {@link RuntimeException} thrown in cases something went wrong inside EventBus.
|
||||
*
|
||||
* @author Markus
|
||||
*
|
||||
*/
|
||||
public class EventBusException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -2912559384646531479L;
|
||||
|
||||
public EventBusException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
public EventBusException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public EventBusException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* This Event is posted by EventBus when no subscriber is found for a posted event.
|
||||
*
|
||||
* @author Markus
|
||||
*/
|
||||
public final class NoSubscriberEvent {
|
||||
/** The {@link EventBus} instance to with the original event was posted to. */
|
||||
public final EventBus eventBus;
|
||||
|
||||
/** The original event that could not be delivered to any subscriber. */
|
||||
public final Object originalEvent;
|
||||
|
||||
public NoSubscriberEvent(EventBus eventBus, Object originalEvent) {
|
||||
this.eventBus = eventBus;
|
||||
this.originalEvent = originalEvent;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class PendingPost {
|
||||
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
|
||||
|
||||
Object event;
|
||||
Subscription subscription;
|
||||
PendingPost next;
|
||||
|
||||
private PendingPost(Object event, Subscription subscription) {
|
||||
this.event = event;
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
|
||||
synchronized (pendingPostPool) {
|
||||
int size = pendingPostPool.size();
|
||||
if (size > 0) {
|
||||
PendingPost pendingPost = pendingPostPool.remove(size - 1);
|
||||
pendingPost.event = event;
|
||||
pendingPost.subscription = subscription;
|
||||
pendingPost.next = null;
|
||||
return pendingPost;
|
||||
}
|
||||
}
|
||||
return new PendingPost(event, subscription);
|
||||
}
|
||||
|
||||
static void releasePendingPost(PendingPost pendingPost) {
|
||||
pendingPost.event = null;
|
||||
pendingPost.subscription = null;
|
||||
pendingPost.next = null;
|
||||
synchronized (pendingPostPool) {
|
||||
// Don't let the pool grow indefinitely
|
||||
if (pendingPostPool.size() < 10000) {
|
||||
pendingPostPool.add(pendingPost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
private PendingPost tail;
|
||||
|
||||
synchronized void enqueue(PendingPost pendingPost) {
|
||||
if (pendingPost == null) {
|
||||
throw new NullPointerException("null cannot be enqueued");
|
||||
}
|
||||
if (tail != null) {
|
||||
tail.next = pendingPost;
|
||||
tail = pendingPost;
|
||||
} else if (head == null) {
|
||||
head = tail = pendingPost;
|
||||
} else {
|
||||
throw new IllegalStateException("Head present, but no tail");
|
||||
}
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
synchronized PendingPost poll() {
|
||||
PendingPost pendingPost = head;
|
||||
if (head != null) {
|
||||
head = head.next;
|
||||
if (head == null) {
|
||||
tail = null;
|
||||
}
|
||||
}
|
||||
return pendingPost;
|
||||
}
|
||||
|
||||
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
|
||||
if (head == null) {
|
||||
wait(maxMillisToWait);
|
||||
}
|
||||
return poll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+44
@@ -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;
|
||||
|
||||
/**
|
||||
* This Event is posted by EventBus when an exception occurs inside a subscriber's event handling method.
|
||||
*
|
||||
* @author Markus
|
||||
*/
|
||||
public final class SubscriberExceptionEvent {
|
||||
/** The {@link EventBus} instance to with the original event was posted to. */
|
||||
public final EventBus eventBus;
|
||||
|
||||
/** The Throwable thrown by a subscriber. */
|
||||
public final Throwable throwable;
|
||||
|
||||
/** The original event that could not be delivered to any subscriber. */
|
||||
public final Object causingEvent;
|
||||
|
||||
/** The subscriber that threw the Throwable. */
|
||||
public final Object causingSubscriber;
|
||||
|
||||
public SubscriberExceptionEvent(EventBus eventBus, Throwable throwable, Object causingEvent,
|
||||
Object causingSubscriber) {
|
||||
this.eventBus = eventBus;
|
||||
this.throwable = throwable;
|
||||
this.causingEvent = causingEvent;
|
||||
this.causingSubscriber = causingSubscriber;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/** 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;
|
||||
|
||||
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 == this) {
|
||||
return true;
|
||||
} else if (other instanceof SubscriberMethod) {
|
||||
checkMethodString();
|
||||
SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
|
||||
otherSubscriberMethod.checkMethodString();
|
||||
// Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
|
||||
return methodString.equals(otherSubscriberMethod.methodString);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void checkMethodString() {
|
||||
if (methodString == null) {
|
||||
// Method.toString has more overhead, just take relevant parts of the method
|
||||
StringBuilder builder = new StringBuilder(64);
|
||||
builder.append(method.getDeclaringClass().getName());
|
||||
builder.append('#').append(method.getName());
|
||||
builder.append('(').append(eventType.getName());
|
||||
methodString = builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return method.hashCode();
|
||||
}
|
||||
}
|
||||
+282
@@ -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<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private List<SubscriberInfoIndex> 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<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
|
||||
boolean ignoreGeneratedIndex) {
|
||||
this.subscriberInfoIndexes = subscriberInfoIndexes;
|
||||
this.strictMethodVerification = strictMethodVerification;
|
||||
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
|
||||
}
|
||||
|
||||
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
|
||||
List<SubscriberMethod> 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<SubscriberMethod> 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<SubscriberMethod> getMethodsAndRelease(FindState findState) {
|
||||
List<SubscriberMethod> 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<SubscriberMethod> 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<SubscriberMethod> subscriberMethods = new ArrayList<>();
|
||||
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
|
||||
final Map<String, Class> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 Subscription {
|
||||
final Object subscriber;
|
||||
final SubscriberMethod subscriberMethod;
|
||||
/**
|
||||
* 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) {
|
||||
this.subscriber = subscriber;
|
||||
this.subscriberMethod = subscriberMethod;
|
||||
active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Subscription) {
|
||||
Subscription otherSubscription = (Subscription) other;
|
||||
return subscriber == otherSubscription.subscriber
|
||||
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
}
|
||||
+31
@@ -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;
|
||||
}
|
||||
}
|
||||
+48
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -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<? extends SubscriberInfo> superSubscriberInfoClass;
|
||||
private final boolean shouldCheckSuperclass;
|
||||
|
||||
protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events.
|
||||
* By default, uses {@link ThrowableFailureEvent}.
|
||||
* <p>
|
||||
* 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
|
||||
* <pre>
|
||||
* -keepclassmembers class com.example.CustomThrowableFailureEvent {
|
||||
* <init>(java.lang.Throwable);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class AsyncExecutor {
|
||||
|
||||
public static class Builder {
|
||||
private Executor threadPool;
|
||||
private Class<?> failureEventType;
|
||||
private EventBus eventBus;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder threadPool(Executor threadPool) {
|
||||
this.threadPool = threadPool;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder failureEventType(Class<?> failureEventType) {
|
||||
this.failureEventType = failureEventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder eventBus(EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AsyncExecutor build() {
|
||||
return buildForScope(null);
|
||||
}
|
||||
|
||||
public AsyncExecutor buildForScope(Object executionContext) {
|
||||
if (eventBus == null) {
|
||||
eventBus = EventBus.getDefault();
|
||||
}
|
||||
if (threadPool == null) {
|
||||
threadPool = Executors.newCachedThreadPool();
|
||||
}
|
||||
if (failureEventType == null) {
|
||||
failureEventType = ThrowableFailureEvent.class;
|
||||
}
|
||||
return new AsyncExecutor(threadPool, eventBus, failureEventType, executionContext);
|
||||
}
|
||||
}
|
||||
|
||||
/** Like {@link Runnable}, but the run method may throw an exception. */
|
||||
public interface RunnableEx {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static AsyncExecutor create() {
|
||||
return new Builder().build();
|
||||
}
|
||||
|
||||
private final Executor threadPool;
|
||||
private final Constructor<?> failureEventConstructor;
|
||||
private final EventBus eventBus;
|
||||
private final Object scope;
|
||||
|
||||
private AsyncExecutor(Executor threadPool, EventBus eventBus, Class<?> failureEventType, Object scope) {
|
||||
this.threadPool = threadPool;
|
||||
this.eventBus = eventBus;
|
||||
this.scope = scope;
|
||||
try {
|
||||
failureEventConstructor = failureEventType.getConstructor(Throwable.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(
|
||||
"Failure event class must have a constructor with one parameter of type Throwable", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Posts an failure event if the given {@link RunnableEx} throws an Exception. */
|
||||
public void execute(final RunnableEx runnable) {
|
||||
threadPool.execute(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
Object event;
|
||||
try {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 java.util.logging.Level;
|
||||
|
||||
|
||||
/**
|
||||
* Maps throwables to texts for error dialogs. Use Config to configure the mapping.
|
||||
*
|
||||
* @author Markus
|
||||
*/
|
||||
public class ExceptionToResourceMapping {
|
||||
|
||||
public final Map<Class<? extends Throwable>, Integer> throwableToMsgIdMap;
|
||||
|
||||
public ExceptionToResourceMapping() {
|
||||
throwableToMsgIdMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/** Looks at the exception and its causes trying to find an ID. */
|
||||
public Integer mapThrowable(final Throwable throwable) {
|
||||
Throwable throwableToCheck = throwable;
|
||||
int depthToGo = 20;
|
||||
|
||||
while (true) {
|
||||
Integer resId = mapThrowableFlat(throwableToCheck);
|
||||
if (resId != null) {
|
||||
return resId;
|
||||
} else {
|
||||
throwableToCheck = throwableToCheck.getCause();
|
||||
depthToGo--;
|
||||
if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Mapping without checking the cause (done in mapThrowable). */
|
||||
protected Integer mapThrowableFlat(Throwable throwable) {
|
||||
Class<? extends Throwable> throwableClass = throwable.getClass();
|
||||
Integer resId = throwableToMsgIdMap.get(throwableClass);
|
||||
if (resId == null) {
|
||||
Class<? extends Throwable> closestClass = null;
|
||||
Set<Entry<Class<? extends Throwable>, Integer>> mappings = throwableToMsgIdMap.entrySet();
|
||||
for (Entry<Class<? extends Throwable>, Integer> mapping : mappings) {
|
||||
Class<? extends Throwable> candidate = mapping.getKey();
|
||||
if (candidate.isAssignableFrom(throwableClass)) {
|
||||
if (closestClass == null || closestClass.isAssignableFrom(candidate)) {
|
||||
closestClass = candidate;
|
||||
resId = mapping.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return resId;
|
||||
}
|
||||
|
||||
public ExceptionToResourceMapping addMapping(Class<? extends Throwable> clazz, int msgId) {
|
||||
throwableToMsgIdMap.put(clazz, msgId);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -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);
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
protected final boolean suppressErrorUi;
|
||||
private Object executionContext;
|
||||
|
||||
public ThrowableFailureEvent(Throwable throwable) {
|
||||
this.throwable = throwable;
|
||||
suppressErrorUi = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param suppressErrorUi
|
||||
* true indicates to the receiver that no error UI (e.g. dialog) should now displayed.
|
||||
*/
|
||||
public ThrowableFailureEvent(Throwable throwable, boolean suppressErrorUi) {
|
||||
this.throwable = throwable;
|
||||
this.suppressErrorUi = suppressErrorUi;
|
||||
}
|
||||
|
||||
public Throwable getThrowable() {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
public boolean isSuppressErrorUi() {
|
||||
return suppressErrorUi;
|
||||
}
|
||||
|
||||
public Object getExecutionScope() {
|
||||
return executionContext;
|
||||
}
|
||||
|
||||
public void setExecutionScope(Object executionContext) {
|
||||
this.executionContext = executionContext;
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -30,3 +30,5 @@ include ':build-tools:fuzzysearch'
|
||||
include ':build-tools:builder-model-impl'
|
||||
include ':build-tools:jsonrpc'
|
||||
include ':build-tools:projects'
|
||||
include ':build-tools:eventbus'
|
||||
include ':build-tools:eventbus-android'
|
||||
|
||||
Reference in New Issue
Block a user