package vandy.mooc;
import java.util.concurrent.CyclicBarrier;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
/**
* @class PlayPingPong
*
* @brief This class uses elements of the Android HaMeR framework to
* create two Threads that alternately print "Ping" and "Pong",
* respectively, on the display.
*/
public class PlayPingPong implements Runnable {
/**
* Keep track of whether a Thread is printing "ping" or "pong".
*/
private enum PingPong {
PING, PONG
};
/**
* Number of iterations to run the ping-pong algorithm.
*/
private final int mMaxIterations;
/**
* The strategy for outputting strings to the display.
*/
private final OutputStrategy mOutputStrategy;
/**
* Define a pair of Handlers used to send/handle Messages via the
* HandlerThreads.
*/
// @@ TODO - you fill in here.
private Handler[] mHandlers = new Handler[PingPong.values().length];
/**
* Define a CyclicBarrier synchronizer that ensures the
* HandlerThreads are fully initialized before the ping-pong
* algorithm begins.
*/
// @@ TODO - you fill in here.
private CyclicBarrier mBarrier = new CyclicBarrier(PingPong.values().length);
/**
* Implements the concurrent ping/pong algorithm using a pair of
* Android Handlers (which are defined as an array field in the
* enclosing PlayPingPong class so they can be shared by the ping
* and pong objects). The class (1) extends the HandlerThread
* superclass to enable it to run in the background and (2)
* implements the Handler.Callback interface so its
* handleMessage() method can be dispatched without requiring
* additional subclassing.
*/
class PingPongThread extends HandlerThread implements Handler.Callback {
/**
* Keeps track of whether this Thread handles "pings" or
* "pongs".
*/
private PingPong mMyType;
/**
* Number of iterations completed thus far.
*/
private int mIterationsCompleted;
/**
* Constructor initializes the superclass and type field
* (which is either PING or PONG).
*/
public PingPongThread(PingPong myType) {
super(myType.toString());
// @@ TODO - you fill in here.
this.mMyType = myType;
}
/**
* This hook method is dispatched after the HandlerThread has
* been started. It performs ping-pong initialization prior
* to the HandlerThread running its event loop.
*/
@Override
protected void onLooperPrepared() {
// Create the Handler that will service this type of
// Handler, i.e., either PING or PONG.
// @@ TODO - you fill in here.
if (mMyType.equals(PingPong.PING)) {
mHandlers[PingPong.PING.ordinal()] = new Handler(this);
} else {
mHandlers[PingPong.PONG.ordinal()] = new Handler(this);
}
try {
// Wait for both Threads to initialize their Handlers.
// @@ TODO - you fill in here.
mBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
// Start the PING_THREAD first by (1) creating a Message
// where the PING Handler is the "target" and the PONG
// Handler is the "obj" to use for the reply and (2)
// sending the Message to the PING_THREAD's Handler.
// @@ TODO - you fill in here.
if (mMyType.equals(PingPong.PING)) {
Handler pingHandler = mHandlers[PingPong.PING.ordinal()];
Handler pongHandler = mHandlers[PingPong.PONG.ordinal()];
Message msg = pingHandler.obtainMessage(
PingPong.PING.ordinal(), pongHandler);
pingHandler.sendMessage(msg);
}
}
/**
* Hook method called back by HandlerThread to perform the
* ping-pong protocol concurrently.
*/
@Override
public boolean handleMessage(Message reqMsg) {
// Print the appropriate string if this thread isn't done
// with all its iterations yet.
// @@ TODO - you fill in here, replacing "true" with the
// appropriate code.
PingPong pingPongType = PingPong.values()[reqMsg.what];
if (mIterationsCompleted < mMaxIterations) {
mIterationsCompleted ++;
mOutputStrategy.print(pingPongType.name() + "(" + mIterationsCompleted
+ ")");
} else {
// Shutdown the HandlerThread to the main PingPong
// thread can join with it.
// @@ TODO - you fill in here.
getLooper().quit();
}
// Create a Message that contains the Handler as the
// reqMsg "target" and our Handler as the "obj" to use for
// the reply.
// @@ TODO - you fill in here.
Handler replyToHandler = (Handler) reqMsg.obj;
PingPong otherType;
if (pingPongType.equals(PingPong.PING)) {
otherType = PingPong.PONG;
} else {
otherType = PingPong.PING;
}
Message msg = replyToHandler.obtainMessage(otherType.ordinal(),
mHandlers[otherType.ordinal()]);
// Return control to the Handler in the other
// HandlerThread, which is the "target" of the msg
// parameter.
// @@ TODO - you fill in here.
replyToHandler.sendMessage(msg);
return true;
}
}
/**
* Constructor initializes the data members.
*/
public PlayPingPong(int maxIterations,
OutputStrategy outputStrategy) {
// Number of iterations to perform pings and pongs.
mMaxIterations = maxIterations;
// Strategy that controls how output is displayed to the user.
mOutputStrategy = outputStrategy;
}
/**
* Start running the ping/pong code, which can be called from a
* main() method in a Java class, an Android Activity, etc.
*/
public void run() {
// Let the user know we're starting.
mOutputStrategy.print("Ready...Set...Go!");
// Create the ping and pong threads.
// @@ TODO - you fill in here.
PingPongThread pingThread = new PingPongThread(PingPong.PING);
PingPongThread pongThread = new PingPongThread(PingPong.PONG);
// Start ping and pong threads, which cause their Looper to
// loop.
// @@ TODO - you fill in here.
pingThread.start();
pongThread.start();
// Barrier synchronization to wait for all work to be done
// before exiting play().
// @@ TODO - you fill in here.
try {
pingThread.join();
pongThread.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
// Let the user know we're done.
mOutputStrategy.print("Done!");
}
}