Fixed issue with registering observers multiple times (#605)

Registering observers would cause the runnables to be
posted multiple times to the checkerHandler. This resulted
in callbacks being called more than once when a result was
received.
This commit is contained in:
Martijn Brekhof 2019-02-07 17:39:56 +01:00 committed by Synced Synapse
parent 0c68ad1610
commit e60df7e8ac
1 changed files with 52 additions and 48 deletions

View File

@ -23,8 +23,8 @@ import org.xbmc.kore.jsonrpc.HostConnection;
import org.xbmc.kore.jsonrpc.method.JSONRPC; import org.xbmc.kore.jsonrpc.method.JSONRPC;
import org.xbmc.kore.jsonrpc.method.Player; import org.xbmc.kore.jsonrpc.method.Player;
import org.xbmc.kore.jsonrpc.notification.Application; import org.xbmc.kore.jsonrpc.notification.Application;
import org.xbmc.kore.jsonrpc.notification.Player.NotificationsData;
import org.xbmc.kore.jsonrpc.notification.Input; import org.xbmc.kore.jsonrpc.notification.Input;
import org.xbmc.kore.jsonrpc.notification.Player.NotificationsData;
import org.xbmc.kore.jsonrpc.notification.System; import org.xbmc.kore.jsonrpc.notification.System;
import org.xbmc.kore.jsonrpc.type.ApplicationType; import org.xbmc.kore.jsonrpc.type.ApplicationType;
import org.xbmc.kore.jsonrpc.type.ListType; import org.xbmc.kore.jsonrpc.type.ListType;
@ -52,6 +52,9 @@ public class HostConnectionObserver
HostConnection.ApplicationNotificationsObserver { HostConnection.ApplicationNotificationsObserver {
public static final String TAG = LogUtils.makeLogTag(HostConnectionObserver.class); public static final String TAG = LogUtils.makeLogTag(HostConnectionObserver.class);
/**
* Interface that an observer has to implement to receive playlist events
*/
public interface ApplicationEventsObserver { public interface ApplicationEventsObserver {
/** /**
* Notifies the observer that volume has changed * Notifies the observer that volume has changed
@ -148,55 +151,57 @@ public class HostConnectionObserver
// Associate the Handler with the UI thread // Associate the Handler with the UI thread
private Handler checkerHandler = new Handler(Looper.getMainLooper()); private Handler checkerHandler = new Handler(Looper.getMainLooper());
private Runnable httpPlayerCheckerRunnable = new Runnable() { private Runnable httpCheckerRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
final int HTTP_NOTIFICATION_CHECK_INTERVAL = 3000; final int HTTP_NOTIFICATION_CHECK_INTERVAL = 3000;
// If no one is listening to this, just exit boolean keepChecking = false;
if (playerEventsObservers.isEmpty()) return; if ( ! playerEventsObservers.isEmpty() ) {
keepChecking = true;
checkWhatsPlaying();
}
// Check whats playing if ( ! applicationEventsObservers.isEmpty() ) {
checkWhatsPlaying(); keepChecking = true;
getApplicationProperties();
}
// Keep checking if (keepChecking)
checkerHandler.postDelayed(this, HTTP_NOTIFICATION_CHECK_INTERVAL); checkerHandler.postDelayed(this, HTTP_NOTIFICATION_CHECK_INTERVAL);
}
};
private Runnable httpApplicationCheckerRunnable = new Runnable() {
@Override
public void run() {
final int HTTP_NOTIFICATION_CHECK_INTERVAL = 3000;
// If no one is listening to this, just exit
if (applicationEventsObservers.isEmpty()) return;
getApplicationProperties();
// Keep checking
checkerHandler.postDelayed(this, HTTP_NOTIFICATION_CHECK_INTERVAL);
} }
}; };
private Runnable tcpCheckerRunnable = new Runnable() { private Runnable tcpCheckerRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
// If no one is listening to this, just exit
if (playerEventsObservers.isEmpty() && applicationEventsObservers.isEmpty())
return;
final int PING_AFTER_ERROR_CHECK_INTERVAL = 2000, final int PING_AFTER_ERROR_CHECK_INTERVAL = 2000,
PING_AFTER_SUCCESS_CHECK_INTERVAL = 10000; PING_AFTER_SUCCESS_CHECK_INTERVAL = 10000;
// If no one is listening to this, just exit
if (playerEventsObservers.isEmpty() && applicationEventsObservers.isEmpty()) return;
JSONRPC.Ping ping = new JSONRPC.Ping(); JSONRPC.Ping ping = new JSONRPC.Ping();
ping.execute(connection, new ApiCallback<String>() { ping.execute(connection, new ApiCallback<String>() {
@Override @Override
public void onSuccess(String result) { public void onSuccess(String result) {
boolean keepChecking = false;
// Ok, we've got a ping, if there are playerEventsObservers and // Ok, we've got a ping, if there are playerEventsObservers and
// we were in a error or uninitialized state, update // we were in a error or uninitialized state, update
if ((! playerEventsObservers.isEmpty()) && if ((! playerEventsObservers.isEmpty()) &&
((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) || ((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) { (hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) {
checkWhatsPlaying(); checkWhatsPlaying();
keepChecking = true;
} }
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
if ( ! applicationEventsObservers.isEmpty() ) {
getApplicationProperties();
keepChecking = true;
}
if (keepChecking)
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
} }
@Override @Override
@ -206,12 +211,6 @@ public class HostConnectionObserver
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_ERROR_CHECK_INTERVAL); checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_ERROR_CHECK_INTERVAL);
} }
}, checkerHandler); }, checkerHandler);
// if ((lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
// (lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR)) {
// checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_ERROR_CHECK_INTERVAL);
// } else {
// checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
// }
} }
}; };
@ -251,7 +250,8 @@ public class HostConnectionObserver
if (this.connection == null) if (this.connection == null)
return; return;
playerEventsObservers.add(observer); if ( ! playerEventsObservers.contains(observer) )
playerEventsObservers.add(observer);
if (replyImmediately) replyWithLastResult(observer); if (replyImmediately) replyWithLastResult(observer);
@ -262,12 +262,10 @@ public class HostConnectionObserver
connection.registerPlayerNotificationsObserver(this, checkerHandler); connection.registerPlayerNotificationsObserver(this, checkerHandler);
connection.registerSystemNotificationsObserver(this, checkerHandler); connection.registerSystemNotificationsObserver(this, checkerHandler);
connection.registerInputNotificationsObserver(this, checkerHandler); connection.registerInputNotificationsObserver(this, checkerHandler);
// Start the ping checker
checkerHandler.post(tcpCheckerRunnable);
} else {
checkerHandler.post(httpPlayerCheckerRunnable);
} }
} }
startCheckerHandler();
} }
/** /**
@ -289,9 +287,6 @@ public class HostConnectionObserver
connection.unregisterPlayerNotificationsObserver(this); connection.unregisterPlayerNotificationsObserver(this);
connection.unregisterSystemNotificationsObserver(this); connection.unregisterSystemNotificationsObserver(this);
connection.unregisterInputNotificationsObserver(this); connection.unregisterInputNotificationsObserver(this);
checkerHandler.removeCallbacks(tcpCheckerRunnable);
} else {
checkerHandler.removeCallbacks(httpPlayerCheckerRunnable);
} }
hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT; hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
} }
@ -306,7 +301,8 @@ public class HostConnectionObserver
if (this.connection == null) if (this.connection == null)
return; return;
applicationEventsObservers.add(observer); if (! applicationEventsObservers.contains(observer) )
applicationEventsObservers.add(observer);
if (replyImmediately) { if (replyImmediately) {
if( hostState.volumeLevel == -1 ) { if( hostState.volumeLevel == -1 ) {
@ -321,11 +317,10 @@ public class HostConnectionObserver
// as a connection observer, which we will pass to the "real" observer // as a connection observer, which we will pass to the "real" observer
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) { if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
connection.registerApplicationNotificationsObserver(this, checkerHandler); connection.registerApplicationNotificationsObserver(this, checkerHandler);
checkerHandler.post(tcpCheckerRunnable);
} else {
checkerHandler.post(httpApplicationCheckerRunnable);
} }
} }
startCheckerHandler();
} }
/** /**
@ -344,8 +339,6 @@ public class HostConnectionObserver
// the http checker thread // the http checker thread
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) { if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
connection.unregisterApplicationNotificationsObserver(this); connection.unregisterApplicationNotificationsObserver(this);
} else {
checkerHandler.removeCallbacks(httpApplicationCheckerRunnable);
} }
} }
} }
@ -363,9 +356,8 @@ public class HostConnectionObserver
connection.unregisterPlayerNotificationsObserver(this); connection.unregisterPlayerNotificationsObserver(this);
connection.unregisterSystemNotificationsObserver(this); connection.unregisterSystemNotificationsObserver(this);
connection.unregisterInputNotificationsObserver(this); connection.unregisterInputNotificationsObserver(this);
connection.unregisterApplicationNotificationsObserver(this);
checkerHandler.removeCallbacks(tcpCheckerRunnable); checkerHandler.removeCallbacks(tcpCheckerRunnable);
} else {
checkerHandler.removeCallbacks(httpPlayerCheckerRunnable);
} }
hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT; hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
} }
@ -456,6 +448,18 @@ public class HostConnectionObserver
} }
} }
private void startCheckerHandler() {
// Check if checkerHandler is already running, to prevent multiple runnables to be posted
// when multiple observers are registered.
if ( checkerHandler.hasMessages(0) )
return;
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
checkerHandler.post(tcpCheckerRunnable);
} else {
checkerHandler.post(httpCheckerRunnable);
}
}
private void getApplicationProperties() { private void getApplicationProperties() {
org.xbmc.kore.jsonrpc.method.Application.GetProperties getProperties = org.xbmc.kore.jsonrpc.method.Application.GetProperties getProperties =
new org.xbmc.kore.jsonrpc.method.Application.GetProperties(org.xbmc.kore.jsonrpc.method.Application.GetProperties.VOLUME, new org.xbmc.kore.jsonrpc.method.Application.GetProperties(org.xbmc.kore.jsonrpc.method.Application.GetProperties.VOLUME,