Created
March 16, 2013 21:16
-
-
Save jakubkulhan/5178365 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package info.kulhan.evented; | |
import java.lang.reflect.Method; | |
import java.util.concurrent.LinkedBlockingQueue; | |
import java.util.concurrent.BlockingQueue; | |
import android.util.SparseArray; | |
/** | |
* Inbox for receiving messages | |
* @author Jakub Kulhan <[email protected]> | |
*/ | |
public class Inbox { | |
/** | |
* Special message ID | |
* | |
* When message with this ID is dispatched, dispatch() returns false. | |
* If dispatched by loop(), loop() stops. | |
*/ | |
final public static int STOP_DISPATCH = 0; | |
/** | |
* Will dispatch immediately on current thread | |
*/ | |
final static PostponedDispatch defaultPostponedDispatch = new PostponedDispatch() { | |
@Override | |
public void postpone(Runnable r) { | |
r.run(); | |
} | |
}; | |
/** | |
* Object that receives messages | |
*/ | |
private Object receiver; | |
/** | |
* Inbox thread | |
*/ | |
private Thread thread; | |
/** | |
* Message queue | |
*/ | |
private BlockingQueue<Message> queue = new LinkedBlockingQueue<Message>(); | |
/** | |
* Association between message ID and its dispatcher | |
* | |
* Every message ID has only one dispatcher. | |
*/ | |
private SparseArray<Method> dispatchers = new SparseArray<Method>(); | |
/** | |
* Initialize inbox | |
* @param receiver | |
*/ | |
public Inbox(Object receiver) { | |
this.receiver = receiver; | |
for (Method method : receiver.getClass().getMethods()) { | |
On annotation = method.getAnnotation(On.class); | |
if (annotation != null) { | |
if (annotation.value() == STOP_DISPATCH) { | |
throw new IllegalArgumentException("@On annotation argument cannot be " + STOP_DISPATCH + "."); | |
} | |
dispatchers.put(annotation.value(), method); | |
} | |
} | |
} | |
/** | |
* Receive message | |
* | |
* The message will be dispatched the next time dispatch() is called. | |
*/ | |
public void receive(int messageID, Object... args) { | |
try { | |
queue.put(new Message(messageID, args)); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
/** | |
* Dispatch message (if possible) | |
* @return true if dispatch should continue, false otherwise | |
*/ | |
public boolean dispatch() { | |
return dispatch(defaultPostponedDispatch); | |
} | |
/** | |
* Dispatch message (if possible) | |
* | |
* Dispatch will be made by Runnable's run() method. | |
* | |
* @param p | |
* @return true if dispatch should continue, false otherwise | |
*/ | |
public boolean dispatch(PostponedDispatch p) { | |
try { | |
final Message msg = queue.take(); | |
if (msg.messageID == 0) { | |
return false; | |
} | |
p.postpone(new Runnable() { | |
@Override | |
public void run() { | |
try { | |
dispatchers.get(msg.messageID).invoke(receiver, msg.args); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
}); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
return true; | |
} | |
/** | |
* Occupy current thread by message dispatching | |
*/ | |
public void loop() { | |
loop(defaultPostponedDispatch); | |
} | |
/** | |
* Occupy current thread by message dispatching | |
* | |
* Dispatch will be made by Runnable's run() method. | |
* | |
* @param p | |
*/ | |
public void loop(PostponedDispatch p) { | |
while (dispatch(p)); | |
} | |
/** | |
* Spawn inbox handling thread | |
*/ | |
public void spawn() { | |
spawn(defaultPostponedDispatch); | |
} | |
/** | |
* Spawn inbox handling thread with different dispatcher | |
* @param p | |
*/ | |
public void spawn(final PostponedDispatch p) { | |
if (thread == null) { | |
thread = new Thread(new Runnable() { | |
@Override | |
public void run() { | |
Inbox.this.loop(p); | |
} | |
}); | |
thread.start(); | |
} | |
} | |
/** | |
* Calls quit() and then waits for inbox handling thread to join() | |
*/ | |
public void join() { | |
if (thread != null) { | |
try { | |
quit(); | |
thread.join(); | |
thread = null; | |
} catch (InterruptedException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} | |
/** | |
* Stop message dispatch loop | |
*/ | |
public void quit() { | |
try { | |
queue.put(new Message(0, null)); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
/** | |
* Message will be dispatched by Runnable | |
*/ | |
public interface PostponedDispatch { | |
void postpone(Runnable r); | |
} | |
/** | |
* Holder for received message | |
*/ | |
private class Message { | |
int messageID; | |
Object[] args; | |
Message(int messageID, Object[] args) { | |
this.messageID = messageID; | |
this.args = args; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package info.kulhan.evented; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
/** | |
* Specifies for what message ID method should be dispatched | |
* @author Jakub Kulhan <[email protected]> | |
*/ | |
@Target(ElementType.METHOD) | |
@Retention(RetentionPolicy.RUNTIME) | |
public @interface On { | |
int value(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment