Skip to content

Instantly share code, notes, and snippets.

@instantiator
Created June 25, 2018 10:52
Show Gist options
  • Save instantiator/6cdc970ea984bf9855b8fed5dff27030 to your computer and use it in GitHub Desktop.
Save instantiator/6cdc970ea984bf9855b8fed5dff27030 to your computer and use it in GitHub Desktop.
WeakEventProvider
package com.flt.example.eventproviders;
import com.flt.aislethree.sharedservicelib.logging.Log;
import java.lang.ref.WeakReference;
import java.util.EventListener;
import java.util.LinkedList;
import java.util.List;
/**
* The WeakEventProvider allows you to link listeners from your transient
* objects (such as Activities and Fragments) to a source of events. The
* WeakEventProvider only holds a weak pointer to the listener, and so
* does not prevent it from being garbage collected. If it finds nulls
* where listeners should be, it handles this gracefully by removing them
* from its listeners collection.
*/
public class WeakEventProvider<EventType> {
protected String TAG;
protected List<WeakReference<Listener<EventType>>> listeners;
protected EventType sticky;
public WeakEventProvider() {
this.TAG = getClass().getSimpleName();
this.listeners = new LinkedList<>();
}
public synchronized void notifyListenersSticky(EventType event) {
this.sticky = event;
notifyListeners(event);
}
public synchronized void clearSticky() {
this.sticky = null;
}
public synchronized void notifyListeners(EventType event) {
List<WeakReference<Listener<EventType>>> toRemove = new LinkedList<>();
for (WeakReference<Listener<EventType>> reference : listeners) {
Listener<EventType> listener = reference.get();
if (listener != null) {
boolean completed;
try {
completed = listener.onNotifyEvent(event);
} catch (Exception e) {
Log.e(TAG, "Exception encountered notifying listener. Removing listener.");
completed = true; //
}
if (completed) {
toRemove.add(reference);
}
} else {
toRemove.add(reference);
}
}
listeners.removeAll(toRemove);
}
public synchronized void cleanDeadRefs() {
List<WeakReference<Listener<EventType>>> toRemove = new LinkedList<>();
for (WeakReference<Listener<EventType>> reference : listeners) {
if (reference.get() == null) {
toRemove.add(reference);
}
}
listeners.removeAll(toRemove);
}
public void requestSticky(Listener<EventType> listener) {
if (hasSticky()) { listener.onNotifyEvent(sticky); }
}
public synchronized void addListener(Listener<EventType> listener) {
boolean alreadyListening = false;
for (WeakReference<Listener<EventType>> reference : listeners) {
if (reference.get() == listener) {
alreadyListening = true;
break;
}
}
if (!alreadyListening) {
listener.reset();
listeners.add(new WeakReference<Listener<EventType>>(listener));
}
// always notify with the sticky state - even if the add is redundant
if (hasSticky()) { listener.onNotifyEvent(sticky); }
}
public boolean hasSticky() { return sticky != null; }
public synchronized void removeListener(Listener<EventType> listener) {
List<WeakReference<Listener<EventType>>> toRemove = new LinkedList<>();
for (WeakReference<Listener<EventType>> reference : listeners) {
if (reference.get() == listener || reference.get() == null) {
toRemove.add(reference);
break;
}
}
listeners.removeAll(toRemove);
}
public static abstract class Listener<E> {
public boolean completed = false;
public boolean onNotifyEvent(E event) {
if (!completed) {
completed = parseEvent(event);
}
return completed;
}
/**
* @return true if this event was accepted and the listener should stop listening,
* false to remain a listener.
*/
protected abstract boolean parseEvent(E event);
public void complete() {
completed = true;
}
public void reset() {
completed = false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment