/android-deferred

Primary LanguageJavaApache License 2.0Apache-2.0

Android-Deferred


Deferred is a fork of Ray Tsang's JDeferred library inspired in JQuery

The purpose of the library is to provide an easy-to-use implementation of promised for Android applications with an architecture based on pools. In Tuenti we have an architecture where the developer is the responsible of known in which thread pool should his code be executed, in a similar way to RxJava, we distinguish between network, disk, computation or UI work, and we wanted a Promise implementation that let us work in that way easily.


How to use

Features

  • Deferred object and Promise
  • Promise callbacks
    • .then(…)
    • .done(…)
    • .fail(…)
    • .progress(…)
    • .always(…)
  • Multiple promises
    • .when(p1, p2, p3, …).then(…)
  • Callable and Runnable wrappers
    • .when(new Runnable() {…})
  • Uses Executor Service
  • Java Generics support
    • Deferred<Integer, Exception, Double> deferred;
    • deferred.resolve(10);
    • deferred.reject(new Exception());
    • deferred.notify(0.80);

Promises and Deferred objects

How to create them
Deferred<Integer, Exception, Void> deferred = deferredFactory.build();

deferred.resolve(1);

Promise<Integer, Exception, Void> promise = deferred.promise();

promise.done((Done.Immediately<Integer>) i -> System.out.println(i));

As you can see, Done.Immediately<Integer> is the type of Done callback that we want to use, for the callbacks, you can use:

  • Immediately: To be run in the same thread where the promise has been resolved.
  • UI: To run it on UI thread.
  • Computation: To run it on the computation pool.
  • Disk: To run it on disk pool.
  • Network: To run it on network pool.
They are asynchronous

The promises are asynchronous, the callbacks can be set before/after the promise is finished

Deferred<String, Exception, Void> deferred = deferredFactory.build();

deferred.done((Done.UI<String>) result -> System.out.println(result));

deferred.resolve("Finish!");
Filter
Deferred<Integer, Void, Void> deferred = deferredFactory.build();

deferred.resolve(1);

deferred.then((Done.Filter.Computation<Integer, Integer>) i -> i * 2)
    .done((Done.UI<Integer>) i -> System.out.println(i));
Pipe
Deferred<Integer, Void, Void> deferred = deferredFactory.build();

deferred.resolve(1);

deferred.then((Computation<Integer, Integer, Exception, Void>) i ->
    deferredFactory.<Integer, Exception, Void>build().reject(new Exception()))
    .done((UI<Integer>) i -> System.out.println(i))
    .fail((Fail.UI<Exception>) e -> System.out.println("Uh oh!"));
All this can be combined together
Deferred<Integer, Void, Void> deferred = deferredFactory.build();

deferred.resolve(1);

deferred.then((Done.Pipe.Computation<Integer, Integer, Exception, Void>) i -> {
  Deferred<Integer, Exception, Void> positiveDeferred = deferredFactory.build();
  positiveDeferred.resolveOrReject(i > 0, i, new Exception("Noooo"));
  return positiveDeferred;
})
    .then((Done.Filter.Computation<Integer, String>) i -> "Result is " + String.valueOf(i))
    .done((Done.UI<String>) i -> System.out.println(i))
    .fail((Fail.UI<Exception>) e -> System.out.println(e.getMessage()));
}
DeferredManager:

You can use a DeferredManager implementation, DefaultDeferredManager or your custom one to combine the execution of several promises

for example:

DeferredManager dm = new DefaultDeferredManager();
Promise p1, p2, p3;
// initialize p1, p2, p3
dm.when(p1, p2, p3)
  .done(…)
  .fail(…)

Or use sequentiallyRunUntilFirstDone to execute sequentially the promises until one is resolved:

For this you must instantiate some objects implementing PromiseObjectTask<D, F, P>

for example:

private PromiseObjectTask<Data, Exception, Void> getDataFromMemory() {
	return new PromiseObjectTask<Data, Exception, Void>() {
		@Override
		public Promise<Data, Exception, Void> run() {
			return inMemoryStorage.getData();
		}
	};
}

private PromiseObjectTask<Data, Exception, Void> getDataFromDisk() {
	return new PromiseObjectTask<Data, Exception, Void>() {
		@Override
		public Promise<Data, Exception, Void> run() {
			return diskStorage.getData();
		}
	};
}

private PromiseObjectTask<Data, Exception, Void> getDataFromAPI() {
	return new PromiseObjectTask<Data, Exception, Void>() {
		@Override
		public Promise<Data, Exception, Void> run() {
			return apiClientStorage.getData();
		}
	};
}

deferredManager.sequentiallyRunUntilFirstDone(getDataFromMemory(),
				getDataFromDisk(),
				getDataFromAPI());

or avoiding verbosity using lambdas or method references:

deferredManager.sequentiallyRunUntilFirstDone(inMemoryStorage::getData,
				diskStorage::getData,
				apiClient::getData);

Also, you can use lazyAnd or lazyOr to execute promises sequentially until the boolean condition is achieved.


How to add to your project

The promises are created via a DeferredFactory, to create an instance of this class you should provide an implementation of ExecutorProvider, this class will is where you will tell Deferred in which pool you want to execute your code, for example:

import java.util.concurrent.Executor;
import com.tuenti.deferred.ExecutorProvider;

public class MyExecutorProvider implements ExecutorProvider {

	private final Executor myExecutor = new Executor() {
		@Override
		public void execute(Runnable command) {
			command.run();
		}
	};

	@Override
	public Executor getUiExecutor() {
		return myExecutor;
	}

	@Override
	public Executor getComputationExecutor() {
		return myExecutor;
	}

	@Override
	public Executor getDiskExecutor() {
		return myExecutor;
	}

	@Override
	public Executor getNetworkExecutor() {
		return myExecutor;
	}
}