I'm trying to figure out how to handle intermittent network connectivity in regards to Amazon SQS on Android. I need to send messages every 10 minutes (or so) and would like any messages that cannot be delivered due to network issues be sent at the earliest time when the network is restored. My hosted service orders messages so getting a few queued messages at once is no issue, but having messages be completely dropped is a problem.
AFAIK, my retry policy is set to retry up to 25 times and then give up. Obviously this is not a great solution, but I didn't see any hooks for network connectivity callbacks or any option to have it retry when the connection is restored if the cause of the error is due to network issues.
The only alternative I can think of (using what I know about this API) is to implement a VERY long-lasting back off strategy and hope the user gets network again with X amount of time, but that seems incorrect.
ClientConfiguration ccfg = new ClientConfiguration();
ccfg.setRetryPolicy(new RetryPolicy(null, null, 25, false));
AmazonSQSClient client = new
AmazonSQSClient(params[0].credentialsProvider, ccfg);
Setting the retry policy is not going to help you in this case.
What you might need to do is:
Cache sqs messages when network is not reachable on mobile.(either file or db).
Implement a network change listener on android and trigger sqs calls when the networks changes from unreachable to reachable.
Take a look at https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html
Related
I am having an Android app which needs continuous network monitoring and I require to be notified when I have internet and when not. I have tried Android connectivity manager, which only tells if the internet wifi is connected or not, but doesn't tell if there is reachability. So I used the following ping method to check the reachability.
private fun isOnline(): Boolean {
return try {
val timeoutMs = 1500
val sock = Socket()
val socketAddress = InetSocketAddress("8.8.8.8", 53)
sock.connect(socketAddress, timeoutMs)
sock.close()
true
} catch (e: IOException) {
Logger.e(TAG, e.toString())
false
}
}
Now to keep checking this every 1 minute I am using a fixedRateTimer which will call this method every 1 minute and notify accordingly.
Now the problem I am facing is, this works fine for few hours and I get proper connection status. But after few hours the ping starts timing out. I get a timeout exception every alternate ping.
I want to understand few things,
First thing, is it okay to ping for every 1 minute to check the network?
Can the client be blocked by Google for frequent pings?
Or is it anything related to the ISP?
Is there a better approach in android to achieve what I want?
Any advice would be appreciated. Thanks in advance!
PS : I have also tried onCapabilitiesChanged and the callbacks are not immediate for every connection and disconnection, though the google documentation says callbacks will be immediate.
but doesn't tell if there is reachability. So I used the following ping method to check the reachability
First, that is not a ping. That is opening and closing a socket.
Second, it can only tell you if you can open a socket connection to that IP address. It does not tell you if you can access anything else. So, this is subject to false positives (you can reach 8.8.8.8 but not your real server) and false negatives (8.8.8.8 is blocked by network management, but your real server is not).
Now to keep checking this every 1 minute I am using a fixedRateTimer which will call this method every 1 minute and notify accordingly.
That will only work so long as your process is running, and only until you lose Internet access due to Doze mode/app standby/manufacturer-specific power management solutions.
First thing, is it okay to ping for every 1 minute to check the network?
It has flaws. "Okay" is a subjective measure; only you can decide whether it is "okay" for you.
Can the client be blocked by Google for frequent pings?
This is not a ping. It is certainly within Google's power to take action for buggy clients like this.
Or is it anything related to the ISP?
There are lots of pieces involved in an Android device reaching 8.8.8.8:
The network management for whatever WiFi network the phone is using for connectivity (where relevant)
The mobile carrier or ISP
The various other ISPs between you and Google
Google's own network management
Any of them could take steps, if they so chose.
Is there a better approach in android to achieve what I want?
I would aim to eliminate the "need" entirely, as Android and device manufacturers will be fighting you every step of the way.
At minimum:
Do a valid expected operation, such as an actual ping; and
Do it against a relevant server
IIRC, 8.8.8.8 is a DNS server. If your app is a DNS client, you are welcome perform a valid, useful DNS operation against 8.8.8.8. If your app is not a DNS client, quit messing with somebody else's server. Run your own server and test reaching it. For example, you could run a Web server and test whether you can retrieve your robots.txt file.
Most of the question has been answered, but I want to pick up on the most important one.
First thing, is it okay to ping for every 1 minute to check the network?
No. It is not OK.
It is wasteful
You are consuming Google's resources. Resources that you are not paying for. If everyone did what you are doing it would cost Google a lot of money ... to run a much larger fleet of DNS servers, etc to cope with bazillions of vacuous connections.
You are also consuming resources in the along the route from the user's app to Google with the (unnecessary) network traffic.
This would also apply if you were doing real (ICMP) pings, though not to the same extent.
And bear in mind, this is also consuming electricity. And that means more fossil fuel is burned.
It may be incurring costs for the user
Depending on the what their mobile phone plan is, this may be costing the user of your app network charges. Each of those connections your App is making probably being metered. If they are not aware of this ... or they can't turn this (mis-)feature off, they could get rather annoyed about this. (I would be!)
It doesn't actually work
What you are doing doesn't actually test if the internet is available. What you are actually doing is seeing if your App can connect to the Google DNS services. But the fact that the DNS server is accepting connections doesn't mean that the real services that the user wants to use will be accessible and working. (And vice versa!)
As you noted, connections will occasionally fail for reasons that are probably due to transient problems that resolve themselves. There is nothing you can do about that. That could be a false negative for the internet being "up".
Even if there was a reliable way to find out if the internet is "working", your "pinging" is only giving you a single sample. The internet could go "down" (or come back "up") any time in the up to 60 seconds between your pings. More false negatives and positives.
And as noted, Google DNS is not the same as "the internet", and "the internet" is not the same as the service that the user of you App is really interested in.
Your app doesn't REALLY need this information
The user does not need to know minute by minute that the internet is available.
Most of the time a typical user is doing something else.
They only actually need to know if they are actively using some service. And even then, knowing that the internet was up 60 seconds ago is probably no help to them.
Unfortunately, the only way that the user can tell if the service they are talking to is available right now ... is to actually try to use it.
So what is the real solution?
IMO, there are two approaches:
Forget it. In most cases, the user really doesn't need to know. It is not actually going to materially effect the user if your App does not distinguish "service down" from "internet down".
If you can't forget it.
Implement an end-point on your service that you can ping ... and pay the bills!
If you are trying to implement this in an App where you are talking to someone else's services, stop free-loading on Google. If you want an "internet is up/down" feature in your App, implement your own service for doing this ... and pay the bills.
Note that you will still have false positives and false negatives to deal with. There is no solution to that. It is a fundamental property of the internet.
I want to change user status to show either he is online or not. I want to change user status to false in database when User close application or when he loses connection with server.
As a method is available named as onDisconnect() .I have used that method to update user status by using following code .
HashMap<String,Object> user_online_status=new HashMap<String,Object>();
user_online_status.put("online",true);
DatabaseReference firebaseDatabase=FirebaseDatabase.getInstance().getReference().child("Users").child(userId);
firebaseDatabase.updateChildren(user_online_status);
//then to show user offline
user_online_status.put("online",false);
firebaseDatabase.onDisconnect().updateChildren(user_online_status);
I do that task but as it is on client side and If we want to monitor user connection with server and when connection is terminated node should be updated by Server Instead of Client.How can we change node value from server as User lose connection with server?
There are two ways the user can get disconnected from the Firebase Database.
a clean disconnect, where the client sends a signal to the server before it disconnects.
a dirty (for lack of a better term) disconnect, where the connection gets closed before the client can send a signal.
In the case of a clean disconnect, your onDisconnect handlers will immediately fire and thus your database will immediately be updated.
In the case of a dirty disconnect, Firebase depends on the socket layer to signal when the remote client is gone. This may take anywhere up to a few minutes. But eventually the server will detect/decide that the client is gone, and your onDisconnect handlers will fire.
A small note in your data structure: you that there is a 1:1 relation between a user and a connection. That is unfortunately not the case.
A user may be connected from multiple devices. If they now disconnect from one of those devices, the onDisconnect from that device will set online to false while they may still be connected on another device.
Mobile devices/networks have a habit of going through occasional disconnect/reconnect cycles. This means that you may have multiple connections, even on a single device. In case of a dirty disconnect, the onDisconnect handler may be fired much later, when you've already set online to true for the new connection. In such a case, your lingering onDisconnect handler will set online to false while the user may already be reconnected.
All this is to say that you should not rely on having a 1:1 relation between a user and their connection(s). The samples in the Firebase documentation treat connections as a collection and assume that the user is connected as long as there is any "connect ID" (generated by push()) left for that user. I recommend you do the same to prevent hard to debug race conditions and connection problems.
I am building an android app that exchanges data with our server through http api calls. In many cases users are complaining that the app is slow or doesn't work at all.
The most common cause of that is bad network connectivity (low 3g/wifi signal or congested public wifi).
What is the best way to detect bad connections? Once i can detect bad connectivity an icon or toast message can be used to inform the user about the situation.
I am using HttpUrlConnection for the api calls.
I think you can make use of ConnectivityManager. Call getActiveNetworkInfo() and then call getDetailedState() on the NetworkInfo object received. You can check the state of the connection and whether it is VERIFYING_POOR_LINK, though I don't know in which conditions this state is active.
Also you might want to listen to network changes as described in Detect Connection Changes.
I'd probably use latency. Save the time when you get the request, and when the request finishes. If you're seeing numbers that are too high, pop up the warning. If you're downloading large files, you may wish to switch to throughput (how many kbps you're transfering).
as far as i remember http is "connectionless"..
you should try concentrating on minimizing the size of your traffic.. (compress, divide.. etc)
if you really want to test connectivity i guess you should do pings.. every x seconds.. then if the ping is bad you could warn the user..
I understand that on a wifi network there are sudden disconnections which prevent me from sending messages to my server.
But sometimes there's still one last chance before the disconnection, for example if the signal is low or the user is trying to turn off the wifi. On those occasions I would like to send a logout message to my server.
How do I detect disconnections like those?
I tried to retrieve changes of connectivity by registering a broadcast listener:
registerReceiver(this,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
...
public void onReceive(Context context, Intent intent) {
NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if( (info.getState()== State.DISCONNECTING) && (info.getType() == ConnectivityManager.TYPE_WIFI) ) {
//send logout
}
But it looks like at that time it's already too late. My logout message doesn't go through.
Is there a better way?
[Update 1]
I also tried:
if( (info.getDetailedState()== DetailedState.DISCONNECTING) && connectionTypeOK ) {
[Update 2 - SOLUTION]
The solution is, as stated below, using a combination of receiving the RSSI_CHANGED_ACTION and WIFI_STATE_CHANGED_ACTION broadcasts to monitor the signal strength and the WIFI_STATE_DISABLING events respectively. When this happens, I send my logout request. This works exactly as I needed. Thanks!!
You could try to implement a variable "heartbeat" function, by using WifiManager to detect changes in signal strength. Here you can find some related code, btw.
Now, once you receive a RSSI_CHANGED notification, according to the corresponding signal strength, you will update the frequency of your app's "heartbeats" to the server: if the signal is strong, you will only need to notify the server infrequently that the app is alive. Once the signal becomes week, however, just like adrenaline kicking in for a real live being, so should your app notify the server more frequently. If the signal's strength recovers, you'll send a specific message to let the server know everything is all right again; if, however, the server does not receive this message in a certain period of time and the "heartbeat" stops - your app ceases notifications for that amount of time - then the server logs it out until receiving from it again.
If you're based on TCP connections, the server should know when a session disconnects unexpectedly - it will get an RST or FIN packet, depending on the router configuration between the client and server.
There's no need to do anything from the client's point of view - TCP connections are designed so you can know when they're interrupted.
Why don't you have the server regularly ping the client, at certain intervals, and just log out if it doesn't get a response? Trying to make this happen through client side will be cumbersome.
A better way is not to have sessions at all, if possible.
Why is it a problem if the user doesn't log out?
Maybe this is a long shot.. but why don't you use Google push notifications to start an activity if wifi is on. That would tell the server that the phone is "online". If that doesn't happen in X seconds or 1 minute ou whatever, redirect it to somewhere else.
I would implement a handler on the server that handles when the client is not able to receive a message. After each message the phone could send a message back to the server saying it successfully received the message.
are you looking for a good way for users to send / receive data after a disconnection?
HTML5 has a local storage (with a good file size too) so if a user is attempting a huge form, you first save it locally and then attempt to send it to server. if failed when the user loads the page again, you can first check if the file has some content, and if so, you can send that data, clear the content and proceed accordingly.
may be this will help you out http://www.html5rocks.com/tutorials/appcache/beginner/
or look at the local storage tutorial: http://www.youtube.com/watch?v=h0uZIljjElo
using this you could save frequent status data and modify it on the fly.
and Android should support HTML5 too.
I have a simple app that periodically sends HTTP_GET requests to a server. When sending requests over 3G, I noticed that the requests sometimes time out (and the server-side logging shows that it NEVER receives the request either).
After trying out different combinations I found one consistant pattern when this problem occures (it times out after every 5-15 successful requests).
- TelephonyRegistry: notifyDataConnection() state=2isDataConnectivityPossible()true, reason=null
- TelephonyRegistry: broadcastDataConnectionStateChanged() state=CONNECTEDtypes=default supl, interfaceName=rmnet0
- NetworkLocationProvider: onDataConnectionStateChanged 3
According to Google, NetworkLocationProvider is changed to 'DATA_SUSPENDED', which implies "connection is up, but IP traffic is temporarily unavailable". (see TelephonyManager). On the situations where HTTP_GET requests succeeds, the state is changed to '8'. My app doesn't use the location manage and I've shut down every other non-critical app from running!
I want to know:
What is the cause of this issue? Why does the connection status go to DATA_SUSPENDED?
Is it possible to avoid/overcome this problem?
Any help/insight into this is much appreciated! Thanks in advance!
I have the same problem with my app running on an Huawei IDEOS X3 with Android 2.3.5. The app sends data each minute to a server using HttpClient.
Using logcat I can see that the data connection is lost and then reestablished after a short while. Previously my app stopped working since it tried to send data without a connection causing an exception which was not properly handed.
I don't know the reason for the intermittently dropped data connection but I now handle the situation by checking if there is a data connection prior to sending the data. In my case it does not matter if some data is never sent. If it was important to avoid data loss, I could buffer the data and send it once the connection was back.
public Boolean isDataConnection() {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
return tm.getDataState() == TelephonyManager.DATA_CONNECTED;
}