2017-06-19 46 views
7

Mam aplikację, która używa AlarmManager do planowania powtarzającego się alarmu co X ilość czasu. Kiedy mój odbiornik otrzymuje Intent, musi wykonać żądanie http.Wywołanie połączenia sieciowego z przekroczeniem czasu alarmu

Sam alarm działa dobrze i uruchamia się, gdy powinien. Jednak połączenie sieciowe rozpoczyna limit czasu, gdy telefon nie jest używany. Aby być bardziej szczegółowym:

Kiedy planuję strzelać co minutę (zła praktyka, wiem, ale tylko dla zilustrowania), pierwsze 5-8 minut żądanie się powiedzie. Potem otrzymuję numer java.net.SocketTimeoutException: connect timed out. Czasami to się udaje, ale w większości tak się dzieje.

Próbowałem ustawić limit czasu połączenia/odczytu/zapisu na minutę, ale otrzymałem ten wyjątek zamiast powyższego: java.net.ConnectException: Failed to connect to myapp.example.com/123.45.67.89:80.

Mój kod:

public class AlarmReceiver extends BroadcastReceiver { 

    @Override 
    public void onReceive(Context context, Intent intent) { 
     // Consider mApi and myBody to be initialised and valid 
     mApi.myPostRequest(myBody).enqueue(new Callback<Void> { 

      @Override 
      public void onResponse(Call<Void> call, Response<Void> response) { 
       //Does not get here 
      } 

      @Override 
      public void onFailure(Call<Void> call, Throwable t) { 
       t.printStackTrace(); 
      } 
     } 
    } 
} 

Rzeczy próbowałem:

  • jak wspomniano wcześniej, zwiększając limity czasu
  • nabycia WakeLock w onReceive i zwalniając go, gdy połączenie jest wykonywane (dodano uprawnienie )

Pozostałe informacje:

  • Alarm jest ustawiony za pomocą alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), interval, pendingIntent); z mojego Activity.
  • Używam Retrofit (2.1.0) dla komunikacji sieciowej, ale prawdopodobnie można by było się domyślić, że z mojego kodu;)

pomysłów, w jaki sposób uzyskać połączenie sieciowe działa, gdy telefon jest spanie?

+5

Brzmi [ingerencja tryb Doze] (https: //developer.android.com/training/monitoring-device-state/doze-standby.html). W przypadku urządzeń z Androidem 5.0 lub nowszym rozważ przejście na "JobScheduler" i skonfiguruj zadania, aby uzyskać kontrolę tylko wtedy, gdy istnieje połączenie z Internetem. – CommonsWare

Odpowiedz

1

Masz podstawowy błąd w swoim kodzie - nie możesz wysyłać żądań (ani żadnych długotrwałych operacji) w swoim odbiorniku - umiera po ~ 10 sekundach, więc może to być przyczyną niektórych twoich błędów.

Powinieneś przenieść logikę żądania do usługi (IntentService), którą uruchomisz z odbiornika i tam ją zgłosisz.

To powinno działać dobrze.

1

Należy używać tutaj usługi JobService, ma wiele ograniczeń do obsługi różnych scenariuszy, a także zadanie jest gwarantowane do wykonania przez system.

Problemem jest tryb drzemki i użycie JobService można łatwo rozwiązać.

Wdrożenie jest również łatwe, wystarczy utworzyć usługę JobService, a wewnątrz on onStartJob() uruchomić wątek sieciowy, a następnie po prostu wysłać zadanie.

Więcej szczegółów

https://developer.android.com/reference/android/app/job/JobService.html

0

Należy użyć AlarmManager.RTC_WAKEUP, nie AlarmManager.ELAPSED_REALTIME_WAKEUP obudzić urządzenie i należy skorzystać z usługi, aby wykonywać swoją pracę rozpoczął w odebrane w onReceive z startWakefulService(context, service.class). Zapewni to, że urządzenie całkowicie się obudzi i wykona połączenie sieciowe bez limitu czasu.

alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), interval, pendingIntent); 
2

Od https://developer.android.com/reference/android/content/BroadcastReceiver.html

