IPStringI have a fairly simple Activity which works as follows:
in the onCreate, I call an AsyncTask TCP Socket connection to a remote server over a proprietary port, send a quick ASCII command to the server and process the response via the onPostExecute(). Works great, it's fast and functional.
However, if the remote server is down -- or I mistakenly entered in the wrong IP address for the communication -- my AsyncTask will hang with the "logging in" dialog spinning on the screen for as long as it takes for the Socket to timeout.
I've been digging away at this for the last 2 days trying to figure out how to call the cancel(), but am failing. Any suggestions?
Here's the current code:
public class Scratchpad extends AsyncTask<Object, String, String>{
private String returningResponse;
private volatile Socket socket;
#Override
protected void onPreExecute(){
//throw the "Logging In" dialog up
initializeDialog();
super.onPreExecute();
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
//send the ASCII result off to the function that parses through it
loginTriggerResult(result);
super.onPostExecute(result);
}
#Override
protected String doInBackground(Object... params) {
/*
* bunch of stuff goes here to strip out the proper
* values from the Object for IP address, Port number etc.
* params[0], params[1] etc.
*
*/
InetAddress serverIP = null;
String IPString = (String) params[1];
int portnumber = (Integer) params[2];
//convert the String "IPString" into an InetAddress
try {
serverIP = InetAddress.getByName(IPString);
} catch (UnknownHostException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//and then, bingo bango bongo, make the connection with the 'try':
try{
socket = new Socket(serverIP.getHostAddress(), portnumber);
//I tried this too, forcing the timeout... It crashes immediately.
//SocketAddress socketAddress = new InetSocketAddress(serverIP.getHostAddress(), portnumber);
//socket.connect(socketAddress, 3000);
Log.d("networking","Connection Completed");
//bunch of stuff goes here that happens AFTER the solid connection,
//which we never get to if the Socket fails on a "waiting to connect" thing.
//reader.close();
//outputStream.close();
}catch (SocketException e){
e.printStackTrace();
Log.w("socket","socket closed with SocketException");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.w("socket","socket closed with IOException");
}
return returningResponse; //postExecute will handle this.
}
}
I'm not getting any of the Log messages until the 3 or 4 minutes pass that the socket times out.
I'm thinking there must be a way to run a TimerTask or something OUTSIDE of the AsyncTask to track how long it's taking to make the connection (or not) and then somehow "cancel()" the running 'try' inside the AsyncTask, but I've been failing on numerous styles.
This HAS to be something that's commonly used, and there's gotta be an easy solution?
UPDATE:
The path I was heading down was trying to use the socket connection from within AsyncTask to "monitor itself", and if/when it timed out without getting a solid connection -- trigger the cancel() of the AsyncTask.
I was trying to do this from within the AsyncTask itself, with this.cancel() inside the IOException catch of the Socket timeout.
The solution so far is to implement a timer outside of the AsyncTask that launches at the same time as the AsyncTask, and call the cancel from there after however many seconds have transpired.
This solution does gracefully exit from the connection attempt, and I can put any sort of Dialog triggers or Toasts inside the onCancelled() stanza of the AsyncTask.
It doesn't crash anymore, BUT: the socket is still trying to connect in the background, even though the task is cancelled.
It definitely seems like the simplest solution (which you have tried) would be to set the timeout when calling connect. Can you post the stack trace generated when the connect with timeout fails?
What exactly is it are you trying to do? Detect when you've entered the wrong IP address or the server is down? Only show logging in for a certain amount of time? I get that you're asking how to cancel your async task, but that doesn't seem to be the root of the problem you're trying to solve.
I think MisterSquonnk is exactly right, you should just be able to set the timeout duration when call connect. You may have something else broken in your code if that timeout doesn't work. Alternatively if you just want something that lets you do your own timeout without having anything to do with socket, see below:
If you want to assume you're connection has failed in a shorter amount of time than the socket takes to time out, start a new CountDownTimer http://developer.android.com/reference/android/os/CountDownTimer.html at the same time and in the same place in your activity as you start your async task with whatever you want the timeout duration to be.
After the timer runs out (and onFinish in the timer is called), call cancel on the async task, or otherwise prevent onPostExecute from executing, and do whatever you want to do when a login attempt fails in your UI. Similarly if the login attempt succeeds before the timer runs out, cancel the timer or otherwise prevent onFinish from being called.
Related
Wondering if its possible to monitor constantly some wifi connection to a server (some ip and port) and display the status of the connection, that is if the server is accessible/responsive or not.
By displaying the status i mean updating some textview displaying either "server up" or "server down".
(something similar to the wifi icon next to the battery indication at the top right..)
the answer is to make a thread class, which will constantly call (with a while loop) an asynctask. The asynctask will (try to) connect to the mysql server. the connection will be successful or not and accordingly a textview or something will be updated in the UIthread (using runOnUiThread)
an example of the code for the thread:
class servlookthread extends Thread {
#Override
public void run() {
while (keeplookingserver){
lookforserver lserv = new lookforserver(); // this is the asynctask thread
try {lserv.execute().get();} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();} //! .get() it waits until the asynctask finishes
try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();} // some delay, 4 seconds
}
return;
}
}
the keeplookingserver is a boolean, "global" variable used to control the execution of the thread.
hope this helps someone! although i think its not elegant, its working for me
When I call BluetoothServerSocket.accept(...) with some timeout value, my Alcatel A30 running Android 7.0 just ignores the timeout and blocks forever. Is this a new bug in Android or some stupidity with this particular phone?
Here's a simple code-section to demonstrate the problem (just paste this into any activity and add <uses-permission android:name="android.permission.BLUETOOTH" /> to your manifest):
Thread test = new Thread() {
#Override
public void run() {
try {
System.out.println("Getting Bluetooth adapter...");
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
System.out.println("Registering service profile...");
BluetoothServerSocket server = bt.listenUsingRfcommWithServiceRecord
("Test", UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
System.out.println("Accepting connection with timeout...");
server.accept(1000); // Android 7.0 gets stuck here rather than timing out
System.out.println("Accepted!");
} catch (Exception e) {
System.err.println("Got an error:");
e.printStackTrace();
}
}
};
test.setDaemon(true);
test.start();
Expected output (from any of my older Android devices; exception shows up after 1s):
Getting Bluetooth adapter...
Registering service profile...
Accepting connection with timeout...
Got an error:
java.io.IOException: Connection timed out
at android.bluetooth.BluetoothSocket.acceptNative(Native Method)
at android.bluetooth.BluetoothSocket.accept(BluetoothSocket.java:364)
at android.bluetooth.BluetoothServerSocket.accept(BluetoothServerSocket.java:113)
at line containing server.accept(1000);
Output from my Android 7.0 Alcatel A30:
Getting Bluetooth adapter...
Registering service profile...
Accepting connection with timeout...
And then it sits there until I kill the app or until I actually connect to the service, in which case I get
`Accepted!`
even after many minutes of waiting before I connect.
Update:
It seems like the code-sample is maybe creating some confusion (Re: deleted answer). Usually when someone posts an exception on SO, they are looking for help how to fix it. This is not what I'm after. By setting the timeout and then not connecting, I am explicitly asking for the exception. The problem is that I am not getting the exception on my Android 7.0 device.
I have the same problem with a device that's running with Android 7.0.
Other devices (Android 8 and 9) have the expected behavior : a IOException is raised when the accept did not get an incoming connection within its configured timeout.
What I ended up doing (and it's not very pretty) is using a Timer to close the BluetoothServerSocket from another thread, this caused accept to raise the IOException as though it was a timeout.
BluetoothServerSocket mBssOutCom = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("blabla", uuid);
Timer securityTimer = new Timer();
try {
//! there seems to be a bug in android 7.0
//! that makes the connect timeout never fire. This timer will do the dirty trick
//! Should only have an effect if the bug is active
securityTimer.schedule(new TimerTask() {
#Override
public void run() {
// this code will be executed after 2 seconds
if (mBssOutCom != null) {
try {
mBssOutCom.close();
} catch (IOException e) {
e.printStackTrace();
}
mBssOutCom = null;
}
}
}, 1500);
socketCom = mBssOutCom.accept(1000);
} catch (IOException e) {
Log.d(TAG, "tryToConnect: accept timedOut");
}
securityTimer.cancel();
if (mBssOutCom != null) {
mBssOutCom.close();
mBssOutCom = null;
}
If anyone is aware of a better solution, I'd love to hear it!
It looks like the timeout isn't used when accepting connections. It is used as the timeout when sending or receiving data through the socket (after the connection has been made).
Calling BluetoothServerSocket.accept(...) with a timeout ends up calling the setOption(...) in LocalSocketImpl with SocketOptions.SO_TIMEOUT, which in turn uses setsockopt to set:
SO_RCVTIMEO: Sets the timeout value that specifies the maximum amount of time an input function waits until it completes. ... If a receive operation has blocked for this much time without receiving additional data, it shall return with a partial count or errno set to [EAGAIN] or [EWOULDBLOCK] if no data is received.
SO_SNDTIMEO: Sets the timeout value specifying the amount of time that an output function blocks because flow control prevents data from being sent. If a send operation has blocked for this time, it shall return with a partial count or with errno set to [EAGAIN] or [EWOULDBLOCK] if no data is sent.
I am developing an Android application and when it launches :
1) I make an HTTP Request to my server to send a small JSON file.
2) Open a webView to show a URL.
When the server is running properly there is absolutely no problem and everything goes smoothly.
HOWEVER , if for any reason the server is down , the HTTP request literally hangs and i need to wait till there is an HTTP timeOut which is around 30seconds till i actually see the webView with the URL loading.
I read that i shouldn't make any networking inside the UI thread and i should use another thread for that.
In BlackBerry , that i have already developed the application for , this is as simple as that :
new Thread(){
public void run(){
HttpConnection hc =
(HttpConnection)Connector.open("http://www.stackoverflow.com");
}
}.start();
I just start a new thread and inside i make the requests and all the necessary networking. That way , even when my server is not reachable the webView is loaded immediately without making the user wait and sense that the app is actually hanging.
How could i do exactly the same in Android , easiest way possible ?
Why not to use the same method as you use it for BlackBerry?
new Thread() {
public void run() {
new URL("http://www.stackoverflow.com").getContent();
}
}.start();
Use AsyncTask, it's the simplest way to do that. For more details:
http://developer.android.com/reference/android/os/AsyncTask.html
In icecream sandwich and above you are not allowed to use any web cal in UI thread. However you may use threads, but best way proposed by android is to use Async task. A very simple example is as follow.
"AsyncTask < Parameters Type, Progress Value Type, Return Type >"
class MyAsyncTask extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
// Runs on UI thread- Any code you wants
// to execute before web service call. Put it here.
// Eg show progress dialog
}
#Override
protected String doInBackground(String... params) {
// Runs in background thread
String result = //your web service request;
return result;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String resp) {
// runs in UI thread - You may do what you want with response
// Eg Cancel progress dialog - Use result
}
}
I modified the standard Bluetoothchat example to send 4 bytes of data at a time to a bluetooth device every half a second. It works fine if I start the App fresh. However, there is a problem if I reconnect as follows:
While Bluetooth is connected, I click the connect button again on the menu and select the same device. This disconnects the bluetooth (not sure whether this is the right procedure to disconnect). Then, I connect again by selecting the device, and it will be reconnected. After reconnection, a very strange problem appears: instead of sending the data every half a second, it will send the data every quarter a second. If I go through the process again and reconnect, the time interval will become even shorter. It gets to a point that the bluetooth device on the receiving end can't keep up with the data. At this point, the only way out is to kill the app and restart again. Then everything becomes normal, till next time I try to reconnect again.
I have tried different things but nothing appear to fix this. For example, I made sure the thread sending the data is killed when disconnected so no multiple threads are sending the data. I was wondering whether the baud rate changed when reconnected, but then why would the baud rate affect the Thread.sleep(500); statement (which is responsible for controlling the half a second data send). Any help would be greatly appreciated.
Here is the code, the SendClass is created under the MainActivity:
class SendClass implements Runnable {
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted()) {
if (mChatService==null || mChatService.getState()
!=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then under STATE_CONNECTED:
case BluetoothChatService.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to,mConnectedDeviceName));
/*
if(sendingThread!=null){
//sendingThread.stop();
sendingThread.interrupt();
if(D) Log.i(TAG, "after sendingThread");
sendingThread = null;
}*/
sendingThread = new Thread(new SendClass());
sendingThread.start();
break;
As you can see, I tried to kill the thread before creating a new one but that didn't make any difference. Any suggestions?
You are creating a thread that never actually stops, even after you create a new thread and assign to the same variable that particular thread wont stop running.
You need to make sure that the thread will stop after it disconnects.
Here is my suggestion
Change your SendClass to:
class SendClass implements Runnable {
private boolean stopped = false;
public void setStopped(boolean s){
this.stopped = s;
}
public void run() {
bytearr[0]=0;bytearr[1]=0;bytearr[2]=0;bytearr[3]=0;
while (!Thread.currentThread().isInterrupted() && !stopped) {
if (mChatService==null || mChatService.getState() !=BluetoothChatService.STATE_CONNECTED) {
continue;
} else {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mChatService.write(bytearr);
}
}//end of run
}//end of runnable
Then when you start your thread keep the reference to the Runnable so you can call the setStopped(true); like this
SendClass sc = new SendClass();
sendingThread = new Thread(sc);
sendingThread.start();
When you disconnect the bluetooth dont forget to call sc.setStopped(true); so your thread will finish by not going into the while.
I think this is a quite common problem, but still I didn't find a satisfactory answer so I'm going to ask myself.
This is a piece of code:
// this is insine OnClickView
TextView status = (TextView) findViewById(R.id.status);
status.setText("Trying to connect to the server...");
try {
// this opens a socket and send a login request to the server.
int result = CommunicationManager.login(String email, String password);
switch (result) {
case CommunicationManager.SUCCESS:
// login ok, go on with next screen
break;
case CommunicationManager.WRONG_EMAIL:
status.setTextColor(Color.RED);
status.setText("Wrong Email!");
break;
case CommunicationManager.WRONG_PASSWORD:
status.setTextColor(Color.RED);
status.setText("Wrong Password!");
break;
}
} catch (CommunicationException e) {
status.setTextColor(Color.RED);
status.setText("Unable to estabilish a connection!");
} catch (ProtocolException e) {
status.setTextColor(Color.RED);
status.setText("Protocol error!");
}
This is what I would like to achieve:
User click Send button;
status textview shows "Trying to connect to the server...";
UI "waits" for communications to be over;
status textview shows result accordingly.
But instead when user clicks Send button, UI freezes (oddly before status text appears) until communication is done (I tried to connect to an unknown host).
A quick fix is to set a socket timeout, but I don't like this kind of solution: UI still freezes and which timeout should be set?
My first thought were Thread obviously, but as you can see I need to return a value, thing that in threading environment doesn't make much sense since threads run independently and asynchronously.
So what I need is definitely that UI waits for the service to be executed but without freezing.
By the way it seems to me that waiting for a return value means that UI has to wait for the task to be over, I just would not let it freeze.
I came across AsyncTask but I see two major disadvantages:
it seems to me that is too tightly coupled with UI;
what if I want to execute service with Integer, String and Boolean parameters? Should I extend AsyncTask<Object, Void, Void>?
Both leads to inextensibility.
What can I do to achieve my goal?
Please note that another request to the service will be a request for something that could not be ready yet, so I should automatically repeat request every few time (let's say ten minutes). So probably I'll need something I can use with TimerTask, but I'll still need to return a value to UI every time I execute that service (so I can update the status text and let the user know what's going on).
This is typical use case while dealing through external communication i.e. HTTP calls.
Best way is to use AsyncTask. To give you answers for your concerns for AsyncTask.
it seems to me that is too tightly coupled with UI;
Here good code design will play a role. You can write you own call back mechanism to get rid of tight coupling. Example can be below.
Create your version for request and response you need for WS call. It can be very simple primitive type or complex type parameters.
class Result{
//Define more para.
}
class Request{
//Deinf more para.
}
Write below callback interface.
public interface MyCallBack {
public void onComplete(Result result);}
Create AsyncTask and get above Interface object in constructor, same object can return Result object.
class LongRunningTask extends AsyncTask<Request, Integer, Long>{
private MyCallBack callback;
public LongRunningTask(MyCallBack callback) {
super();
this.callback = callback;
}
#Override
protected Long doInBackground(Request... params) {
// Perform your back ground task.
return null;
}
#Override
protected void onPostExecute(Long result) {
super.onPostExecute(result);
callback.onComplete(new Result()); //Here result is dummy but in real it should be contructred from doInBackground() method
}
}
Now last and important part to implement the interface and call asynctask. I am trying to reuse your code to have better clarity.
public class MainActivity extends Activity implements MyCallBack{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView status = (TextView) findViewById(R.id.status);
status.setText("Trying to connect to the server...");
}
private void onClick(){
//Similer to CommunicationManager.login(String email, String password); in your code.
LongRunningTask longRunningTask = new LongRunningTask(this);
longRunningTask.execute(new Request());
}
#Override
public void onComplete(Result result) {
try {
int result = result.getStatus
switch (result) {
case CommunicationManager.SUCCESS:
// login ok, go on with next screen
break;
case CommunicationManager.WRONG_EMAIL:
status.setTextColor(Color.RED);
status.setText("Wrong Email!");
break;
case CommunicationManager.WRONG_PASSWORD:
status.setTextColor(Color.RED);
status.setText("Wrong Password!");
break;
}
} catch (CommunicationException e) {
status.setTextColor(Color.RED);
status.setText("Unable to estabilish a connection!");
} catch (ProtocolException e) {
status.setTextColor(Color.RED);
status.setText("Protocol error!");
}
}
what if I want to execute service with Integer, String and Boolean parameters? Should I extend AsyncTask?
First Parameter is any user defined para. In case you need to pass multiple parameters then put them in to form of entity (i.e. - Class). Also, you can pass initial configuration parameter in constructor of AsyncTask i.e. - Communication URL.
Hope it will help.
Use multi threading, do all the communication in a different thread
Use worker thread, or AsyncTask for doing long-running operations.
Moreover, from Android Honeycomb, system throws exception, if you perform network operations on UI thread.