Reputation: 638
I know that the exception I am getting in the log cat has A VERY STRAIGHTFORWARD name. My only problem is that I can't identify what is causing my exception. I have created an PC-Android multiclient chat.
The PC version works 100% with the server and the client. I implemented the same technique from the PC client to the android client and chatting WORKS.
The only problem I am getting, is when I connect with the android, the clientArea (tracks all users connected), is not tracking the other names, but rather multiples of the android client username. ie. I connect ONLY on android with username:Jurko, the clientArea will have 2 people called Jurko instead of only having one.
Logcat:
10-11 17:19:08.221: ERROR/AndroidRuntime(12379): FATAL EXCEPTION: main
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
at com.example.JurkoAndroidChat.MyActivity$ServerTask.onProgressUpdate(MyActivity.java:175)
at com.example.JurkoAndroidChat.MyActivity$ServerTask.onProgressUpdate(MyActivity.java:130)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:647)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4895)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:994)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:761)
at dalvik.system.NativeStart.main(Native Method)
Android Client:
package com.example.JurkoAndroidChat;
import android.app.Activity;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.view.View;
import java.net.*;
import java.io.*;
import java.util.*;
public class MyActivity extends Activity {
/**
* Called when the activity is first created.
*/
// Right here, we connecting the components of the main.xml form to code
Button connectButton, disconnectButton, sendButton;
TextView chatArea, clientArea;
EditText messageField, usernameField, ipField;
//Extra variables and sockets
String username, serverIP;
int Port = 5000;
Socket sock;
PrintWriter out;
BufferedReader in;
ArrayList<String> userList = new ArrayList();
Boolean isConnected = false, exceptionCaught = false;
ServerTask serverTask;
@Override
public void onCreate(Bundle savedInstanceState) {
System.out.println("Working?");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
connectButton = (Button)findViewById(R.id.button);
sendButton = (Button)findViewById(R.id.button1);
disconnectButton = (Button)findViewById(R.id.button2);
chatArea = (TextView)findViewById(R.id.textView2);
clientArea = (TextView)findViewById(R.id.textView3);
messageField = (EditText)findViewById(R.id.editText2);
usernameField = (EditText)findViewById(R.id.editText);
ipField = (EditText)findViewById(R.id.editText1);
connectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//To change body of implemented methods use File | Settings | File Templates.
if (isConnected == false) {
username = usernameField.getText().toString();
usernameField.setFocusable(false);
usernameField.setClickable(false);
serverIP = ipField.getText().toString();
ipField.setFocusable(false);
ipField.setClickable(false);
serverTask = new ServerTask();
serverTask.execute();
} else if (isConnected == true) {
chatArea.append("You are already connected to the server.\n");
}
}
});
disconnectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//To change body of implemented methods use File | Settings | File Templates.
String bye = (username + "µ µDisconnect");
try {
out.print(bye);
out.flush();
chatArea.append("Disconnected.\n");
sock.close();
} catch (Exception e) { } // nothing caught so far.
isConnected = false;
usernameField.setFocusable(true);
usernameField.setClickable(true);
usernameField.setFocusableInTouchMode(true);
ipField.setFocusable(true);
ipField.setFocusableInTouchMode(true);
ipField.setClickable(true);
clientArea.setText("");
}
});
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//To change body of implemented methods use File | Settings | File Templates.
String nothing = "";
if ((messageField.getText().toString().equals(nothing))) {
messageField.setText("");
messageField.requestFocus();
} else {
try {
out.println(username + "µ" + messageField.getText().toString() + "µ" + "Chat");
out.flush();
} catch (Exception e) {
Log.e("Error", e.toString());
chatArea.append("Message was not sent.\n" + e);
}
messageField.setText("");
messageField.requestFocus();
}
}
});
}
public class ServerTask extends AsyncTask<Void, Void, Void> {
String[] data;
String stream, done = "Done", connect = "Connect", disconnect = "Disconnect", chat = "Chat";
@Override
protected Void doInBackground(Void... voids) {
try {
Log.i("Asynctask", "doInBackground");
sock = new Socket(serverIP, Port);
out = new PrintWriter(sock.getOutputStream());
in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
out.println(username + "µ" + "has connected." + "µ" + "Connect");
out.flush();
isConnected = true;
} catch (Exception ex) {
exceptionCaught = true;
Log.e("Application", ex.toString());
}
try {
while ((stream = in.readLine()) != null) {
publishProgress();
}
} catch (Exception e) {}
return null;
}
public void writeUsers() {
clientArea.setText("");
Iterator<String> iterator = userList.iterator();
while (iterator.hasNext()) {
String token = iterator.next();
clientArea.append(token + '\n');
}
}
@Override
protected void onProgressUpdate(Void... values) {
data = stream.split("µ");
if (data[2].equals(chat)) {
if (data[1].equals("has connected.") || data[1].equals("has disconnected."))
chatArea.append(data[0] + " " + data[1] + '\n');
else
chatArea.append(data[0] + ": " + data[1] + '\n');
} else if (data[2].equals(connect)) {
userList.add(data[0]);
writeUsers();
} else if (data[2].equals(disconnect)) {
userList.remove(data[0]);
writeUsers();
} else if (data[2].equals(done))
userList.clear();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if (exceptionCaught == true) {
chatArea.append("Unable to connect to " + serverIP + " at port " + Port + ".\n");
usernameField.setFocusable(true);
usernameField.setClickable(true);
usernameField.setFocusableInTouchMode(true);
ipField.setFocusableInTouchMode(true);
ipField.setFocusable(true);
ipField.setClickable(true);
exceptionCaught = false;
}
//To change body of overridden methods use File | Settings | File Templates.
}
}
}
Server (On PC):
/*
* To change this template, choose Tools : Templates
* and open the template in the editor.
*/
/*
* ServerWindow.java
*
* Created on Apr 23, 2011, 4:16:05 PM
*/
import java.io.*;
import java.net.*;
import java.util.*;
/**
*
* @author JurkoGuba
*/
public class ServerWindow extends javax.swing.JFrame {
ArrayList clientOutputStreams;
ArrayList<String> onlineUsers;
public class ClientHandler implements Runnable {
BufferedReader reader;
Socket sock;
PrintWriter client;
public ClientHandler(Socket clientSocket, PrintWriter user) {
// new inputStreamReader and then add it to a BufferedReader
client = user;
try {
sock = clientSocket;
reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
} // end try
catch (Exception ex) {
outputPane.append("Error beginning StreamReader. \n");
} // end catch
} // end ClientHandler()
public void run() {
String message, connect = "Connect", disconnect = "Disconnect", chat = "Chat" ;
String[] data;
try {
while ((message = reader.readLine()) != null) {
outputPane.append("Received: " + message + "\n");
data = message.split("µ");
if (data[2].equals(connect)) {
tellEveryone((data[0] + "µ" + data[1] + "µ" + chat));
userAdd(data[0]);
} else if (data[2].equals(disconnect)) {
tellEveryone((data[0] + "µhas disconnected." + "µ" + chat));
userRemove(data[0]);
} else if (data[2].equals(chat)) {
tellEveryone(message);
} else {
outputPane.append("No Conditions were met. \n");
}
} // end while
} // end try
catch (Exception ex) {
outputPane.append("Lost a connection. \n");
ex.printStackTrace();
clientOutputStreams.remove(client);
} // end catch
} // end run()
} // end class ClientHandler
/** Creates new form ServerWindow */
public ServerWindow() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
outputPane = new javax.swing.JTextArea();
startButton = new javax.swing.JButton();
stopButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("House Server");
outputPane.setColumns(20);
outputPane.setEditable(false);
outputPane.setLineWrap(true);
outputPane.setRows(5);
outputPane.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
jScrollPane1.setViewportView(outputPane);
startButton.setText("Start");
startButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
startButtonActionPerformed(evt);
}
});
stopButton.setText("Stop");
stopButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stopButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(startButton, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(stopButton, javax.swing.GroupLayout.DEFAULT_SIZE, 183, Short.MAX_VALUE))
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 229, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(startButton)
.addComponent(stopButton))
.addContainerGap(19, Short.MAX_VALUE))
);
pack();
}// </editor-fold>
private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Thread starter = new Thread(new ServerStart());
starter.start();
outputPane.append("Server started. \n");
}
private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
tellEveryone("Serverµis stopping and all users will be disconnected.\nµChat");
outputPane.append("Server stopping... \n");
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ServerWindow().setVisible(true);
}
});
}
public class ServerStart implements Runnable {
public void run() {
clientOutputStreams = new ArrayList();
onlineUsers = new ArrayList();
try {
ServerSocket serverSock = new ServerSocket(5000);
while (true) {
// set up the server writer function and then begin at the same
// the listener using the Runnable and Thread
Socket clientSock = serverSock.accept();
PrintWriter writer = new PrintWriter(clientSock.getOutputStream());
clientOutputStreams.add(writer);
// use a Runnable to start a 'second main method that will run
// the listener
Thread listener = new Thread(new ClientHandler(clientSock, writer));
listener.start();
outputPane.append("Got a connection. \n");
} // end while
} // end try
catch (Exception ex)
{
outputPane.append("Error making a connection. \n");
} // end catch
} // end go()
}
public void userAdd (String data) {
String add = "µ µConnect", done = "Serverµ µDone", name = data;
outputPane.append("Before " + name + " added. \n");
onlineUsers.add(name);
outputPane.append("After " + name + " added. \n");
tellEveryone(done);
Iterator<String> iterator = onlineUsers.iterator();
while (iterator.hasNext()){
String token = iterator.next();
tellEveryone(token + add);
}
}
public void userRemove (String user) {
String add = "µ µDisconnect", done = "Serverµ µDone";
// Iterator<String> remove = onlineUsers.iterator();
// while (remove.hasNext()) {
// String token = remove.next();
// if (token.equals(user))
// onlineUsers.remove(token);
// }
onlineUsers.remove(user);
tellEveryone(user + add);
}
public void tellEveryone(String message) {
// sends message to everyone connected to server
Iterator it = clientOutputStreams.iterator();
while (it.hasNext()) {
try {
PrintWriter writer = (PrintWriter) it.next();
writer.println(message);
outputPane.append("Sending: " + message + "\n");
writer.flush();
outputPane.setCaretPosition(outputPane.getDocument().getLength());
} // end try
catch (Exception ex) {
outputPane.append("Error telling everyone. \n");
} // end catch
} // end while
} // end tellEveryone()
// Variables declaration - do not modify
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea outputPane;
private javax.swing.JButton startButton;
private javax.swing.JButton stopButton;
// End of variables declaration
}
Upvotes: -1
Views: 5064
Reputation: 638
I have come to a conclusion, whether or not someone is having the same problem as me, some of the professional's who replied to my question might want to know for the sake of adding it to there knowledge.
First, I double checked that every message being sent contained 2 µ's, so there is no way that data[] could have a size less than 3.
However, I work with the socket in an AsyncTask (you have to or you get NetworkTaskOnMainThread exception), after I init the socket and the readers/writers, I call publishProgess while the socket is connected. I realized that somehow there could be a bit of discrepancy through using a whileLoop, and then calling progress based on something that could be changing back and forth, ie. 2 users sending the a message at the same time. So I changed the params of my AsyncTask from <Void, Void, Void>
to <Void, String, Void>
and then when I do publishProgress in doInBackground, I put stream as the parameter and work with that instead.
That seemed to have solved the problem. If someone wants to touch on it please do!
Upvotes: 0
Reputation: 24885
your stream
has not 2 µ
characters, so the split
returns an array with less than 3 items. When the following code tries to use data[2]
, such element is outside the range of the array and the exception is thrown.
Make your code more defensive, do not always assume that the data received will be ok. V.g., after the split, add this code
if (data.length < 3) {
// Process error (log, user message, whatever)
return;
}
Upvotes: 1
Reputation: 178263
Let's start with the error message:
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
We know somewhere you have an array of length 2, but you've gone off the end of it, since in Java array indices range from 0
to length - 1
.
Likely explanation:
Your data
array is initialized with this call:
data = stream.split("µ");
However, if there aren't at least 2 µ
characters in stream
, then the array will be shorter than 3, and this line will throw the exception:
if (data[2].equals(connect)) {
Check data.length
before entering this piece of code, to ensure it's at least 3, and if it's not, handle the error appropriately.
Upvotes: 1
Reputation: 16536
The exception is clear.-
java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
is telling you that you're trying to access to index 2
position in an array that has only a length of 2
(positions 0
and 1
).
I'm guessing that line MyActivity.java:175
is this one.-
if (data[2].equals(chat)) {
so you need to review this.-
data = stream.split("µ");
and check that stream String
has the expected value (it should have at least two µ
characters to result in an array of 3+ items).
Upvotes: 5