/* * Copyright 2016 Kejun Xia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.shipdream.lib.android.mvc; import java.util.concurrent.Future; /** * Task to execute a block of code */ public interface Task<RESULT> { /** * Monitor is used to track and cancel the task */ class Monitor<RESULT> { /** * State of executing phases of {@link Task} */ public enum State { /** * The {@link Monitor} hasn't started */ NOT_STARTED, /** * The {@link Monitor} has started. After this state and before {@link #DONE}, {@link #ERRED}, * {@link #CANCELED} or {@link #INTERRUPTED} the task is in running state. */ STARTED, /** * The {@link Monitor} has finished successfully */ DONE, /** * The {@link Monitor} failed to finish due to errors */ ERRED, /** * The {@link Monitor} is cancelled before it starts */ CANCELED, /** * The {@link Monitor} is cancelled after it starts and before finishes */ INTERRUPTED } private final Callback<RESULT> callback; private final UiThreadRunner uiThreadRunner; private Future future; private State state; private Task<RESULT> task; /** * Constructor * @param task The task being monitored * @param uiThreadRunner The runner runs runnable on ui thread * @param callback The callback for the execution of the task. It can be null. * @param uiThreadRunner */ public Monitor(Task<RESULT> task, UiThreadRunner uiThreadRunner, Callback<RESULT> callback) { this.uiThreadRunner = uiThreadRunner; this.state = State.NOT_STARTED; this.task = task; this.callback = callback; } synchronized void setState(State state) { this.state = state; } synchronized void setFuture(Future future) { this.future = future; } /** * Gets the state of this {@link Task} * @return */ public synchronized State getState() { return state; } /** * Attempts to cancel execution of this task. This attempt will * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when <tt>cancel</tt> is called, * this task should never run. If the task has already started, * then the <tt>mayInterruptIfRunning</tt> parameter determines * whether the thread executing this task should be interrupted in * an attempt to stop the task. * * <p> * If this method is called when {@link #getState()} is {@link State#NOT_STARTED} * subsequent calls to {@link #getState()} will always return <tt>{@link State#CANCELED}</tt>. * </p> * * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete * @return <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally; * <tt>true</tt> otherwise */ public synchronized boolean cancel(final boolean mayInterruptIfRunning) { switch (state) { case NOT_STARTED: state = State.CANCELED; if (callback != null) { callback.onCancelled(false); callback.onFinally(); } return true; case STARTED: boolean cancelled = future.cancel(mayInterruptIfRunning); if (cancelled) { if (mayInterruptIfRunning) { state = State.INTERRUPTED; } else { state = State.CANCELED; } } if (callback != null) { uiThreadRunner.post(new Runnable() { @Override public void run() { callback.onCancelled(mayInterruptIfRunning); callback.onFinally(); } }); } return cancelled; case DONE: case ERRED: case CANCELED: case INTERRUPTED: default: return false; } } } /** * The callback for the execution of a {@link Task} */ abstract class Callback<RESULT> { /** * Called when the execution of the task starts * @deprecated */ public void onStarted() {} /** * Called when the execution of the task starts * @param monitor The monitor to watch this task. The task can be cancelled by {@link Monitor#cancel(boolean)} */ public void onStarted(Monitor monitor) {} /** * Called when the execution of the task completes successfully * @param result The result of the execution */ public void onSuccess(RESULT result){} /** * Called when the execution of the task is cancelled * @param interrupted true when the task is cancelled while the task is running. * false when the task is cancelled before it starts running */ public void onCancelled(boolean interrupted){} /** * Called when the execution of the task encounters exceptions. Note that an * {@link InterruptedException} caused by cancelling won't trigger this callback but * {@link #onCancelled(boolean)} with argument equals true * @param e The exception to handle * @throws Exception The exception by default will be thrown out otherwise override this * method to handle it and <b>DO NOT call super.onException(e)</b> */ public void onException(Exception e) throws Exception { throw e; } /** * Called when the task has started and runs into {@link #onSuccess(Object)} ()}, * {@link #onCancelled(boolean)} or {@link #onException(Exception)} */ public void onFinally() {} } /** * Override this method to define what this task does. * @param monitor The monitor to track the execution of this task * @throws Exception exception occurring during the execution */ RESULT execute(Monitor<RESULT> monitor) throws Exception; }