Problem description:
In my android App, I experience connectitity issues when doing a remote HTTP ("polling") call from an AsyncTask that was started after an alarm went off.
The lookup works very well when the standard Android setting "Enable always-on mobile data" (Settings -> Wireless & networks -> Mobile networks) is turned to "on".
Solution that works: alarm goes of, Android "Service" receives an alarm intent, starts a background thread (AsyncTask). The new t开发者_如何学Chread acquires a partial wake lock, establishes the connection (polling), notifies the user and releases the wake lock.
So far, so good. The issue is that, when always-on is turned "off", the polling fails most of the time if the phone was in standby for a while (> 30 min).
Since the polling thread sends a notification, I directly get feedback on the unsuccessful polling attempt.
Motivation:
A lot of users turn of "always-on" to reduce battery drain. So, it's likely that app users run into issues. I want to handle or prevent the "errors" that users will face.
Solution attempts:
I have experimented a lot without any major break-throughs:
- multiple retries and intermediate sleeps to give the phone some time to establish the connection
- http parameters (timeouts, etc.)
- different HttpClient (Apache)
Questions:
- What excactly does the setting "always-on" mean and what do developers have to regard?
- I am wondering if it is generally possible to implement an alarm-based polling mechanism that is able to establish a data connection even if "always-on" is turned "off".
- Are there any alternative solutions (no C2DM possible)?
Update:
It seems that not all Android devices have the "always-on" setting. It seems to be device or, more likely, provider-dependent.
If you intend to implement a live communications protocol, and are attempting to do so with short-lived polling, this may illuminate the issue with a similar experience:
We have had a similar requirement with our app, and have noticed a few things:
Not every network even has the "Enable always-on mobile data" preference. I am on Verizon with DroidX and this option isn't in the menu. Data IS always on.
Contrary to what you might think, sockets are NOT killed when the phone goes into the sleeping state, even if you don't hold a partial wake lock. Your app does stop receiving broadcast events when the phone goes to sleep (e.g. connectivity change events), so you cannot rely on the Android OS to tell you when you temporarily dropped your data connection. Rather than doing alarm polling, consider long polling whereby you send an HTTP request with Keep-Alive, and hold the connection open indefinitely until you get a response.
Blocking Socket read operations will not throw an IOException, EOFException, or otherwise when you drop your signal, so you need to have a separate thread periodically check the state of the network. The easiest way to do this is to use the NetworkInterface class and build something like this:
.
private OnCheckNetworkConnectivity networkConnectivityCallback = new OnCheckNetworkConnectivity() {
String ipAddress;
public boolean isConnected() {
String newIpAddress = getLocalIpAddress();
if(newIpAddress != null) {
if(ipAddress == null) {
ipAddress = newIpAddress;
return true;
}
if(!newIpAddress.equals(ipAddress)) {
ipAddress = newIpAddress;
return false;
}
// still the same IP address, we should still have the same connection
return true;
}
ipAddress = null;
return false;
}
public String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException ex) {
return null;
}
return null;
}
};
If you are using AlarmManager, it should wake the device.
I wonder if you are attempting to make the connection too quickly after the phone is awakened, before it has a chance to secure a connection.
Consider:
Checking for a connection in the AlarmManager receiver. If no connection is present, then:
Before returning from the AlarmManager receiver, start a separate thread. In the thread:
- Acquire a wake lock
- Register a broadcast receiver for CONNECTIVITY_CHANGE
- Wait a reasonable amount of time for a successful connection (indicated by the broadcast receiver event).
- If the time expires, release the wake lock and stop the thread and the phone will go back to sleep.
Try to make your connection only after receiving an event from the CONNECTIVITY_CHANGE broadcast receiver.
精彩评论