我是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)
答案 0 :(得分:0)
只有创建视图层次结构的原始线程才能触及它 视图。
您的片段正在一个线程上创建视图,并且您在(不同的)消息接收器线程中调用notifyDataSetChanged。
当日志中的调用堆栈显示时,notifyDataSetChanged
会导致调用requestLayout
,并尝试更新相关视图。
您需要确保notifyDataSetChanged
调用在创建视图的同一个线程上运行。您可以通过为父Activity调用runOnUiThread
来执行此操作。
答案 1 :(得分: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();
}
});
}