Unable to access Internet via WiFi from a Background service

匿名 (未验证) 提交于 2019-12-03 03:02:02

问题:

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?

回答1:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!