Fix not working scheduled reconnect

Documentation for postDelayed:
> Causes the Runnable r to be added to the message queue, to be run
> after the specified amount of time elapses.
> The runnable will be run on the thread to which this handler
> is attached.
> <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
> Time spent in deep sleep will add an additional delay to execution.

TL;DR: if the CPU is in deep sleep, the postDelayed runnable won't be executed.
This commit is contained in:
Jannis Mattheis
2020-05-30 19:59:28 +02:00
parent fe9e431a2b
commit 91dfd881e1
2 changed files with 36 additions and 14 deletions

View File

@@ -1,7 +1,9 @@
package com.github.gotify.service; package com.github.gotify.service;
import android.app.AlarmManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import com.github.gotify.SSLSettings; import com.github.gotify.SSLSettings;
import com.github.gotify.Utils; import com.github.gotify.Utils;
@@ -9,6 +11,7 @@ import com.github.gotify.api.Callback;
import com.github.gotify.api.CertUtils; import com.github.gotify.api.CertUtils;
import com.github.gotify.client.model.Message; import com.github.gotify.client.model.Message;
import com.github.gotify.log.Log; import com.github.gotify.log.Log;
import java.util.Calendar;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@@ -17,8 +20,9 @@ import okhttp3.Response;
import okhttp3.WebSocket; import okhttp3.WebSocket;
import okhttp3.WebSocketListener; import okhttp3.WebSocketListener;
public class WebSocketConnection { class WebSocketConnection {
private final ConnectivityManager connectivityManager; private final ConnectivityManager connectivityManager;
private final AlarmManager alarmManager;
private OkHttpClient client; private OkHttpClient client;
private final Handler reconnectHandler = new Handler(); private final Handler reconnectHandler = new Handler();
@@ -41,8 +45,10 @@ public class WebSocketConnection {
String baseUrl, String baseUrl,
SSLSettings settings, SSLSettings settings,
String token, String token,
ConnectivityManager connectivityManager) { ConnectivityManager connectivityManager,
AlarmManager alarmManager) {
this.connectivityManager = connectivityManager; this.connectivityManager = connectivityManager;
this.alarmManager = alarmManager;
OkHttpClient.Builder builder = OkHttpClient.Builder builder =
new OkHttpClient.Builder() new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) .readTimeout(0, TimeUnit.MILLISECONDS)
@@ -119,14 +125,25 @@ public class WebSocketConnection {
} }
} }
public synchronized void scheduleReconnect(long millis) { public synchronized void scheduleReconnect(long seconds) {
reconnectHandler.removeCallbacks(reconnectCallback); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.i(
Log.i( "WebSocket: scheduling a restart in "
"WebSocket: scheduling a restart in " + seconds
+ TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS) + " second(s) (via alarm manager)");
+ " second(s)"); final Calendar future = Calendar.getInstance();
reconnectHandler.postDelayed(reconnectCallback, millis); future.add(Calendar.SECOND, (int) seconds);
alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
future.getTimeInMillis(),
"reconnect-tag",
this::start,
null);
} else {
Log.i("WebSocket: scheduling a restart in " + seconds + " second(s)");
reconnectHandler.removeCallbacks(reconnectCallback);
reconnectHandler.postDelayed(reconnectCallback, TimeUnit.SECONDS.toMillis(seconds));
}
} }
private class Listener extends WebSocketListener { private class Listener extends WebSocketListener {
@@ -191,7 +208,7 @@ public class WebSocketConnection {
int minutes = Math.min(errorCount * 2 - 1, 20); int minutes = Math.min(errorCount * 2 - 1, 20);
onNetworkFailure.execute(minutes); onNetworkFailure.execute(minutes);
scheduleReconnect(TimeUnit.MINUTES.toMillis(minutes)); scheduleReconnect(TimeUnit.MINUTES.toSeconds(minutes));
} }
super.onFailure(webSocket, t, response); super.onFailure(webSocket, t, response);

View File

@@ -1,5 +1,6 @@
package com.github.gotify.service; package com.github.gotify.service;
import android.app.AlarmManager;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@@ -30,7 +31,6 @@ import com.github.gotify.messages.Extras;
import com.github.gotify.messages.MessagesActivity; import com.github.gotify.messages.MessagesActivity;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class WebSocketService extends Service { public class WebSocketService extends Service {
@@ -92,10 +92,15 @@ public class WebSocketService extends Service {
ConnectivityManager cm = ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
connection = connection =
new WebSocketConnection( new WebSocketConnection(
settings.url(), settings.sslSettings(), settings.token(), cm) settings.url(),
settings.sslSettings(),
settings.token(),
cm,
alarmManager)
.onOpen(this::onOpen) .onOpen(this::onOpen)
.onClose(() -> foreground(getString(R.string.websocket_closed))) .onClose(() -> foreground(getString(R.string.websocket_closed)))
.onBadRequest(this::onBadRequest) .onBadRequest(this::onBadRequest)
@@ -121,7 +126,7 @@ public class WebSocketService extends Service {
return; return;
} }
connection.scheduleReconnect(TimeUnit.SECONDS.toMillis(5)); connection.scheduleReconnect(5);
} }
private void onBadRequest(String message) { private void onBadRequest(String message) {