I'll get straight onto some facts/figures I discovered pls help me if you've faced/solved a similar problem.
I send back data every 5 minutes to a server unless the user manually toggles it Off with the help of a wakeful broadcast receiver through a intent service. Plus I get wifi locks before doing (I've tried this already without locks; doesnt work either)
Now on devices sending back data from regular data networks (2G/3G/4G) this works fine but on those connected to a wifi network somehow gets me a getActiveNetworkInfo() as null (Even directly hitting the web URL gives a hostname not found error) this surely happens on HTC One (with v.4.4.2 installed) others devices do work fine.
we all know about the previous official connectivity manager have had issues in returning too much true / false condition even if network was not/available. I highly doubt if they've cropped up again or is just some custom OEM jing-bang
the alarm gets fired >> fires up the wakeful broadcast receiver >> get wifi locks >> sleep the thread for 3 secs inside theonReceive` after getting locks in order to wait for wifi to resume connectivity >> do my service work afterwards.
am weighing the possibility of forcing use of mobile data connection instead of wifi. (by using startUsingNetworkFeature()) Does anyone have a solution for the same?
Hope I'm not too late, I had a problem just like yours. My app needed to send data (from internal storage file) to server at certain intervals (30m/1h/2h/4h). For getActiveNetworkInfo() to give proper result, you need to wake the wifi (because after 5-15 min of phone sleep wifi turns off). That gave me a lot of trouble, but here's how my solution works:
First, I have a WakefulBroadcastReceiver that gets called when I need to wake the phone:
/** Receiver for keeping the device awake */ public class WakeUpReceiver extends WakefulBroadcastReceiver { // Constant to distinguish request public static final int WAKE_TYPE_UPLOAD = 2; // AlarmManager to provide access to the system alarm services. private static AlarmManager alarm; // Pending intent that is triggered when the alarm fires. private static PendingIntent pIntent; /** BroadcastReceiver onReceive() method */ @Override public void onReceive(Context context, Intent intent) { // Start appropriate service type int wakeType = intent.getExtras().getInt("wakeType"); switch (wakeType) { case WAKE_TYPE_UPLOAD: Intent newUpload = new Intent(context, UploadService.class); startWakefulService(context, newUpload); break; default: break; } } /** Sets alarms */ @SuppressLint("NewApi") public static void setAlarm(Context context, int wakeType, Calendar startTime) { // Set alarm to start at given time alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, WakeUpReceiver.class); intent.putExtra("wakeType", wakeType); pIntent = PendingIntent.getBroadcast(context, wakeType, intent, PendingIntent.FLAG_UPDATE_CURRENT); // For android 4.4+ the method is different if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { alarm.setExact(AlarmManager.RTC_WAKEUP, startTime.getTimeInMillis() + 5000, pIntent); } else { alarm.set(AlarmManager.RTC_WAKEUP, startTime.getTimeInMillis() + 5000, pIntent); } // The + 5000 is for adding symbolic 5 seconds to alarm start } }
Note the public static void setAlarm(Context, int, Calendar) function, I use that function to set the alarms (see Main application).
Next, the service itself isn't an IntentService, just Service:
/** Service for uploading data to server */ public class UploadService extends Service { private static final String serverURI = "http://your.server.com/file_on_server.php"; private Looper mServiceLooper; private ServiceHandler mServiceHandler; WifiLock wfl; private Intent currentIntent; private SharedPreferences sharedPrefs; private int updateInterval; private boolean continueService; /** Service onCreate() method */ @Override public void onCreate() { super.onCreate(); // Initialize wifi lock wfl = null; // Initialize current Intent currentIntent = null; // Create separate HandlerThread HandlerThread thread = new HandlerThread("SystemService", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); // Get shared variables values if set sharedPrefs = getSharedPreferences("serviceVars", MODE_PRIVATE); updateInterval = sharedPrefs.getInt("sendInterval", 60); // in your case 5 continueService = sharedPrefs.getBoolean("bgServiceState", false); } /** Service onStartCommand() method */ @Override public int onStartCommand(Intent intent, int flags, int startId) { // If continuing, set new alarm if (continueService) { Calendar nextStart = Calendar.getInstance(); nextStart.set(Calendar.SECOND, 0); // Set alarm to fire after the interval time nextStart.add(Calendar.MINUTE, updateInterval); WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD, nextStart); } // Get current Intent and save it currentIntent = intent; // Acquire a wifi lock to ensure the upload process works WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE); wfl = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "WifiLock"); if (!wfl.isHeld()) { wfl.acquire(); } // For each start request, send a message to start a job and give // start ID so we know which request we're stopping when we finish Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If service gets killed, it will restart return START_STICKY; } /** Handler that receives messages from the thread */ private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } /** Executes all service operations */ @Override public void handleMessage(Message msg) { // First wait for 5 seconds synchronized (this) { try { wait(5 * 1000); } catch (Exception e) { e.printStackTrace(); } } // Start checking if internet is enabled boolean hasInternet = false; long endTime = System.currentTimeMillis() + 55 * 1000; // Check every second (max 55) if connected while (System.currentTimeMillis() < endTime) { if (hasInternet(UploadService.this)) { hasInternet = true; break; } synchronized (this) { try { wait(1000); } catch (Exception e) { e.printStackTrace(); } } } // Connected to internet or 60 (5 + 55) seconds passed if (hasInternet) { // Connect to server connectToServer(serverURI, fileName); } else { // Can't connect, send message or something } // Stop service stopSelf(msg.arg1); } } /** Checks if phone is connected to Internet */ private boolean hasInternet(Context context) { ConnectivityManager cm = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = null; if (cm != null) { ni = cm.getActiveNetworkInfo(); } return ni != null && ni.getState() == NetworkInfo.State.CONNECTED; } /** Service onDestroy() method */ @Override public void onDestroy() { // Release wifi lock if (wfl != null) { if (wfl.isHeld()) { wfl.release(); } } // Release wake lock provided by BroadcastReceiver. WakeUpReceiver.completeWakefulIntent(currentIntent); super.onDestroy(); } /** Performs server communication */ private void connectToServer(String serverUri, String dataFileName) { // this function does my network stuff } /** Service onBind() method - Not used */ @Override public IBinder onBind(Intent intent) { return null; } }
I also have a BOOT BroadcastReceiver, so if alarm is set and then phone is rebooted it will run service again:
/** Receiver for (re)starting alarms on device reboot operations */ public class BootReceiver extends BroadcastReceiver { WakeUpReceiver alarm = new WakeUpReceiver(); /** BroadcastReceiver onReceive() method */ @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { WakeUpReceiver.setAlarm(context, WakeUpReceiver.WAKE_TYPE_UPLOAD, Calendar.getInstance()); } } }
Last, but not least in the Main Activity, I start the service by starting receivers:
// Start upload service now Calendar timeNow = Calendar.getInstance(); WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD, timeNow); // Enable BootReceiver to (re)start alarm on device restart getPackageManager().setComponentEnabledSetting( new ComponentName(this, BootReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
And when I stop the service, I stop the BOOT receiver too:
// Disable BootReceiver to (re)start alarm on device restart getPackageManager().setComponentEnabledSetting( new ComponentName(this, BootReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Additionally, don't forget to add proper permissions and components to manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <receiver android:name=".BootReceiver" android:enabled="false" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <receiver android:name=".WakeUpReceiver" /> <service android:name=".UploadService" android:enabled="true" android:label="ServerUpload" android:launchMode="singleInstance" />
I think I covered it all, I'll be glad if this helps anyone solve their problems.