mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
build: reorganize and reconfigure modules to resolve dependency resolution errors
Gradle, for some reason, does not the path of modules to identify them uniquely. Instead, the 'build id' is used to do so, which consists of the maven group and artifact ID. So, even if two modules are located in separate directories, but have the name group and artifact ID, including both of them in other modules may result in dependency resolution errors.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import com.itsaky.androidide.build.config.BuildConfig
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
groupConfig {
|
||||
groupIdSuffix.set("event")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.eventbus.android"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.event.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.kts.
|
||||
#
|
||||
# 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/>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
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,39 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
import com.itsaky.androidide.build.config.BuildConfig
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("kotlin-android")
|
||||
}
|
||||
|
||||
groupConfig {
|
||||
groupIdSuffix.set("event")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "${BuildConfig.packageName}.eventbus.events"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.common.kotlin)
|
||||
implementation(projects.logging.logger)
|
||||
implementation(projects.utilities.shared)
|
||||
|
||||
api(projects.event.eventbus)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?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/>
|
||||
@@ -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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base class for Events. Additional properties that may be required by the event subscribers can be
|
||||
* specified using the {@link #put(Class, Object)} method and can be retrieved using {@link
|
||||
* #get(Class)} method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class Event {
|
||||
|
||||
private Map<Class<?>, Object> data = new HashMap<>();
|
||||
|
||||
public <T> void put(Class<T> klass, T value) {
|
||||
data.put(klass, value);
|
||||
}
|
||||
|
||||
public <T> T get(Class<T> klass) {
|
||||
return (T) data.get(klass);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
/**
|
||||
* Any class that receives an [Event].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
interface EventReceiver {
|
||||
|
||||
/** Registeres this handler with [EventBus]. */
|
||||
fun register() {
|
||||
if (!EventBus.getDefault().isRegistered(this)) {
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
}
|
||||
|
||||
/** Unregisteres this handler from [EventBus]. */
|
||||
fun unregister() {
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.editor
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
|
||||
abstract class EditorActivityLifecycleEvent : Event()
|
||||
|
||||
/**
|
||||
* Dispatched just after `EditorActivity`'s `onCreate` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnCreateEvent : EditorActivityLifecycleEvent()
|
||||
|
||||
/**
|
||||
* Dispatched just after `EditorActivity`'s `onStart` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnStartEvent : EditorActivityLifecycleEvent()
|
||||
|
||||
/**
|
||||
* Dispatched just after `EditorActivity`'s `onResume` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnResumeEvent : EditorActivityLifecycleEvent()
|
||||
|
||||
/**
|
||||
* Dispatched just before `EditorActivity`'s `onPause` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnPauseEvent : EditorActivityLifecycleEvent()
|
||||
|
||||
/**
|
||||
* Dispatched just before `EditorActivity`'s `onStop` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnStopEvent : EditorActivityLifecycleEvent()
|
||||
|
||||
/**
|
||||
* Dispatched just before `EditorActivity`'s `onDestroy` method.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class OnDestroyEvent : EditorActivityLifecycleEvent()
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.editor
|
||||
|
||||
import com.itsaky.androidide.models.Range
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
|
||||
/** Base class for files that accept files as parameters. */
|
||||
open class DocumentEvent(var file: Path) {
|
||||
fun asUri(): URI {
|
||||
return file.toUri()
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatched when an editor is opened for the given file. */
|
||||
data class DocumentOpenEvent(var openedFile: Path, var text: String, var version: Int) :
|
||||
DocumentEvent(openedFile)
|
||||
|
||||
/** Dispatched when the given file is closed. Always dispatched after [DocumentOpenEvent]. */
|
||||
data class DocumentCloseEvent
|
||||
@JvmOverloads
|
||||
constructor(var closedFile: Path, val selectionRange: Range = Range.NONE) :
|
||||
DocumentEvent(closedFile)
|
||||
|
||||
/**
|
||||
* Dispatched when the content of the given opened document changes. The change can be either
|
||||
* performed by the user or the IDE itself.
|
||||
*/
|
||||
data class DocumentChangeEvent(
|
||||
var changedFile: Path,
|
||||
var changedText: String,
|
||||
var newText: String? = null,
|
||||
var version: Int,
|
||||
var changeType: ChangeType,
|
||||
var changeDelta: Int,
|
||||
var changeRange: Range
|
||||
) : DocumentEvent(changedFile)
|
||||
|
||||
/** Dispatched when the given document is saved to disk. */
|
||||
data class DocumentSaveEvent(var savedFile: Path) : DocumentEvent(savedFile)
|
||||
|
||||
/** Dispatched when the given opened document is selected and it is visible to the user. */
|
||||
data class DocumentSelectedEvent(var selectedFile: Path) : DocumentEvent(selectedFile)
|
||||
|
||||
/** The type of change in a [DocumentChangeEvent]. */
|
||||
enum class ChangeType {
|
||||
INSERT,
|
||||
DELETE,
|
||||
NEW_TEXT
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.editor
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
|
||||
/**
|
||||
* Dispatched when the color schemes has been reloaded. Listeners should update any UI which use
|
||||
* color schemes.
|
||||
*
|
||||
* For example, on receiving this event, the editors in AndroidIDE invalidate themselves to redraw
|
||||
* the content with the updated color schemes.
|
||||
*/
|
||||
class ColorSchemeInvalidatedEvent : Event()
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.file
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
import java.io.File
|
||||
|
||||
/** Base class for file events. */
|
||||
abstract class FileEvent : Event() {
|
||||
abstract val file: File
|
||||
}
|
||||
|
||||
/**
|
||||
* Event dispatched when a new file is created in the file tree.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class FileCreationEvent(override val file: File) : FileEvent()
|
||||
|
||||
/**
|
||||
* Event dispatched when a file is deleted in the file tree.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class FileDeletionEvent(override val file: File) : FileEvent()
|
||||
|
||||
/**
|
||||
* Event dispatched when a file is renamed in the file tree.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class FileRenameEvent(override val file: File, val newFile: File) : FileEvent()
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.filetree
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Event dispatched when a file is clicked in the file tree.
|
||||
*
|
||||
* @param file The clicked file.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class FileClickEvent(val file: File) : Event()
|
||||
|
||||
/**
|
||||
* Event dispatched when a file is long clicked in the file tree.
|
||||
*
|
||||
* @param file The file that was long clicked.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class FileLongClickEvent(val file: File) : Event()
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.preferences
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
|
||||
/**
|
||||
* Dispatched when a preference's is value changed in IDE preferences.
|
||||
*
|
||||
* @param key The key of the preference that was changed.
|
||||
* @param value The new value of the preference.
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
data class PreferenceChangeEvent(val key: String, val value: Any?) : Event()
|
||||
|
||||
/**
|
||||
* Dispatched when a preference is removed from shared preferences.
|
||||
*
|
||||
* @param key The key of the preference that was removed.
|
||||
*/
|
||||
data class PreferenceRemoveEvent(val key: String) : Event()
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.eventbus.events.project
|
||||
|
||||
import com.itsaky.androidide.eventbus.events.Event
|
||||
|
||||
/**
|
||||
* Event dispatched when the project is initialized. A project instance is provided with this event
|
||||
* which can be obtained using [Event.get].
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class ProjectInitializedEvent : Event()
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
@Suppress("JavaPluginLanguageLevel")
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
}
|
||||
|
||||
groupConfig {
|
||||
groupIdSuffix.set("event")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.common.jkotlin)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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,75 @@
|
||||
/*
|
||||
* 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,610 @@
|
||||
/*
|
||||
* 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). */
|
||||
static final 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,201 @@
|
||||
/*
|
||||
* 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 static final 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;
|
||||
}
|
||||
}
|
||||
|
||||
public EventBus installDefaultEventBus() {
|
||||
return installDefaultEventBus(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(boolean ignoreIfInstalled) {
|
||||
synchronized (EventBus.class) {
|
||||
if (EventBus.defaultInstance != null) {
|
||||
if (ignoreIfInstalled) {
|
||||
return EventBus.defaultInstance;
|
||||
}
|
||||
|
||||
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,38 @@
|
||||
/*
|
||||
* 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,70 @@
|
||||
/*
|
||||
* 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,27 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.greenrobot.eventbus;
|
||||
|
||||
/**
|
||||
* 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,34 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.greenrobot.eventbus;
|
||||
|
||||
/**
|
||||
* 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,58 @@
|
||||
/*
|
||||
* 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 static final 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,55 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.greenrobot.eventbus;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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,70 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.")
|
||||
|| clazzName.startsWith("jdkx.")
|
||||
|| clazzName.startsWith("openjdk.")) {
|
||||
clazz = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
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,73 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -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.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,134 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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.util;
|
||||
|
||||
public interface HasExecutionScope {
|
||||
Object getExecutionScope();
|
||||
|
||||
void setExecutionScope(Object executionScope);
|
||||
}
|
||||
@@ -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.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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user