0

我是 Java 中 TCP 协议的新手。我想做一个服务器-客户端聊天界面。我可以将消息从客户端发送到服务器,但是当我从服务器向客户端发送消息时,我的 android 应用程序突然崩溃。这是我的代码..Android客户端..

public class chatWithServer extends Fragment
{
    private ListView mList;
    private ArrayList<String> arrayList;
    private MyCustomAdapter mAdapter;

   Button send;
   EditText editText;
   String serverMessage;
    PrintWriter out;
    BufferedReader in;
    private static final String HOST = "192.168.48.1";  
    private static final int PORT = 5000; 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        //super.onCreate(savedInstanceState);
        final View myFragmentView = inflater.inflate(
                R.layout.activity_main, container, false);
        arrayList = new ArrayList<String>();

         editText = (EditText)myFragmentView. findViewById(R.id.editText);
        send = (Button)myFragmentView.findViewById(R.id.send_button);
       send.setOnClickListener(onClickSend(myFragmentView));

        //relate the listView from java to the one created in xml
        mList = (ListView)myFragmentView.findViewById(R.id.list);
        mAdapter = new MyCustomAdapter(getActivity(), arrayList);
        mList.setAdapter(mAdapter);


        connectServerTask();

        return myFragmentView;

    }
    //-------->By clicking Send Button<---------//////
    private OnClickListener onClickSend(final View myFragmentView) {
        return new OnClickListener() {

            @Override
            public void onClick(View v) {

                String message = editText.getText().toString();

                //add the text in the arrayList
                arrayList.add("c: " + message);

                //sends the message to the server
                sendToServer(message);

                //refresh the list
                mAdapter.notifyDataSetChanged();
                editText.setText("");
            }


        };
    }

    private void connectServerTask() {
        Runnable runnable= new Runnable() {

            @Override
            public void run() {

                 setSocket();
            }
            private void setSocket() {
                try {
                    // Create Socket instance
                    Socket socket = new Socket(HOST, PORT);

                    out = new PrintWriter(new BufferedWriter(new                 OutputStreamWriter(socket.getOutputStream())), true);
                    out.println("Connected to Client");
                    try{
                    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    serverMessage =in.readLine();
                    if(serverMessage!=null) {
                         //sends the message to the server
                     chatUp(serverMessage);
                    }
                    serverMessage=null;



                } catch (UnknownHostException e) {
                    e.printStackTrace();
                } finally{
                    socket.close();
                }
                    }catch (IOException e) {
                    e.printStackTrace();
                }

            }
                         private void chatUp(String message) {

               arrayList.add(message);
               mAdapter.notifyDataSetChanged();
            }           

        };
        new Thread(runnable).start(); 

    }
    //method of sending message to server
public void sendToServer(String message){



      if (out != null && !out.checkError())
          {
              out.println(message);
              out.flush();
           };
    }

    }

Here is the code for Java-Server

    public class TCPServer extends Thread {

        public static final int SERVERPORT = 5000;
        private boolean running = false;
        private PrintWriter mOut;
        private OnMessageReceived messageListener;

        public static void main(String[] args) {

            //opens the window where the messages will be received and sent
            ServerBoard frame = new ServerBoard();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);

        }

        /**
     * Constructor of the class
     * @param messageListener listens for the messages
     */
    public TCPServer(OnMessageReceived messageListener) {
        this.messageListener = messageListener;
    }


    /**
     * Method to send the messages from server to client
     * @param message the message sent by the server
     */
    public void sendMessage(String message){
        if (mOut != null && !mOut.checkError()) {
            mOut.println(message);
            mOut.flush();
        }
    }

    @SuppressWarnings("resource")
    @Override
    public void run() {
        super.run();

        running = true;

        try {
            System.out.println("S: Connecting...");

            //create a server socket. A server socket waits for requests to come in over the network.
            ServerSocket serverSocket = new ServerSocket(SERVERPORT);

            //create client socket... the method accept() listens for a connection to be made to this socket and accepts it.
            Socket client = serverSocket.accept();
            System.out.println("S: Receiving...");

             try {

                //sends the message to the client
                mOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);

                //read the message received from client
                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

                //in this while we wait to receive messages from client (it's an infinite loop)
                //this while it's like a listener for messages
                while (running) {
                    String message = in.readLine();

                    if (message != null && messageListener != null) {
                        //call the method messageReceived from ServerBoard class
                        messageListener.messageReceived(message);
                    }
                }

            } catch (Exception e) {
                System.out.println("S: Error");
                e.printStackTrace();
            } finally {
                client.close();
                System.out.println("S: Done.");
            }

        } catch (Exception e) {
            System.out.println("S: Error");
            e.printStackTrace();
        }

    }


    //Declare the interface. The method messageReceived(String message) will must be implemented in the ServerBoard
    //class at on startServer button click
    public interface OnMessageReceived {
        public void messageReceived(String message);
    }

}

