package net.maxbraun.mirror; import android.os.Handler; import android.os.Looper; import android.util.Log; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * An abstract class that continuously queries a data source on a background thread via * {@link #getData()} and updates the {@link UpdateListener} on the main thread with the result. */ public abstract class DataUpdater<Data> { /** * The {@link ScheduledExecutorService} used to query data on a background thread. All requests * are handled sequentially. */ private final ScheduledExecutorService scheduledBackgroundExecutor = Executors.newSingleThreadScheduledExecutor(); /** * The current task on the {@link #scheduledBackgroundExecutor} or {@code null} if there is none. */ private ScheduledFuture updateTask; /** * A {@link Handler} on the main thread. */ private final Handler mainHandler = new Handler(Looper.getMainLooper()); /** * The {@link UpdateListener} called each time there is new data. */ private final UpdateListener<Data> updateListener; /** * The time in milliseconds between each update. */ private final long updateIntervalMillis; /** * The generic interface used for data updates. */ public interface UpdateListener<Data> { /** * Called when there is new data. * * @param data The latest {@link Data} or {@code null} if there was an error. */ void onUpdate(Data data); } /** * When creating a new {@link DataUpdater}, provide a non-{@code null} {@link UpdateListener} and * an update interval in milliseconds. */ public DataUpdater(UpdateListener<Data> updateListener, long updateIntervalMillis) { if (updateListener == null) { throw new IllegalArgumentException("A listener is required."); } this.updateListener = updateListener; this.updateIntervalMillis = updateIntervalMillis; } /** * Starts the regular background updates. */ public void start() { Log.d(getTag(), "Starting."); // Remember the task so we can cancel it later. updateTask = scheduledBackgroundExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { update(); } }, 0, updateIntervalMillis, TimeUnit.MILLISECONDS); } /** * Stops the regular background updates. */ public void stop() { Log.d(getTag(), "Stopping."); // If there is a running task, cancel any repetition while allowing the current one to finish. if (updateTask != null) { updateTask.cancel(false); updateTask = null; } } /** * Performs the update by retrieving the data and updating the listener. */ private void update() { Log.d(getTag(), "Updating."); final Data data = getData(); mainHandler.post(new Runnable() { @Override public void run() { updateListener.onUpdate(data); } }); } /** * Implement this to query the data source and return a {@link Data} instance or {@code null}. */ protected abstract Data getData(); /** * Implement this to provide a tag for logging. */ protected abstract String getTag(); }