I have an android app which connects to a server using a socket connection which is kept open while the app is active. If the phone gets inactive (lock screen) or the user presses the home button, the application closes the socket connection and reopens it if the app becomes visible again.
This Pattern works fine on most of the android phones we have (about 15 devices), but the Motorola Milestone, Defy, SE Xperia Arc and the LG Optimus One need very long (>10 secs) to detect if a Wifi is available after and connect to it. So to wait for the best network connection I use the following code (before opening the socket to the server):
public static boolean waitNetworkConnection(Context context, int retries) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = getNetworkToTest(cm);
if (ni == null || !ni.isConnected()) {
// sleep a short while to allow system to switch connecting networks.
Tools.sleep(1000);
int counter = 0;
while (counter < retries && (ni == null || (ni.isAvailable() &&
!ni.isConnected()))) {
Tools.sleep(500);
ni = getNetworkToTest(cm);
counter++;
}
}
return (cm.getActiveNetworkInfo() != null &&
cm.getActiveNetworkInfo().isConnected());
}
and this method (use by the one above) to get the connection to test, which prefers a Wifi-Connection if one (not necessary connected) is available:
private static NetworkInfo getNetworkToTest(ConnectivityManager cm) {
NetworkInfo[] nis = cm.getAllNetworkInfo();
NetworkInfo ni = cm.getActiveNetworkInfo();
for (int i = 0; i < nis.length; i++) {
if (nis[i].getType() == 1 /* Wifi */ && nis[i].isAvailable()) {
ni = nis[i];
return(ni);
}
}
return(ni);
}
This works fine for most of the devices, but for the mentioned ones this very often fails and this method tells me to use a mobile network connection and the device switches the connection type while I open a socket connection which leads to a SocketException with a very generic error message so I'm unable to determine if the socket connection is caused by this issue or because of some other network error.
Simply doing a retry doesn't fix this either, as this breaks the handling for the other network errors because it then takes very long to detect a socket timeout (because it is checked twice).
Has anyone else ran into this problem (very slowing connect to Wifi) and has a solution for this?
Yes, this is a tricky problem. One option would be to wait for the right network state broadcast using a BroadcastReceiver.
As described here: How to detect when WIFI Connection has been established in Android?
And here: How can I monitor the network connection status in Android?
There is a project called droidfu that has a HTTP wrapper, that gets round the wi-fi / 3g issue.
Here is a snippet from the code for the BetterHttpRequestBase class:
public BetterHttpResponse send() throws ConnectException {
BetterHttpRequestRetryHandler retryHandler = new BetterHttpRequestRetryHandler(maxRetries);
// tell HttpClient to user our own retry handler
httpClient.setHttpRequestRetryHandler(retryHandler);
HttpContext context = new BasicHttpContext();
// Grab a coffee now and lean back, I'm not good at explaining stuff. This code realizes
// a second retry layer on top of HttpClient. Rationale: HttpClient.execute sometimes craps
// out even *before* the HttpRequestRetryHandler set above is called, e.g. on a
// "Network unreachable" SocketException, which can happen when failing over from Wi-Fi to
// 3G or vice versa. Hence, we catch these exceptions, feed it through the same retry
// decision method *again*, and align the execution count along the way.
boolean retry = true;
IOException cause = null;
while (retry) {
try {
return httpClient.execute(request, this, context);
} catch (IOException e) {
cause = e;
retry = retryRequest(retryHandler, cause, context);
} catch (NullPointerException e) {
// there's a bug in HttpClient 4.0.x that on some occasions causes
// DefaultRequestExecutor to throw an NPE, see
// http://code.google.com/p/android/issues/detail?id=5255
cause = new IOException("NPE in HttpClient" + e.getMessage());
retry = retryRequest(retryHandler, cause, context);
} finally {
// if timeout was changed with this request using withTimeout(), reset it
if (oldTimeout != BetterHttp.getSocketTimeout()) {
BetterHttp.setSocketTimeout(oldTimeout);
}
}
}
// no retries left, crap out with exception
ConnectException ex = new ConnectException();
ex.initCause(cause);
throw ex;
}
Related
So, my question is how to check if I am receiving any data or not. The scenario is that I am connected to a wifi network such as Starbucks wifi (which user should first connect to the network and then accept the agreement before receiving any data.)
This code is not serving my purpose.
ConnectivityManager cm =
(ConnectivityManager) _mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnected()) {
return true;
}
return false;
It returns true when I am connected to the network but I have not accepted the agreement yet. Thanks,
There's no way to know that, because that isn't something handled at an OS level- its handled at the router. THe OS only knows whether it has completed the handshake with the router and received an IP. Whether the router is going to throw him to a login screen is beyond the scope of the OS.
My app connects to a bunch of web services running on a web server on the cloud. So I need to make sure this connection is established before my app tries to do stuff. Instead of trying to check if internet is available, all I do is call one of my services. I have a simple one called TestService that just returns the word "success". So my app calls the service, receives the string "success" and knows that it is connected to the internet and successfully reaching my services. I don't have to worry about ConnectivityManager and NetworkInfo because this solution works well.
Of course, if my services themselves were to go down, then I would not know for sure if the device is connecting to the internet, I would only know for sure it is not reaching my service. However a) I make sure my services are redundant and have 99.9% uptime, and b) my app wouldn't be much use if not connecting to my services anyway. An alternative to just check for internet might be to try to connect to a public service from a reputable company that you know will alyways be online.
YOu can check this by trying to ping a website. For example
try {
Socket s = new Socket("www.google.com", 80);
return s.getLocalAddress().getHostAddress();
// network connection available
} catch (Exception e) {
// no network connection
}
I haven't tried it thogh.. But I think it should work.
If it doesn't work then you can try another thing. Send a httprequest to google and wait for response.
Heres what I have run in an asynctask:
public Boolean testServer(){
//stackoverflow.com/questions/9552743/proper-way-to-test-if-server-is-up-in-java
//stackoverflow.com/questions/9180072/android-check-connection-to-server
Boolean boolconnect = false;
try {
myUrl = new URL(UPLOAD_URL);
connection = myUrl.openConnection();
connection.setConnectTimeout(30*1000);
connection.connect();
Log.i(TAG, "FROM SERVER Connection made to: " + UPLOAD_URL);
boolconnect = true;
} catch (SocketException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER socket exception:" + e);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER Unknown host:" + e);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.i(TAG, "FROM SERVER IOexception:" + e);
}
return boolconnect;
}
Im trying to measure how much bytes my app has received.
Im doing it this way:
long receivedBytesBefore = TrafficStats.getTotalRxBytes();
...
doSomething();
...
long receivedBytesAfter = TrafficStats.getTotalRxBytes();
long receivedBytes = receivedBytesAfter - receivedBytesBefore;
My problem is that getTotalRxBytes() always returns 0. So my result is 0 no matter what I do.
I have found out that the method is just reading some textfiles like
/sys/class/net/rmnet0/statistics/rx_bytes
So I looked into these files and they all contain just "0".
Do I miss something or do I have to activate this function somehow?
Is there another way to measure how much bytes my app has received?
My Android device is a Samsung Galaxy Ace S5830 running Android 2.3.3
I can verify this was happening to me as well.
From behavior I've observed, it appears that getTotalRxBytes only works when wifi is connected. But something to be aware of is that if you are trying to get an accurate number of bytes received for a file for example, there seams to be some extra bytes sent.
So if you don't need it to be super accurate. You can use getMobileRxBytes() for when wifi is not active and getTotalRxBytes() for when wifi is active.
Here is a simple example.
ie:
ConnectivityManager connManager;
connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
mMobile = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
long initialBytes = 0;
long finalBytes = 0;
long byteDifference = 0;
boolean onWifi= false;
if (mWifi.isConnected())
{
//wifi connected
initialBytes = TrafficStats.getTotalRxBytes();
onWifi = true;
}
else if (mMobile.isConnected())
{
//if 3g/4g connected
initialBytes = TrafficStats.getMobileRxBytes();
onWifi = true;
}
else
{
//Something funny going on
Log.e("Error", "Something funny going on");
return;
}
// Process whatever you want to process
if(onWifi)
{
finalBytes = TrafficStats.getTotalRxBytes();
}
else
{
finalBytes = TrafficStats.getMobileRxBytes();
}
byteDifference = finalBytes - initialBytes;
Something along these lines. Hopefully this helps.
These may not be supported on your device, and can return UNSUPPORTED, which might be 0.
I have written an app to start my server at home remotely. The app works without problems in the emulator and also on my smartphone (HTC desire, Android 2.2) when WiFi is enabled.
However it does not work when WiFi is disabled.
Before restarting I first check if it's already running. To do this I use sockets and I first connect to a dyndns address. After that I try to connect to my ip-box where I can switch on my computer by sending commands via a socket.
When the connection to that socket fails I know the server is not running.
The relevant code is:
socket = new Socket();
socket.connect(new InetSocketAddress(serverName, port), 10000);
status = socket.isConnected() == true;
socket.close();
If there's an exception (SocketException) I know that the server is not running.
This approach works perfectly when I have switched WiFi on. However if WiFi's not switched on then the connect always says it's ok, even if it could not establish a connection since the server is not available.
Is there a way to check if the the connection is really established, even if WiFi is disabled?
Any suggestions welcome!
Thanks,
Rudi
Try to open your socket like this :
public boolean connect(String ip, int port) {
try {
this.clientSocket = new Socket(ip, port);
clientSocket.setSoTimeout(timeout);
this.outToServer = new DataOutputStream(clientSocket
.getOutputStream());
this.inFromServer = clientSocket.getInputStream();
isconnected = true;
} catch (IOException e) {
Log.e("TCPclient", "connection failed on " + ip + ":" + port);
isconnected = false;
return isconnected;
}
Log.e("TCPclient", "connection to " + ip + " sucessfull");
return isconnected;
}
If connection is not successful , it will generate an IOException (work when wifi enabled and no server , and when wifi is not enabled(HTC desire 2.3)).
This code is not really correct ,it's just a short version
EDIT
Try to check wfi state like this (it is not practical but it should work)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (ni.isConnected()) {
Toast.makeText(this,"Wifi enabled", Toast.LENGTH_LONG).show();
Log.d("WiFiStateTestActivity", "WiFi!");
} else {
Toast.makeText(this,"Wifi not enabled", Toast.LENGTH_LONG).show();
Log.d("WiFiStateTestActivity", "not WiFi!");
}
}
Don't forget to set the permission in manifest.xml to allow you app to open a socket.
I have a Service which downloads a list of files one by one. However, when the internet connection goes down or it is disconnected the downloading gets stuck. I did some research on internet connection checking and found some examples utilizing the ConnectivityManager class.
However, the ConnectivityManager class seems to be checking only if the phone is connected to a network using WIFI or MOBILE. It doesn't check whether there actually is internet access. So you could be connected to a network and still be unable to browse using HTTP.
Is there any way other than ConnectivityManager?
I'm using the following alternative:
public boolean isOnline(){
try{
URL url = new URL("http://www.google.com");
HttpURLConnection urlc = (HttpURLConnection)url.openConnection();
urlc.connect();
urlc.setConnectTimeout(1000 * 2);
if(urlc.getResponseCode() == 200){
return true;
}else{
}
}catch(Exception e){
return false;
}
return true;
}
The problem with this approach is that it's too slow. Since I'm checking inside a while loop (where the downloading of the file is taking place) the download speed is decreased due to the calls to isOnline():
while(..something){
if(!isOnline())
killService();
return;
}
Any other way of doing this or improving on that one
EDIT:
I was thinking of running a thread that checks if the download is stuck within a specified amount of time such as 20 secs or 30secs and if it is stop the service (meaning that the internet is down).
int downloadProgress;
Runnable checkNet = new Runnable(){
public void run(){
// code to check download using Timer - need help implementing this
}
};
I'm doing bluetooth development for connecting with a PC. I've basicly used the BTChatExample and changed the UUID to the standard PC SPP-profile.
Trying to close a bluetooth application during a blocking read, by closing the BluetoothSocket will leave the Bluetooth stack in a unusable state. This can only be fixed by disabling and enabling bluetooth and restarting the application. Checking logcat, you can see that some of the internal methods are failing, leaving a open port. Any information on this?
First of all there seams to be differences on how bluetooth is implemented on N1 and HTC Legend/Desire both running 2.1, do you know anything about this?
Connecting isn't 100% reliable, sometimes I get a warning saying ~PortSystemContext init: FAILED. This leaves bluetooth unusable, and restarting is needed.
Am I right in assuming that SPP is the only profile supported for use with the APIs? That's what the docs on the BluetoothAdapter says.
I would love to discuss issues on bluetooth with a developer and iron out these bugs so that Android can have good proper Bluetooth support it deserves.
Closing a socket in one thread during a blocking read should definitely cause the read to return (by throwing IOException) and should not leave the stack in a 'bad state'. This is the behavior on Droid and Nexus.
I spoke directly to the original poster, and he had observed this problem on HTC Legend and HTC Desire. It appears like they are not implementing the API's correctly. I am raising the issue with them.
You are correct that SPP/RFCOMM is the only profile that is intended for use with the API's. SPP/RFCOMM gets you a streaming socket which is good enough for a lot of use cases.
For now I recommend BT development on Nexus One / Motorola Droid, which are considered 'reference' implementations of the Bluetooth API's.
May I suggest that you do not perform blocking read() calls unless you have first checked that there is data ready to be read by using inputstream.available() which returns an integer indicating how many bytes are waiting in the input buffer.
long timeouttime = gettimeinseconds() + 2;
String response = "";
while (gettimeinseconds() < timeouttime) {
if (inputstream.available() > 0)
response = response + inputstream.read();
} else {
Thread.sleep(100); // sleep to slow down the while() loop.
}
}
return response;
That's just pseudo code, and its oversimplified. The bottom line is that we're not performing any blocking calls (read()) unless we're sure they will return immediately without delay.
Also, I highly recommend using BufferedInputStream instead of the standard InputStream.
Anyone could solve this problem ?
I try the following code :
// Keep listening to the InputStream while connected
while (!isInterrupted)
{
try
{
//Clear buffer
buffer = new byte[1024];
// Read from the InputStream
if (mmInStream != null && mmInStream.available() > 0)
{
if (isInterrupted)
break;
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Act_Main.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
else
{
try
{
synchronized (this)
{
this.wait(100);
}
if (isInterrupted)
break;
}
catch(InterruptedException ex)
{
Log.e(TAG, "WAIT_EXCEPTION:"+ ex.getMessage());
}
}
}
catch(Exception ex)
{
Log.e(TAG, "disconnected", ex);
connectionLost();
break;
}
}
And I changed the isInterrupted boolean in the cancel() method. Here is my stop() method:
/**
* Stop all threads
*/
public synchronized void stop()
{
isStop = true ;
if (D)
Log.d(TAG, "stop");
if(mConnectThread != null)
{
mConnectThread.cancel();
mConnectThread = null;
}
if(mConnectedThread != null)
{
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_NONE);
}