Java 现有TCP套接字类在更新为新API时引发NetworkOnMainThreadException

Java 现有TCP套接字类在更新为新API时引发NetworkOnMainThreadException,java,android,sockets,Java,Android,Sockets,我继承了一个Android应用程序,正在更新它以获得最新的API。 当我创建套接字时,我得到了NetworkOnMainThreadException。我读过很多解决方案,它们说使用AsyncTask。但是,AsyncTask应仅用于短时间运行的任务。这是一个标准的TCP连接,将持续运行应用程序的生命周期。这是双向的 我如何使用线程更新类,以便不出现异常并能够通过套接字发送数据 public class TcpConnection { enum State { Closed,

我继承了一个Android应用程序,正在更新它以获得最新的API。 当我创建套接字时,我得到了
NetworkOnMainThreadException
。我读过很多解决方案,它们说使用
AsyncTask
。但是,
AsyncTask
应仅用于短时间运行的任务。这是一个标准的TCP连接,将持续运行应用程序的生命周期。这是双向的

我如何使用线程更新类,以便不出现异常并能够通过套接字发送数据

public class TcpConnection
{
    enum State
{
    Closed,
    Closing,
    Open
};

State _state;    
Socket _socket;
//data stream from the head-unit
DataInputStream _inputStream;
//data stream to the head-unit
DataOutputStream _outputStream;

Receiver _receiver;
Thread _thread;

OnTcpDataReceivedListener _onTcpDataReceived;

public TcpConnection()
{                 
     _state = State.Closed;
}

// Listen to this to be informed of what data has been received from a TCP / IP socket.
public void setOnTcpDataReceivedListener(OnTcpDataReceivedListener listener)   
{
    _onTcpDataReceived = listener;
}

// Used to inform the listener that data has been received from a TCP / IP socket.
public interface OnTcpDataReceivedListener
{
    public void onTcpDataReceived(byte[] buffer, int length);
}

// Try connecting to a given TCP / IP connection.
// Notes:
// Blocks until a connection is created.
// if connected
public synchronized void connect(String ipAddress, int port) 
throws IOException
{
    if (_state != State.Closed)
        return;

    try
    {
        _state = State.Open;

        _socket = new Socket(ipAddress, port);
        _inputStream = new DataInputStream(_socket.getInputStream());
        _outputStream = new DataOutputStream(_socket.getOutputStream());            
    }
    catch(IOException ex)
    {            
        //TODO: do better error handling
        ex.printStackTrace();

        if (_socket != null) {
            _socket.close();
            _socket = null;             
        }

        if (_inputStream != null) {
            _inputStream.close();
            _inputStream = null;
        }

        if (_outputStream != null) {
            _outputStream.close();
            _outputStream = null;
        }

        throw ex;
    }

    _receiver = new Receiver();

    _thread = new Thread(_receiver);
    _thread.setName("TcpConnection.Receiver");
    _thread.start();
}

public void write(byte[] buffer)
{
    if (_state != State.Open)
        return;

    try
    {
        _outputStream.write(buffer);
    }
    catch(IOException ex)
    {            
        //TODO: do better error handling
        ex.printStackTrace();            
    } 
}

public synchronized void close()
{
    _state = State.Closing;

    if (_socket != null && !_socket.isClosed())
    {
        try
        {
            _socket.close();
        }
        catch(IOException ex)
        {
            //TODO: do better error handling
            ex.printStackTrace();
        }
    }         
}

private class Receiver implements Runnable
{                                  
    @Override
    public void run() 
    {                        
        try 
        {
            byte[] buffer = new byte[512];
            int read = 0;

            while(_state == State.Open)
            {                    
                read = _inputStream.read(buffer);

                if (read > 0)
                {
                    if (_onTcpDataReceived != null)
                    {
                        _onTcpDataReceived.onTcpDataReceived(buffer, read);
                    }
                }
            }
        }
        catch (SocketException ex)
        {
            if (    !(_state != State.Open 
                    && ex.getMessage() != null 
                    && ex.getMessage().equalsIgnoreCase("socket closed")))
            {
                ex.printStackTrace();
            }
        }
        catch (IOException ex) 
        {
            //TODO: need error handling
            ex.printStackTrace();
        }            
        finally 
        {
            if (_socket != null && !_socket.isClosed())
            {
                try
                {
                    _socket.close();
                }
                catch (IOException ex) 
                {
                    //TODO: need error handling
                    ex.printStackTrace();
                }                    
            }
        }

        _state = State.Closed;
     }
}   

}您有两种选择:

  • 更新实际的Android类,该类使用
    TcpConnection
    将对
    TcpConnection
    的所有调用封装在一个单独的线程中:您可以创建一个
    Executor
    并使用它
  • 将处理所有传输和接收的
    Executor
    实例添加到
    TcpConnection
    。我认为您的问题在于
    connect
    函数:也许可以尝试将该代码包装在单独的Runnable中,然后在线程上启动它

  • 我已使TcpConnection类实现可运行,并删除了内部Receiver类。更改了connect函数以存储地址和端口,并将connect的现有内容移动到run函数的开头。我不再获得异常,可以通过套接字发送和接收信息。就是这样:基本上你根本不能执行任何网络活动,直接与任何UI活动连接/侦听/发送(因此不能通过任何按钮按下或处理程序或任何其他方式)。这是相同的症状。我还没有找到当前创建不在主线程上的套接字的示例。即使是最近的示例也使用较旧的API版本来避免异常。如果有一个不回避这个问题的例子就好了。上面链接问题的顶部答案包括一个例子。它确实有一个使用AsyncTask的例子。关于AsyncTask的文档说,它应该用于短任务,长度为几秒钟。所以我一直在寻找一个不使用AsyncTask但使用线程的示例。