[Java for Android App Development] FluffyNevyn:
I am writing an IRC Client, and so far as long as I dont send the app to the background and try to restore it it works fine. Tabs for multiple channels, the connected socket is in a bound service (started separately via INTENT and a startService call), etc and so on.

However, whenever I send the app to the background, then bring it back forward, the socket closes. I would have the same issue with screen rotation but I found the config setting that stops it from going through destroy/create on rotation. If I figure this out I may actually get rid of that since the issue will have been solved.

The other issue I seem to be having is that it takes a long time to re-bind to the service, and I have no idea why (the initial binding and startup is pretty quick, but re-binding to it seems to take forever, and when It does re-bind, the socket is closed).

Here are the code samples that I feel to be relevant, let me know if there's something more specific you want to see.

//This is the Service in question
public class ConnectionService extends Service{

private BlockingQueue<String> MessageQueue;

public final IBinder myBind = new ConnectionBinder();

public class ConnectionBinder extends Binder {
ConnectionService getService() {
return ConnectionService.this;
}
}

private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;

private IRCServer server;

private WifiManager.WifiLock wLock;

private Thread readThread = new Thread(new Runnable() {

@Override
public void run() {
try {
String line;
while ((line = reader.readLine( )) != null) {
//message parsing stuff
}
}
catch (Exception e) {}
}

});

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

if(MessageQueue == null)
MessageQueue = new LinkedBlockingQueue<String>();

return Service.START_STICKY;
}

@Override
public IBinder onBind(Intent arg0) {
return myBind;
}

@Override
public boolean stopService(Intent name) {
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return super.stopService(name);
}

@Override
public void onDestroy()
{//I put this here so I had a breakpoint in place to make sure this wasn't firing instead of stopService
try {
socket.close();
wLock.release();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

super.onDestroy();
}

public void SendMessage(String message)
{
try {
writer.write(message + "\r\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}

public String readLine()
{//this is called by the activity which consumes the service. Its just an accessor to MessageQueue
try {
if(!isConnected())
return null;
else
return MessageQueue.take();
} catch (InterruptedException e) {
return "";
}
}

public boolean ConnectToServer(IRCServer newServer)
{
try {
//create a new message queue (connecting to a new server)
MessageQueue = new LinkedBlockingQueue<String>();

//lock the wifi
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "LockTag");
wLock.acquire();

server = newServer;

//connect to server
socket = new Socket();
socket.setKeepAlive(true);
socket.setSoTimeout(60000);

socket.connect(new InetSocketAddress(server.NAME, Integer.parseInt(server.PORT)), 10000);

writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//run basic login scripts.

String line;
while ((line = reader.readLine( )) != null) {
//server initialization stuff
}

//start the reader thread AFTER the primary login!!!
CheckStartReader();

if(server.START_CHANNEL == null || server.START_CHANNEL == "")
{
server.WriteCommand("/join " + server.START_CHANNEL);
}
//we're done here, go home everyone
} catch (NumberFormatException e) {
return false;
} catch (IOException e) {
return false;
}

return true;

}

private void queueMessage(String line) {
try {
MessageQueue.put(line);
} catch (InterruptedException e) {
}
}

public boolean isConnected()
{
return socket.isConnected();
}

public void CheckStartReader()
{
if(this.isConnected() && !readThread.isAlive())
readThread.start();
}
}

//Here are the relevant portions of the hosting Activity that connects to the service
//NOTE: THE FOLLOWING CODE IS PART OF THE ACTIVITY, NOT THE SERVICE
private ConnectionService conn;

private ServiceConnection mConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
conn = ((ConnectionService.ConnectionBinder)service).getService();
//debug toast
Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT)
.show();
synchronized (_serviceConnWait) {
_serviceConnWait.notify();
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
conn = null;//does this even run? Breakpoint here
}
};

@Override
protected void onSaveInstanceState(Bundle state){
super.onSaveInstanceState(state);
state.putParcelable("Server", server);
state.putString("Window", CurrentTabWindow.GetName());
//have to unbind, othewise we get that leaked service exception
unbindService(mConnection);
}

@Override
protected void onDestroy()
{
super.onDestroy();
if(this.isFinishing())
stopService(new Intent(this, ConnectionService.class));
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab_page);

localTabHost = (TabHost)findViewById(R.id.tabHostMain);
localTabHost.setup();
localTabHost.setOnTabChangedListener(new tabChange());

_serviceConnWait = new Object();

if(savedInstanceState == null)
{//initial startup, coming from Intent to start
//get server definition
server = (IRCServer)this.getIntent().getParcelableExtra(IRC_WINDOW);
server.addObserver(this);
AddTabView(server);

//this should only run the first time, all other calls to OnCreate should have something in SavedInstanceState
startService(new Intent(this, ConnectionService.class));
}
else
{
server = (IRCServer)savedInstanceState.getParcelable("Server");
String windowName = savedInstanceState.getString("Window");

//Add Needed Tabs
//Server
if(!(windowName.equals(server.GetName())))
AddTabView(server);
//channels
for(IRCChannel c : server.GetAllChannels())
if(!(windowName.equals(c.GetName())))
AddTabView(c);
//reset each view's text (handled by tabChange)

if(windowName.equals(server.GetName()))
SetCurrentTab(server.NAME);
else
SetCurrentTab(windowName);

ResetMainView(CurrentTabWindow.GetWindowTextSpan());

//Rebind to service
BindToService(new Intent(this, ConnectionService.class));
}
}

@Override
protected void onStart()
{
super.onStart();

final Intent ServiceIntent = new Intent(this, ConnectionService.class);

//check start connection service
final Thread serverConnect = new Thread(new Runnable() {

@Override
public void run() {

if(!BindToService(ServiceIntent))
return;

server.conn = conn;
conn.ConnectToServer(server);
server.StartReader();

if(server.START_CHANNEL != null && !server.START_CHANNEL.equals(""))
{
IRCChannel chan = server.FindChannel(server.START_CHANNEL);

if(chan != null)
{
AddTabView(chan);
}
else
{
server.JoinChannel(server.START_CHANNEL);
chan = server.FindChannel(server.START_CHANNEL);
AddTabView(chan);
}

}
}

});

serverConnect.start();
}

private boolean BindToService(Intent ServiceIntent)
{
int tryCount = 0;
bindService(ServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
while(conn == null && tryCount < 10)
{
tryCount++;
try {
synchronized (_serviceConnWait) {
_serviceConnWait.wait(1500);
}
}
catch (InterruptedException e) {
//do nothing
}
}

return conn != null;
}

Logcat...well...there isn't really any exception thrown, the code runs just fine...except that it closes the socket. I suppose that counts as an exception. Whenever I run the socket write command It throws a "Socket Closed" exception at me. No other crash involved.