I am developing an Android app in which there is going to be a lot of network communication between the app and my server.
To Accomplish this, I am using SocketChannel
with Selector
, to do the non-blocking IO.
The design I have chosen is, that there will be a BlockingQueue
on which a 'NetworkIOManager' thread will be waiting. Other threads of the app will be posting messages to that BlockingQueue
and the NetworkIOManager
will pickup those messages and send it to the other thread AsyncRequestHandlerThread
.
Therefore the main responsibility of NetworkIOManager
thread is to pick the messages from BlockingQueue
and delegate them to the AsyncRequestHandlerThread
to send the requests and receive the responses.
Code for NetworkIOManager.java
:
public class NetworkIOManager implements Runnable
{
private AsyncRequestHandlerThread handlerThread = null;
/*
*
* some code here
*
*/
private void vSendRequestUsingSocketChannel(String pTargetURL, int pTargetPort, String pRequestXML, boolean pUseSameConn) {
// if thread is not created, initialize the thread
if(handlerThread == null) {
handlerThread = new AsyncRequestHandlerThread();
}
// create a channel to send the request and register it with the selector
AsyncRequestHandlerThread.createChannelWithSelector(pTargetURL, pTargetPort, pRequestXML);
// if thread is not started, start it.
if(!handlerThread.isAlive())
handlerThread.start();
}
}
The AsyncRequestHandlerThread
basically creates a SocketChannel
for each request to be send, with a Non-Blocking
configuration and registers it with a single Selector
associated with this thread.
Code for AsyncRequestHandlerThread.java
:
public class AsyncRequestHandlerThread extends Thread {
private static Selector selector = null;
public AsyncRequestHandlerThread() {
if(selector == null)
vSetSelector();
}
private static void vSetSelector()
{
try {
selector = Selector.open();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static Selector getSelector()
{
return selector;
}
public static void createChannelWithSelector(String pTargetURL, int pTargetPort, String pRequestXML) {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(pTargetURL, pTargetPort));
socketChannel.register(selector, SelectionKey.OP_CONNECT, pRequestXML);
}
catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
// Wait for events with TIMEOUT : 30 secs
while (selector.select(30000) > 0) {
try {
// Get list of selection keys with pending events
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
// Process each key at a time
while (iterator.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)iterator.next();
// Remove it from the list to indicate that it is being processed
iterator.remove();
if (selKey.isValid() && selKey.isConnectable()) {
// Get channel with connection request
SocketChannel sChannel = (SocketChannel)selKey.channel();
boolean success = sChannel.finishConnect();
if (success) {
sChannel.register(selector, SelectionKey.OP_WRITE, selKey.attachment());
}
else {
// An error occurred; handle it
// Unregister the channel with this selector
selKey.cancel();
}
}
else if(selKey.isValid() && selKey.isWritable()) {
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Writing to a SocketChannel
ByteBuffer requestBuffer = null;
requestBuffer = ByteBuffer.wrap(selKey.attachment().toString().getBytes(Charset.forName("UTF-8")));
sChannel.write(requestBuffer);
sChannel.register(selector, SelectionKey.OP_READ);
}
else if (selKey.isValid() && selKey.isReadable()) {
// Get channel with bytes to read
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Reading from a SocketChannel
ByteBuffer responseBuffer = ByteBuffer.allocate(15);
while(sChannel.read(responseBuffer) > 0) {
String responseString = new String(responseBuffer.array(), Charset.forName("UTF-8"));
Log.d("STATS", responseString);
}
sChannel.close();
}
}
}
catch(IOException e)
{
e.printStackTrace();
}
catch(CancelledKeyException e) {
e.printStackTrace();
}
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
The issue I am facing is when I ran the application on my device I got an exception java.lang.IllegalThreadStateException
on the line handlerThread.start();
in the class NetworkIOManager
. When I am debugging this, the application works fine.
I am not able to understand where the problem lies and how can it be resolved ?
Any suggestions ?
You're trying to start the thread after it has exited. You need to rethink your logic. It presently assumes there can only be one such thread and that it never exits, which isn't true.