服务套接字断开背景 - >前景切换

时间:2014-06-04 22:29:59

标签: java android sockets service

我正在写一个IRC客户端。与IRC服务器的套接字连接通过服务处理。我已经设法在方向更改期间稳定了有问题的活动的所有UI元素,但是在更改期间,服务维护的套接字在某种程度上被关闭。

以下是我认为相关的代码。如果您需要了解更多信息,请与我们联系。

//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) {
                if (line.toUpperCase().startsWith("PING ")) {
                    SendMessage("PONG " + line.substring(5));
                }
                else
                    queueMessage(line);
            }
        }
        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()
{
    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.
        if(server.PASS != "")
            SendMessage("PASS " + server.PASS);

        //write nickname
        SendMessage("NICK " + server.NICK);

        //write username login
        SendMessage("USER " + server.NICK + " 0 * :Fluffy IRC");

        String line;
        while ((line = reader.readLine( )) != null) {
            if (line.indexOf("004") >= 0) {
                // We are now logged in.
                break;
            }
            else if (line.indexOf("433") >= 0) {
                //change to alt Nick
                if(!server.NICK.equals(server.ALT_NICK) && !server.ALT_NICK.equals(""))
                {
                    server.NICK = server.ALT_NICK;
                    SendMessage("NICK " + server.NICK);
                }
                else
                {
                    queueMessage("Nickname already in use");
                    socket.close();
                    return false;
                }
            }
            else if (line.toUpperCase().startsWith("PING ")) {
                SendMessage("PONG " + line.substring(5));
            }
            else
            {
                queueMessage(line);
            }
        }

        //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();
        Toast.makeText(main_tab_page.this, "Connected", Toast.LENGTH_SHORT)
          .show();
        synchronized (_serviceConnWait) {
            _serviceConnWait.notify();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        conn = null;
    }
  };

@Override
protected void onSaveInstanceState(Bundle state){
    super.onSaveInstanceState(state);
    state.putParcelable("Server", server);
    state.putString("Window", CurrentTabWindow.GetName());

    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);

            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;
}

我不完全确定我在那里做错了什么。显然有一些我想念的东西,还没有找到,或者甚至没想过要检查。但是,在方向更改后,我的发送命令会给我这条消息并且没有任何反应:

06-04 22:02:27.637: W/System.err(1024): java.net.SocketException: Socket closed
06-04 22:02:27.982: W/System.err(1024): at com.fluffyirc.ConnectionService.SendMessage(ConnectionService.java:90)

我不知道套接字什么时候关闭,或者为什么。

更新

我已经更改了代码,而不是绑定到服务并使用它来启动它,而是在适当的位置调用startServicestopService以及绑定到它,在思想上绑定丢失时服务正在被销毁。这与我改变它之前的工作方式完全一样。套接字在方向改变时仍然关闭,我不明白为什么。

更新: - 代码和说明

我添加了最近为Start / Stop服务和START_STICKY所做的代码更改。我最近还阅读了一篇非常好的文章,解释了方向更改流程如何工作以及为什么将android:configChanges="orientation|screenSize"行添加到清单中并不是一个坏主意。所以这解决了方向问题,但如果我将活动置于后台模式,然后将其恢复到前台,它仍然会做同样的事情。这仍然遵循相同的保存/销毁/创建过程,方向没有该清单行......它仍然关闭我的套接字,我仍然不知道为什么。

我知道它在重新创建过程之前不会关闭套接字...我知道这是因为消息队列将显示应用程序在后台时收到的消息,但是一旦我将其恢复转发它关闭套接字,其他任何东西都不能发送或接收。

1 个答案:

答案 0 :(得分:1)

&#39;套接字关闭&#39;意味着关闭套接字然后继续使用它。它不是“断开连接”。

你需要在catch块中添加一些东西。永远不要忽略异常。当你看到异常实际上是什么时,你可能会感到惊讶。

NB Socket.isConnected()并未告诉您关于连接状态的任何信息:只知道您是否曾连接过Socket.,所以它返回true。