2014-07-15 10 views
5

Vado dritto su alcuni fatti/figure. Ho scoperto che pls mi aiuta se hai affrontato/risolto un problema simile.Impossibile accedere a Internet tramite Wi-Fi da un servizio in background

Trasmetto i dati ogni 5 minuti a un server a meno che l'utente non lo disattivi manualmente con l'aiuto di un wakeful broadcast receiver tramite un intent service. Inoltre ottengo blocchi wifi prima di farlo (ho già provato questo senza serrature, non funziona neanche)

Ora sui dispositivi che rimandano dati da reti di dati regolari (2G/3G/4G) questo funziona bene ma su quelli collegati a una rete wifi in qualche modo mi ottiene un getActiveNetworkInfo() come null (Anche colpendo direttamente l'URL web dà un hostname not found error) questo sicuramente accade su HTC One (con v.4.4.2 installato) altri dispositivi funzionano bene.

  • tutti noi conosciamo il precedente direttore ufficiale connettività hanno avuto problemi a tornare troppo vero/falso condizione, anche se la rete non è/era disponibile. Dubito fortemente se sono tagliate di nuovo o è solo un po 'su misura OEM jing-bang

  • l'allarme viene licenziato >> fuochi il wakeful broadcast receiver >> get wifi locks >> sleep the thread for 3 secs inside the onReceive` dopo aver ottenuto le serrature al fine di attendere per la connessione wifi per riprendere la connettività >> fare il mio lavoro di servizio in seguito.

  • sto pesando la possibilità di forzare l'uso della connessione dati mobile invece di wifi. (utilizzando startUsingNetworkFeature()) Qualcuno ha una soluzione per lo stesso?

risposta

5

Spero di non essere troppo tardi, ho avuto un problema come la tua. La mia app aveva bisogno di inviare dati (dal file di archiviazione interno) al server a determinati intervalli (30m/1h/2h/4h). Per getActiveNetworkInfo() per dare il risultato corretto, è necessario riattivare il wifi (perché dopo 5-15 minuti di spegnimento del telefono il wifi si spegne). Questo mi ha dato un sacco di problemi, ma ecco come funziona la mia soluzione:

In primo luogo, ho un WakefulBroadcastReceiver che viene chiamato quando ho bisogno di svegliare il telefono:

/** 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 
    } 
} 

Nota la funzione public static void setAlarm(Context, int, Calendar), io uso quella funzione per impostare gli allarmi (vedi Applicazione principale).

Avanti, il servizio stesso non è un IntentService, basta 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; 
    } 
} 

Ho anche un BOOTBroadcastReceiver, quindi se l'allarme è impostato e poi il telefono viene riavviato verrà eseguito di nuovo il servizio:

/** 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()); 
     } 
    } 
} 

Ultimo, ma non meno importante nel Main Activity, ho avviare il servizio da parte dei ricevitori a partire:

// 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); 

E quando mi fermo il servizio, mi fermo il ricevitore BOOT troppo:

// 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); 

Inoltre, non dimenticare di aggiungere le autorizzazioni e componenti adeguati per manifestare:

<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" /> 

Credo coperto tutto, sarò lieto se questo aiuti qualcuno a risolvere i loro problemi.

Problemi correlati