Zgodnie z ogólną zasadą, nadawanych odbiorniki mogą trwać maksymalnie 10 sekund przed System rozważy im odpowiadać i ANR aplikacja. Ponieważ zazwyczaj są one wykonywane w głównym wątku aplikacji, są one już związane z ~ 5 sekundowym limitem różnych operacji , które mogą się tam zdarzyć (nie wspominając już o uniknięciu jotu interfejsu użytkownika), więc limit odbioru zasadniczo nie jest problemem . Jednak po użyciu {@goAsync}, chociaż może być wyłączony z głównego wątku, nadal obowiązuje limit wykonania transmisji , który obejmuje czas spędzony przed wywołaniem tej metody i ostatecznie PendingResult.finish().

Dalsze czytanie mówi

Jeśli korzystając z tej metody, aby mieć więcej czasu na wykonać, warto wiedzieć, że dostępny czas może być dłuższy w pewnych sytuacjach. W szczególności, jeśli transmisja, którą otrzymujesz, nie jest transmisją na pierwszym planie (to znaczy, że nadawca nie użył FLAG_RECEIVER_FOREGROUND), wtedy więcej czasu jest dozwolone dla odbiorników do uruchomienia, pozwalając im na wykonanie przez 30 sekund lub nawet nieco więcej.

(długa praca powinna być punted do innego obiektu systemu, takich jak JobScheduler, Service lub zobaczyć zwłaszcza JobIntentService)

Można spróbować użyć @goAsync. Lub możesz przełączyć swoją logikę na JobIntentService

Nie przetestowałem żadnego z nich.

1

Od docs deweloperskich: https://developer.android.com/reference/android/app/AlarmManager.html

Menedżer Alarm posiada blokadę procesora budzenia tak długo, jak metoda alarm odbiornika onReceive() jest wykonywany. To gwarantuje, że telefon nie zostanie uśpiony, dopóki nie zakończysz obsługi transmisji. Po powrocie onReceive(), Menedżer alarmów zwalnia tę blokadę wybudzania. Oznacza to, że telefon będzie w niektórych przypadkach spać jak najszybciej metody swojej onReceive() kończy

w kodzie onReceive powróci przed mApi.myPostRequest(myBody).enqueue ... zadanie zostanie wykonane, to zadanie będzie prawdopodobnie nigdy wykonywane należnego Procesor zatrzyma się, gdy tylko zostanie przywrócona funkcjaReceive.

Mówiłeś przetestowany nabycia blokada wybudzenia, ale nowy tryb Android 6.0 Doze ignoruje wakelocks

Wydaje się, że OnReceive będą musieli poczekać do tego zadania do końca

kilka pomysłów:

Sprawdzanie niektóre kończą flagę w pętli z thread.sleep?

Jeśli zadanie używa obiektu Thread, a następnie używa thread.join()?

0

Jeśli problem jest spowodowany przez drzemać i Wakelocks ignorowane, należy spróbować porady oferowane w https://developer.android.com/training/monitoring-device-state/doze-standby.html:

Standardowy AlarmManager alarmów (w tym setExact() i setWindow()) jest> odroczone do następnego okno konserwacji.

  • Jeśli trzeba ustawić alarmy ognia, podczas gdy w drzemać, użyj setAndAllowWhileIdle()> lub setExactAndAllowWhileIdle().
  • Alarmy ustawione za pomocą setAlarmClock() kontynuują normalne uruchamianie - system wychodzi z funkcji Drzemki na krótko przed uruchomieniem tych alarmów.
0

są 2 problemy w implementacji:

1) jeżeli odbiorca przekazu nie zostanie zakończone w ciągu 10 sekund, wykonując następnie ANR stanie. 2) Wszystkie połączenia sieciowe są zoptymalizowane w tle, więc gdy urządzenie jest wybudzane, może działać inaczej, nie wystrzeli żądania HTTP w celu oszczędzania baterii i innych zasobów.

co powinieneś zrobić, to w środku usługi stworzyć pętlę (z czasem uśpienia kilku sekund), która sprawdza czas w każdej iteracji i kiedy ten czas zostanie osiągnięty, a następnie wykonać zadanie, również napotkałem takie problemy, gdy próbowałem wgrać pliki do serwer w odstępie 1 godziny, więc postanowiłem działać na własną rękę, zamiast przy użyciu klasy AlarmManager ...

mam nadzieję, że pomoże ...