Problemas… Output arguments are counterintuitive. Readers expect arguments to be inputs, not outputs. If your function must change the state of something, have it change the state of the object it is called on. Robert C. Martin
Patrón Promise. Características • Código más legible • Concurrencia más sencilla • Tests más sencillos • Sin ArgumentCaptors para capturar callbacks que luego estimular.
jDeferred • Implementación Java del patrón Promise • Inspirado en Deferred Object de jQuery • Soporte específico para Android • Código muy compacto con Lambdas en Java 8 • http://jdeferred.org
jDeferred. Claves • Promise : interface • Promesa que recibes al realizar una operación. • DeferredObject : class • Objeto con el que controlas el estado de la promesa • AndroidDeferredObject • DeferredManager : interface • Gestor de promesas • DefaultDeferredManager implementación por defecto • AndroidDefererdManager
• Creamos un DeferredObject • Realizamos la operación en background • Devolvemos la promesa jDeferred. Caso práctico II public Promise read(File jsonFile) { DeferredObject deferredObject = new DeferredObject<>(); doReadAsync(jsonFile, deferredObject); return deferredObject.promise(); }
• El trabajo lo dejamos para que lo ejecute un ThreadPool jDeferred. Caso práctico III private Executor threadPool = Executors.newSingleThreadExecutor(); //Whatever thread pool private void doReadAsync(File jsonFile, DeferredObject deferredObject) { threadPool.execute(new Runnable() { @Override public void run() { doTryRead(jsonFile, deferredObject); } }); }
• ¿En qué hilo se ejecuta el Listener? ¿UI o Background? • UI • Cosas en el hilo de UI que no queremos • Background • Cuando queremos hacer algo en UI tenemos que recurrir a Handlers • Problema subyacente • Responsabilidad recae en la API: Bad idea • Es el cliente de la API quien debe decidir esto Listeners en Android
• AndroidDeferredObject • Wrapper de DeferredObject para añadir soporte para Android • Nuevos callbacks • AndroidDoneCallback • AndroidFailCallback • AndroidProgressCallback • AndroidAlwaysCallback • Método para elegir en que hilo se va a ejecutar • A partir de 1.2.5, se puede indicar con una anotación jDeferred en Android
jDeferred en Android • Consejo: Crear clases abstractas que oculten el método public abstract class UIDoneCallback implements AndroidDoneCallback { public AndroidExecutionScope getExecutionScope() { return AndroidExecutionScope.UI; } } public abstract class BackgroundDoneCallback implements AndroidDoneCallback { public AndroidExecutionScope getExecutionScope() { return AndroidExecutionScope.BACKGROUND; } }
• CPUs con muchos cores • Algunos se apagan, pero hay un consumo mínimo • Usar todos de forma eficiente permite apagar la CPU antes • Un ThreadPool no es suficiente • Nos ayuda a lanzar trabajos en paralelo • ¿Cómo sincronizar trabajos dependientes entre si? • Herramientas de bajo nivel… complicadas de depurar. Paralelizar trabajo
Filters • Se puede aplicar a cualquier tipo de promesa fileManager.read(new File("config.json")) .then(new DoneFilter() { @Override public Config filterDone(JSONObject result) { return new Config(result); } }) .done(result -> view.showConfig(result)) .fail(ex -> view.showMessage("Config not loaded :-("));
jDeferred no es RxJava • Promesas tienen estado • No se puede reutilizar • Solo se puede llamar 1 vez a resolve() o reject() • A notify() si podemos llamar muchas veces • Para progreso, no para notificar cambios • Ni mejor ni peor, depende de nuestros requisitos.