ServerBoard.java(用于服务器)

public class ServerBoard extends JFrame {//jFrame is for the desplaying the window and for nice GUI
    private JTextArea messagesArea;//A JTextArea is a multi-line area that displays plain text.
    private JButton sendButton;
    private JTextField message;//JTextField is a lightweight component that allows the editing of a single line of text.
    private JButton startServer;
    private TCPServer mServer;

    public ServerBoard() {

        super("ServerBoard");

        JPanel panelFields = new JPanel();
        panelFields.setLayout(new BoxLayout(panelFields,BoxLayout.X_AXIS));

        JPanel panelFields2 = new JPanel();
        panelFields2.setLayout(new BoxLayout(panelFields2,BoxLayout.X_AXIS));

        //here we will have the text messages screen
        messagesArea = new JTextArea();
        messagesArea.setColumns(30);
        messagesArea.setRows(10);
        messagesArea.setEditable(false);

        sendButton = new JButton("Send");
        sendButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // get the message from the text view
                String messageText = message.getText();
                // add message to the message area
                messagesArea.append("\n" + messageText);
                // send the message to the client
                mServer.sendMessage(messageText);
                // clear text
                message.setText("");
            }
        });

        startServer = new JButton("Start");
        startServer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // disable the start button
                startServer.setEnabled(false);

                //creates the object OnMessageReceived asked by the TCPServer constructor
                mServer = new TCPServer(new TCPServer.OnMessageReceived() {
                    @Override
                    //this method declared in the interface from TCPServer class is implemented here
                    //this method is actually a callback method, because it will run every time when it will be called from
                    //TCPServer class (at while)
                    public void messageReceived(String message) {
                        messagesArea.append("\n "+message);
                    }
                });
                mServer.start();

            }
        });

        //the box where the user enters the text (EditText is called in Android)
        message = new JTextField();
        message.setSize(200, 20);

        //add the buttons and the text fields to the panel
        panelFields.add(messagesArea);
        panelFields.add(startServer);

        panelFields2.add(message);
        panelFields2.add(sendButton);

        getContentPane().add(panelFields);
        getContentPane().add(panelFields2);


        getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));

        setSize(300, 170);
        setVisible(true);
    }
}

--已编辑--

10-24 17:04:07.039: E/AndroidRuntime(15911): FATAL EXCEPTION: Thread-1131
10-24 17:04:07.039: E/AndroidRuntime(15911): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5908)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:837)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.view.View.requestLayout(View.java:15792)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.widget.AbsListView.requestLayout(AbsListView.java:1837)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:813)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.widget.AbsListView$AdapterDataSetObserver.onChanged(AbsListView.java:5998)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at com.example.cardioapp.database.chatWithServer$2.display(chatWithServer.java:142)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at com.example.cardioapp.database.chatWithServer$2.setSocket(chatWithServer.java:123)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at com.example.cardioapp.database.chatWithServer$2.run(chatWithServer.java:94)
10-24 17:04:07.039: E/AndroidRuntime(15911):    at java.lang.Thread.run(Thread.java:841)
4

2 回答 2

0

只有创建视图层次结构的原始线程才能接触其视图。

您的片段正在一个线程上创建视图,并且您正在(不同的)消息接收器线程中调用 notifyDataSetChanged。

正如日志中的调用堆栈所示,notifyDataSetChanged导致调用requestLayout尝试更新关联的视图。

您需要确保notifyDataSetChanged调用在创建视图的同一线程上运行。您可以通过调用runOnUiThread父 Activity 来完成此操作。

于 2013-10-24T15:44:34.363 回答
0

在google for baseAdapter之后,我理解了您的观点。我更新了负责更新baseadapter的显示方法。

private void display(String... message) {

                arrayList.add(message[0]);
                 Log.e("setSocket", "arraylist");
                getActivity().runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        Log.e("display", "runonuithread");
                        mAdapter.notifyDataSetChanged();

                    }
                });

    }
于 2013-10-25T10:08:36.913 回答