i built up an application based on aSmack, and obviously every thing about connection is running in a service, so it is very important to keep its connection Alive, as we know services running in background, may get killed upon phone's low sources (usually Ram) , so with the START_STICKY flag on the service, it restarts itself with a null intent.
now i wonder if there be no network on the phone ,or suddenly an unexpected temporary exception happenes in that time, (and because service is restarted reconnectionManager hasnt been set up yet), so the app must get restarted to retrieve its connection. my question is how can i handle these exceptions ? i know i can do some thing like this:
public void connect(){
try {
connection.connect();
} catch (SmackException | IOException | XMPPException e) {
if(getIntent() == null){
connect();
return;
}
}
}
but this is unprofessional imo, i know there was a way to determine temporary exceptions but unfortunanly i cant either remember or find them. any information is appreciated. thanks alot
so here is what i have done, works perfectly and every thing is counted in that.
this is awhat my service does every time it starts
private void connect(){
if (!connection.isConnected()){
try {
connection.connect();
} catch (SmackException | IOException | XMPPException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
if(intent ==null){
Toast.makeText(getBaseContext(),"Could not Connect to The Server , Network Problems , Retrying in 30 Seconds...", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getBaseContext(),"Could not Connect to The Server , Network Problems...", Toast.LENGTH_LONG).show();
}
}
});
if(intent == null){
//When intent is null, It Means that service got Destroyed middle of app, which
//means user has already connected and Authenticated once, but can not do it again.
//so thats the key
nonMainHandler.postDelayed(new Runnable() {
#Override
public void run() {
connect();
}
},30000);
}
e.printStackTrace();
}
}
try {
if (connection.isConnected() && !connection.isAuthenticated()) {
try {
connection.login(LMApplication.userName,LMApplication.passWord);
} catch (SmackException | IOException | XMPPException e) {
mainHandler.post(new Runnable() {
#Override
public void run() {
if(intent != null){
Toast.makeText(getBaseContext(),"Could not Login Using this Information..", Toast.LENGTH_LONG).show();
}
}
});
e.printStackTrace();
configEnterButton(-1);
}
}
if(connection.isAuthenticated()){
configEnterButton(100);
try {
Thread.sleep(300);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
goAhead();
Log.i("XMPPChatDemoActivity", "Logged in as" + LMApplication.Raw_CurrentUser);
//TODO
// check if need to set presence from shared preferences
Presence p = new Presence(Presence.Type.available,"", 42, Mode.available);
try {
connection.sendPacket(p);
} catch (NotConnectedException e1) {
e1.printStackTrace();
}
}else if(intent == null){
nonMainHandler.post(new Runnable() {
#Override
public void run() {
if(connection.isConnected() && !connection.isAuthenticated()){
try {
connection.login(LMApplication.userName,LMApplication.passWord);
} catch (XMPPException | SmackException
| IOException e) {
e.printStackTrace();
}
}
if(connection.isConnected() && connection.isAuthenticated()){
notifyReconnect();
}else if(connection.isConnected()){
nonMainHandler.postDelayed(this,10000);
}
}});
}
my question is how can i handle these exceptions ?
By performing a reconnect. Most applications I know do schedule the reconnect after a few seconds.
Related
I have two devices connected through Bluetooth now. After that, I disconnected the Bluetooth connection on the Client device, and the broadcast receiver in this Client device can detect the disconnection, and then switch it back to previous activity. Something like this:
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Message msg = Message.obtain();
String action = intent.getAction();
if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
try {
Log.i("Disconnecting3", "Disconectinggg....");
Intent intent1 = new Intent(Main3Activity.this, MainActivity.class);
startActivity(intent1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
Anyhow, on my other device which is the Server device, this device CAN NOT detect the disconnection despite the Bluetooth socket is closed! The broadcast receiver in the Server device cannot detect the disconnection. FYI, below code will show how I close the Bluetooth socket on the Server device when the Client device is disconnected.
private boolean CONTINUE_READ_WRITE;
CONTINUE_READ_WRITE = true;
public void run() {
try {
while (CONTINUE_READ_WRITE) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
// Send the obtained bytes to the UI activity.
Message readMsg = handleSeacrh.obtainMessage(MessageConstants.MESSAGE_READ, numBytes, -1, mmBuffer);
readMsg.sendToTarget();
} catch (IOException e) {
//nothing();
CloseConnection closeConnection = new CloseConnection();
closeConnection.start();
break;
}
}
} catch (Exception e) {
// Log.d(TAG, "Input stream was disconnected", e);
}
}
public void cancel() {
try {
Log.i("TAG", "Trying to close the socket");
CONTINUE_READ_WRITE = false;
mBluetoothSocket.close();
mmBluetoothSocket.close();
Log.i("TAG", "I thinked its still closing");
} catch (IOException e) {
Log.e("TAG", "Could not close the connect socket", e);
}
}
So when there is a disconnection happened on the Client device, the while(CONTINUE_READ_WRITE)..loop will break the loop and start a new Thread. Something like this :
private class CloseConnection extends Thread {
public void run(){
Log.i("Running","Runinnggggg");
try {
mmInStream.close();
mmOutStream.close();
bluetoothDataTransmission.cancel();
Log.i("Interrupted","InteruppteDDDD");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Alright, I found a solution , just need to add this line of code
intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
In my App, i have a Service including a Thread that ciclycally starts and stops BLE Advertising:
public class BeaconSearchService extends Service {
private void startAdvertisingThread() {
(advertisingThread = new Thread("Advertising Thread") {
#Override
public void run() {
//Ensure that advertising is off
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
}
while(!isInterrputed()) {
AdvertiseSettings advertiseSettings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.setConnectable(true)
.build();
BeaconSearchService.this.advertiseData.getServiceUuids().clear();
BeaconSearchService.this.advertiseData.getServiceUuids().add(BeaconSearchService.this.getUuid());
long uptime = 5000;
long downTime = 7000;
//START ADVERTISING
try {
BeaconSearchService.this.bluetoothLeAdvertiser.startAdvertising(advertiseSettings, BeaconSearchService.this.advertiseData, BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
//SLEEP FOR 5 SEC
try {
sleep(upTime);
} catch (InterruptedException e) {
interrupt();
break;
}
//AFTER WAKE FROM SLEEP, STOP ADVERTISING
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
//SLEEP FOR 7 SEC
try {
sleep(downTime);
} catch (InterruptedException e) {
interrupt();
break;
}
//RESTART THE CIRCLE IF NOT INTERRUPTED
}
//Ensure stop advertising while exiting
try {
BeaconSearchService.this.bluetoothLeAdvertiser.stopAdvertising(BeaconSearchService.this.advertiseCallback);
} catch (Exception e) {
}
}).start();
}
#Override
public void onDestroy() {
try {
advertisingThread.interrupt();
} catch(Exception e) {
}
}
}
The problem is that when the Advertising is ON and Service and Thread are closed WITHOUT calling the onDestroy() method (e.g. App Crash, App process killed by the Local Android ROM Custom Behavior or while developing with Android Studio, App restart after new version download from Playstore etc), the Android system keeps the BLE Advertising ON. So when I reopen the App and sniff the BLE Packets I see TWO or More Advertising "sessions" of my App. The problem is solved when I reboot the phone and "orphan" Advertisig sessions disappear.
Is there any way to kill all the "zombie" Advertising sessions without forcing the user to reboot his phone?
When another thread calls closeConnection(), the thread doesn't reach
Log.d("Subscriber", "Client thread has ended.");
Why is this? What is the blocking behaviour of a stream that has been closed? I thought trying to write or flush to it would generate an IOException, but it seems the code is still blocking somewhere. Where? I can't find info on what happens when you interrupt() on a write, or what happens when writing to a closed outputstream.
public void closeConnection() {
try {
this.interrupt();
autoCloseOutputStream.close();
} catch (IOException e) {
Log.w("Subscriber", "IOException when closing stream. Buffer might not have been flushed to client.");
}
}
#Override
public void run() {
Log.d("Subscriber","Client thread has started.");
ByteBuffer pgnAndDataBytes=null;
while(true) {
try {
pgnAndDataBytes=fmsByteBufferSubscriberQueue.take();
} catch (InterruptedException e) {
break;
}
Log.d("Subscriber","Still running thread");
try {
autoCloseOutputStream.write(pgnAndDataBytes.array());
autoCloseOutputStream.flush();
} catch (IOException e) {
break;
}
}
Log.d("Subscriber", "Client thread has ended.");
}
The output is as follows:
Still running thread
Still running thread
Still running thread
Close called.
And nothing more. Where is it blocking and why?
Have a volatile boolean shouldClose that you set to true on closeConnect(). Incorporate the boolean into the condition check of the while loop.
boolean done = false;
while(!shouldClose && !done) {
try{
autoCloseOutputStream.write(pgnAndDataBytes.getInt());
} catch(BufferUnderflowException bue) {
final ArrayList<Byte> remainder = new ArrayList<Byte>(3);
while(!shouldClose && !done) {
try {
remainder.add(pgnAndDataBytes.get());
} catch(BufferUnderflowException ex) {
autoCloseOutputStream.write(remainder.toArray(new Byte[remainder.size()]);
done = true;
}
}
}
}
I'm trying to periodically check the network connection. However, this is for a Chinese Android Mini PC, not a tablet or smartphone. I'm using an ethernet to usb adapter instead of Wi-Fi. First I used a broadcastreceiver class:
public class NetworkStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null) {
#SuppressWarnings("deprecation")
NetworkInfo eni = (NetworkInfo) intent.getExtras().get(
ConnectivityManager.EXTRA_NETWORK_INFO);
if (eni != null && eni.getState() == NetworkInfo.State.CONNECTED) {
Log.d(TAG, "Network " + eni.getTypeName() + " connected.");
}
}
if (intent.getExtras().getBoolean(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
Log.d(TAG, "There's no network connectivity.");
}
}
}
This works perfectly for Wi-Fi and mobile. However, for ethernet, there are complications. When I connect the ethernet to usb adapter, it thinks it already has ETHERNET connection, whether the ethernet cable is connected or not. Only when removing the adapter, it knows the ethernet connection was removed.
I tried using a socket, and this kind of works:
private static boolean checkSocket(String host, int port) {
Socket socket = null;
boolean reachable = false;
try {
socket = new Socket(InetAddress.getByName(host), port);
reachable = true;
} catch (UnknownHostException e) {
} catch (IOException e) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
return reachable;
}
When there is a connection, it works perfectly and fast. When the connection is lost, it takes way too long for the program to know it has. I need this solution, but it should know way faster that the ethernet connection has been lost. Also, this relies on Exceptions, which I'm not fond of at all.
Lastly I tried a simple ICMP message:
try {
InetAddress address = InetAddress.getByName(host);
if (address.isReachable(timeout)) {
return true;
}
} catch (UnknownHostException e) {
} catch (IOException e) {
}
return false;
This should work, right? Unfortunately, it doesn't. Until now, I've always received a false when executing this code.
What am I doing wrong and what is the correct way to do this?
EDIT 1
I have now tried this solution, which works and doesn't work. It's funny and annoying, as I'm checking this in the onResume(). After a few correct tries, it suddenly stops. I have no idea why though.
boolean reachable = false;
try {
Process p1 = java.lang.Runtime.getRuntime().exec("ping -c 1 " + host);
int retValue = p1.waitFor();
reachable = (retValue == 0);
Log.d(TAG, String.valueOf(reachable));
p1.destroy();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return reachable;
Try connecting to ip addres, not resolving dns as you do
ADD:
Try using ConnectivityManager.getActiveNetworkInfo then
((ConnectivityManager) Context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo().isConnected()
I'm developing an application for Android 4.03. The code of relevance is this:
public void startConnection() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Log.i(LOG_TAG, "Beginning");
_socket = new Socket(_server, _port);
_socket.setSoTimeout(DEFAULT_SOCKET_TIMEOUT);
_writer = new BufferedWriter(new OutputStreamWriter(_socket.getOutputStream()));
_reader = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
_in = new InputThread(_reader, new InputThreadObserver());
_in.start();
Log.i(LOG_TAG, "End");
} catch (UnknownHostException e) {
Log.i(LOG_TAG, "UnknownHostException");
} catch (IOException e) {
Log.i(LOG_TAG, "IOException");
}
}
}).start();
}
The creation of the socket is performed in a new thread, otherwise the execution freezes for few seconds.
If I set the variable _server to an existing host (for example www.google.com) everything goes all right. But if I set the _server variable to an host that does not exist (for example asd.asd) I really expect "UnknownHostException" to be printed in the logger. This does not happen (but the _socket variable is null). It just prints "Beginning" (and not "End"). Any Idea?
EDIT:
The variables are declared like this:
private String _server;
private Socket _socket;
private int _port;
private BufferedWriter _writer;
private BufferedReader _reader;
private InputThread _in;
EDIT:
I'm trying this:
public void startConnection() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Log.i(LOG_TAG, "Beginning");
_socket = new Socket(_server, _port);
if (_socket == null)
Log.i(LOG_TAG, "NULL SOCKET! (test 1)");
} catch (Exception e) {
Log.i(LOG_TAG, "EXCEPTION!");
}
if (_socket == null)
Log.i(LOG_TAG, "NULL SOCKET! (test 2)");
}
}).start();
}
Don't know why but the output is only:
Beginning
EDIT:
After 3 minutes and 13 seconds waiting i finally got:
EXCEPTION!
NULL SOCKET! (test 2)
Is that normal? Shouldn't the UnknownHostException be thrown immediatly?
Looking at the API docs, if _server is a String, then you'll get an UnknownHost exception. If it's any of the other possibilities, you won't.
In particular this signature will create the exception:
Socket(String host,
int port)
throws UnknownHostException,
IOException