I've been create an app for Bluetooth control.
Everything is fine until I open BluetoothServerSocket to listen to an incoming connection.
here is my code:
public class ListeningThread extends Thread {
private final BluetoothServerSocket bluetoothServerSocket;
public ListeningThread() {
BluetoothServerSocket temp = null;
try {
temp = myBluetoothAdapter.listenUsingRfcommWithServiceRecord(getString(R.string.app_name), uuid);
Toast.makeText(getApplicationContext(), "Listening",
Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
bluetoothServerSocket = temp;
}
public void run() {
BluetoothSocket bluetoothSocket;
// This will block while listening until a BluetoothSocket is returned
// or an exception occurs
while (true) {
try {
bluetoothSocket = bluetoothServerSocket.accept();
Toast.makeText(getApplicationContext(), "Alert", <-------//the code is not run through here, there is no toast coming out
Toast.LENGTH_SHORT).show();
} catch (IOException e) {
break;
}
// If a connection is accepted
if (bluetoothSocket != null) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "A connection has been accepted.",
Toast.LENGTH_SHORT).show();
}
});
// Manage the connection in a separate thread
try {
bluetoothServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
// Cancel the listening socket and terminate the thread
public void cancel() {
try {
bluetoothServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
there is no error shown but when i triggered the ListeningThread() function, this line of code is running Toast.makeText(getApplicationContext(), "Listening" it is shown that my server is listening to incoming connection
but when I'm using other client to connect to this server, then client are showing "paired" but on the server there is nothing shown, but the connection was actually successfull.
from the line
//This will block while listening until a BluetoothSocket is returned//or an exception occurs and //If a connection is acceptedis not running, because the toast did not coming out.
this function is called from MainActivity Threads and was putted on a button. I don't think pasting all the code here will be good, but here is the button codes. if there is anything you need to see, simply ask me.
serverBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ListeningThread t = new ListeningThread();
t.start();
}
});
Related
I have an app that runs 3 threads at the same time. One thread is for stablishing a bluetooth connection between the phone and another bluetooth device (Arduino). Thread 2 plays audio incoming from another phone via bluetooh. Thread 3 records and sends audio to the other phone via bluetooth.
The audio communication works with lots of glitches if phone is trying to stablish a connection with the Arduino (when thread 1 is running bluetoothsocket.connect();). However, when phone does not try to stablish a connection with the Arduino or the connection is already stablished and thread 1 is done, then the communication is good.
Here is the code for thread 1 - arduino (this code is with a class)
public class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// As mmSocket is final, we use a temporary socket variable
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
// Send the name of the disconnected device back to the UI Activity
sendDeviceConnectionToActivity(deviceMAC, false);
Log.d("Bluetoot connected -->", "NNNNNNNN" + connectException);
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket, mmDevice);
// mConnectedThread = new ConnectedThread(mmSocket);
// mConnectedThread.start();
Log.d("Bluetoot connected -->", mmDevice.getName());
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
the code for audio in thread 2 and 3 (this code is with a another class)
public void audioCreate() {
// Audio track object
track = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
16000, AudioFormat.CHANNEL_OUT_MONO,
encoding, minSize, AudioTrack.MODE_STREAM);
// Audio record object
recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000,
AudioFormat.CHANNEL_IN_MONO, encoding,
bufferSize);
}
public void initiateBluetoothConexion(BluetoothDevice deviceSelected) {
// Toast.makeText(getApplicationContext(), "Service On", Toast.LENGTH_SHORT).show();
deviceMAC = deviceSelected.getAddress();
mBluetoothAdapter.cancelDiscovery();
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
mConnectThread = new ConnectThread(deviceSelected);
mConnectThread.setPriority(10);
mConnectThread.start();
}
public class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// As mmSocket is final, we use a temporary socket variable
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
// Send the name of the disconnected device back to the UI Activity
sendDeviceConnectionToActivity(deviceMAC, false);
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket, mmDevice);
// mConnectedThread = new ConnectedThread(mmSocket);
// mConnectedThread.start();
Log.d("Bluetoot connected -->", mmDevice.getName());
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private void manageConnectedSocket(BluetoothSocket mmSocket, BluetoothDevice mmDevice) {
// Cancel the thread that completed the connection
// if (mConnectThread != null) {
// mConnectThread.cancel();
// mConnectThread = null;
// }
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(mmSocket);
mConnectedThread.setPriority(10);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Log.d(TAG, "Connected to " + mmDevice.getName());
sendDeviceConnectionToActivity(mmDevice.getAddress(), true);
// setState(STATE_CONNECTED);
}
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private byte buffer[] = null;
private byte playBuffer[] = null;
private boolean intercomm = false;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams; using temp objects because
// member streams are final.
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating input stream", e);
}
try {
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "Error occurred when creating output stream", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
intercomm = true;
}
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
playBuffer = new byte[minSize];
// Playback received audio
track.play();
startRecording();
// receive recording until an exception occurs.
while (intercomm) {
try {
if (mmInStream.available() == 0) {
//Do nothing
} else {
mmInStream.read(playBuffer);
track.write(playBuffer, 0, playBuffer.length);
}
} catch (IOException e) {
Log.d("AUDIO", "Error when receiving recording");
sendDeviceConnectionToActivity(deviceMAC, false);
break;
}
}
}
// Record Audio
public void startRecording() {
Log.d("AUDIO", "Assigning recorder");
buffer = new byte[bufferSize];
// Start Recording
recorder.startRecording();
Log.d("startRecording", "passed");
// Start a thread
recordingThread = new Thread(new Runnable() {
#Override
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
Log.d("startRecording", "sendRecording");
sendRecording();
}
}, "AudioRecorder Thread");
recordingThread.setPriority(10);
recordingThread.start();
}
// Method for sending Audio
public void sendRecording() {
// Infinite loop until microphone button is released
while (intercomm) {
try {
recorder.read(buffer, 0, bufferSize);
mmOutStream.write(buffer);
} catch (IOException e) {
Log.d("AUDIO", "Error when sending recording");
sendErrorsToActivity("Error sending audio");
}
}
}
// Call this method from the main activity to shut down the connection.
public void cancel() {
intercomm = false;
stopPlaying();
stopRecording();
destroyProcesses();
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close the connect socket", e);
}
}
// Stop playing and free up resources
public void stopPlaying() {
if (track != null) {
track.stop();
track.flush();
}
}
// Stop Recording and free up resources
public void stopRecording() {
if (recorder != null) {
recorder.stop();
}
}
public void destroyProcesses() {
//Release resources for audio objects
track.release();
recorder.release();
}
}
I tested the code in an octacore android oreo. However, when I did it in an phone sdk 23, it was worst.
Your AudioTrack is starving because it is not receiving data quickly enough from the arduino. This is most likely due to increased network contention during the BT connection process.
You appear to be configuring your AudioTrack with the smallest possible play-buffer. On most devices, this is only a few ms of audio, so if the AudioTrack isn't fed more data every few ms, it will starve, and you will hear a glitch.
One solution is to increase the AudioTrack's buffer size (perhaps to around 8000 samples or more).
In addition, you have not checked the return value from mmInStream.read(), which means you may be trying to "play" a playBuffer that is only partially filled.
Altering the thread priorities, as you are, is unlikely to make a qualitative difference.
my ListeningThread is freezing the UI thread of the Barometer activity although it shouldn't. I have no idea why this is happening. Help needed :)
This is my ListeningThread:
public class ListeningThread extends Thread {
String TAG = "bluetoothThread";
private final BluetoothServerSocket bluetoothServerSocket;
BluetoothAdapter bl;
public ListeningThread(BluetoothAdapter bluetoothAdapter, String appName) {
BluetoothServerSocket temp = null;
bl = bluetoothAdapter;
try {
temp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(appName, Constants.MY_UUID_SECURE);
} catch (IOException e) {
e.printStackTrace();
}
bluetoothServerSocket = temp;
}
public void start() {
Log.d(TAG, "ListeningThread running");
BluetoothSocket bluetoothSocket;
//This will block while listening until a BluetoothSocket is returned or an exception occurs
while (true) {
try {
bluetoothSocket = bluetoothServerSocket.accept();
Log.d(TAG, "ListeningThread connected");
} catch (IOException e) {
break;
}
// If a connection is accepted
if (bluetoothSocket != null) {
// Manage the connection in a separate thread
try {
bluetoothServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
// Cancel the listening socket and terminate the thread
public void cancel() {
try {
bluetoothServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And this is the activity using the thread and freezing:
public class BarometerActivity extends AppCompatActivity {
BluetoothAdapter bluetoothAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barometer);
if (SingletonBluetoothAdapter.bluetoothAdapter == null) {
SingletonBluetoothAdapter.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
bluetoothAdapter = SingletonBluetoothAdapter.bluetoothAdapter;
ListeningThread t = new ListeningThread(bluetoothAdapter, getString(R.string.app_name));
t.start();
}
}
I am sure the problem is somewhere in the run-method of the thread. I tried to surround this method with a comment and then everything runs fine.
You should put the while(true) code inside the method public void run(). Not in the public void start() as documented here https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html
I am writing an IRC Client. The socket connection to the IRC Server is handled via a service. I have managed to stabilize all the UI elements of the Activities in question during the orientation change, but somehow the socket that is maintained by the service is being closed during the change.
Here is what I believe to be the relevant code. Please let me know if you need to see more.
//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;
}
Im not entirely certain what I am doing wrong there. Obviously there's something I'm missing, haven't found yet, or haven't even thought to check. What happens though is that after the orientation change my Send command gives me this message and nothing happens:
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)
I have no idea when the socket is getting closed, or why.
Update
I have changed the code so that rather than binding to the service and using that to start it, instead I call startService and stopService at appropriate points as well as binding to it, on the thought that the service was being destroyed when the binding was lost. This is working exactly like it was before I changed it. The socket still closes on an orientation change, and I have no idea why.
Update :- Code and description
I added the code changes recently made for Start/Stop service and START_STICKY. I also recently read a very good article explaining how the orientation change process flow works and why its NOT a bad idea to add the android:configChanges="orientation|screenSize" line to your manifest. So this fixed the orientation issue, but its still doing the same thing if I put the activity into background mode, and then bring it back to the foreground. That still follows the same Save/Destroy/Create process that the orientation does without that manifest line...and it still closes my socket, and I still don't know why.
I do know that it doesn't close the socket until the re-create process...I know this because the message queue will display messages that were received while the app was in the background, but once I bring it back forward it closes the socket and nothing else can be sent or received.
'Socket closed' means that you closed the socket and then continued to use it. It isn't a 'disconnect'.
You need to put something into that catch block. Never just ignore an exception. You might get a surprise when you see what the exception actually was.
NB Socket.isConnected() doesn't tell you anything about the state of the connection: only whether you have ever connected the Socket. You have, so it returns true.
I'm using Jetty WebSockets for the server side and Autobahn Android for the client one.
A simple connection between server and client works fine. But I'm having some trouble when I try to handle the loss of connection.
On Android, what I have is this:
private void connectToServer() {
try {
mConnection.connect(getString(R.string.server_addr), new WebSocketHandler(){
// connection au serveur
#Override
public void onOpen(){
Log.d(TAG, "Connected");
Log.d(TAG, "First connexion, sending MAC #");
Log.d(TAG, "My MAC Addr: "+ macAddr);
mConnection.sendTextMessage(macAddr);
}
// reception d'un message text
#Override
public void onTextMessage(String payload) {
//TODO
}
// fermeture de la connexion
#Override
public void onClose(int code, String reason) {
Log.d(TAG, "Connection lost. "+reason);
if(mConnection.isConnected()){
Log.d(TAG, "Still connected, disconnect!");
mConnection.disconnect();
}
if(code<4000){
int totalWaitTime = 0;
int waitTime = 0;
Log.d(TAG, "Should be disconnected");
while(!mConnection.isConnected()){
try {
waitTime= random.nextInt(MAX_TO_WAIT - MIN_TO_WAIT + 1) + MIN_TO_WAIT;
Log.d(TAG, "I'll wait "+waitTime+"ms");
totalWaitTime +=waitTime;
Log.d(TAG, "Waiting for "+totalWaitTime+"ms");
if(totalWaitTime <= HOUR_TO_MS){
Thread.sleep(waitTime);
Log.d(TAG, "Trying to reconnect");
connectToServer();
}else{
throw new InterruptedException("Attempt to connect to the server during 1 hours without success");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
} catch (WebSocketException e) {
Log.e(TAG, "Error on connect: "+e.toString());
Log.d(TAG, "is connected: "+mConnection.isConnected());
if(mConnection.isConnected())
mConnection.disconnect();
connectToServer();
}
}
And I always, always have the same error:
06-26 10:36:07.823: E/wingo.stb.qos.AutoStartService(1842): Error on connect: de.tavendo.autobahn.WebSocketException: already connected
But like you can see, I close the connection in onClose(), and when a WebSocketException is catched. Does this method really works ? Or am I doing it wrong ?
By the way, mConnection is final. So maybe the problem comes here ?
private final WebSocketConnection mConnection = new WebSocketConnection();
On the server, when I have a connection loss I manually close the session:
#OnWebSocketClose
public void onClose(Session session, int closeCode, String closeReason){
try {
System.out.println("connexion closed. Reason: "+closeReason);
pingPongTimer.cancel();
if(session.isOpen())
session.close();
WebSocketsCentralisation.getInstance().leave(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Thanks in advance awesome people!
So, what I dit was a bit messy. What I have now works:
private void connectToServer() {
try {
mConnection.connect(getString(R.string.server_addr), new WebSocketHandler(){
// connection au serveur
#Override
public void onOpen(){
Log.d(TAG, "Connected");
Log.d(TAG, "First connexion, sending MAC #");
Log.d(TAG, "My MAC Addr: "+ macAddr);
mConnection.sendTextMessage(macAddr);
}
// reception d'un message text
#Override
public void onTextMessage(String payload) {
//TODO
}
// fermeture de la connexion
#Override
public void onClose(int code, String reason) {
Log.d(TAG, "Connection lost. "+reason+" error code : "+code);
if(code<4000){
reconnectToServer();
}
}
});
} catch (WebSocketException e) {
Log.e(TAG, "Error on connect: "+e.toString());
Log.d(TAG, "is connected: "+mConnection.isConnected());
}
}
private void reconnectToServer() {
try {
if(goConnect){
goConnect = false;
Thread.sleep(1000);
Log.d(TAG, "DISCONNECT:");
// mConnection.disconnect();
Log.d(TAG, "ReconnectTimer Launched");
new ReconnectTask().run();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class ReconnectTask extends TimerTask{
#Override
public void run() {
try{
if(totalWaitTime<HOUR_TO_MS){
if(!mConnection.isConnected()){
int waitTime= random.nextInt(MAX_TO_WAIT - MIN_TO_WAIT + 1) + MIN_TO_WAIT;
Log.d(TAG, "Next tentative to connect in "+waitTime+" ms");
totalWaitTime +=waitTime;
reconnectTimer.schedule(new ReconnectTask(), waitTime);
connectToServer();
}else{
Log.d(TAG, "Connected to the server again");
reinitializeReconnection();
}
}else throw new InterruptedException("Attempt to connect to the server during 1 hours without success");
}catch(InterruptedException e){
Log.d(TAG, e.getMessage());
}
}
}
private void reinitializeReconnection(){
reconnectTimer.purge();
goConnect = true;
totalWaitTime = 0;
}
Basically, I tried to get everything out of the WebSocketHandler. And what I didn't understand is that if you try to connect to the server in "onClose" and the server is down, it will go on "onClose" again. So I was doing recursive calls and it was really messy.
I have implemented my own android service as follows
public class MyService extends Service {
private static final String TAG = "MyService";
private Server mServer;
private LocalServerSocket server;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.d(TAG, "onCreate");
mServer = new Server();
mServer.start();
}
#Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
if(server != null){
try {
server.close();
} catch (IOException e) {
Log.d(TAG, "exception in server close");
e.printStackTrace();
}
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStart");
return START_STICKY;
}
class Server extends Thread {
#Override
public void run() {
try {
server = new LocalServerSocket("my.socket");
while (true) {
LocalSocket receiver;
try{
receiver = server.accept();
}catch(SocketException e){
Log.d(TAG, "SocketException");
break;
}
catch(IOException e){
Log.d(TAG, "IOException");
break;
}
if (receiver != null) {
Log.d(TAG, "Got Data in receiver");
}
receiver.close();
}
} catch (IOException e) {
Log.d(TAG, "one more");
e.printStackTrace();
}
}
}
}
The problem I am facing is that, if my LocalServerSocket is blocking in accept(), then a call to server.close() in OnDestroy() will not throw a SocketException. Hence, next time I start the service, I get "address already in use exception". If instead of LocalServerSocket, I use java.net.Socket, then i get the required behavior. I would like to know why LocalServerSocket behaves differently from Java Sockets. In my case, how do I come out of the while loop.
I had the same problem and "solved" it this way. The thread run() method is checking for "!isInterrupted()". The method "stopSocketServer()" which I added to my Listener-Thread marks the thread for interrupt() and then made a connect request to itself to trigger the accept() method.
/**
* Executed if thread is started.
*/
public void run() {
try {
// leave while loop if thread is marked for interrupt.
while (!isInterrupted()) {
LocalSocket clientSocket = serverSocket.accept();
if (!isInterrupted()) {
threadPool.execute(new ClientProcessor(clientSocket));
}
}
} catch (IOException e) {
if (!isInterrupted()) {
Log.e(TAG, "socket listener terminated", e);
}
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
if (threadPool != null) {
threadPool.shutdownNow();
}
Log.i(TAG, "socket listener stopped");
} catch (IOException e) {
}
}
}
public void stopSocketServer() {
if (serverSocket != null) {
try {
// mark thread as interrupted
interrupt();
// now send connect request to myself to trigger leaving accept()
LocalSocket ls = new LocalSocket();
ls.connect(serverSocket.getLocalSocketAddress());
ls.close();
} catch (IOException e) {
Log.e(TAG, "stopSocketServer failed", e);
}
}
}
From the code you've posted it looks like your LocalServerSocket server will remain null forever and onDestroy() won't close it. Also, closing the socket normally shouldn't throw IOException - but the accept() will do so if the socket is closed concurrently.