awex


Source link: https://github.com/raycoarana/awex

awex

AWEX (Android Work EXecutor) is a thread pool to execute tasks that uses Promises to deliver results. Promises that can be cancelled, can be combined or even can process collections in parallel automatically.

How to use it?

First thing you need to do is setup an Awex object, that will be your thread pool. You could create some provider that will keep an instance to the Awex object implementing a Singleton pattern or you could implement it with your DI framework.

public class AwexProvider {

private static final int WORKER_PRIORITY = Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE;
  private static final int MAX_THREADS = 4;

private static Awex sInstance = null;

public static synchronized Awex get() {

if (sInstance == null) {

 sInstance = new Awex(new AndroidThreadHelper(),

  new AndroidLogger(),

  new LinearWithRealTimePriorityPolicy(WORKER_PRIORITY, MAX_THREADS));

}

return sInstance;
  
}
  
}

Once you have an Awex object, all you need to do to start is submit tasks. A Task is an object like the classic Runnable but with some extras. To start with a simple example, let's create a task, submit to the thread pool and do something when it finishes.

Awex awex = AwexProvider.get();
 awex.submit(new Task<Integer>() {

  @Override
  protected Integer run() throws InterruptedException {

//Do some heavy task here

 return 42; //Return some result
  
}
 
}
).done(new DoneCallback<Integer>() {

  @Override
  public void onDone(Integer result) {

Log.i("Awex", "Result to the task execution is: " + result);

  
}
 
}
);

If your task doesn't return anything, you could use VoidTask instead and have a cleaner code than having to use Task.

awex.submit(new VoidTask() {

  @Override
  protected void runWithoutResult() throws InterruptedException {

//Do some heavy task here
  
}
 
}
).done(new DoneCallback<Void>() {

  @Override
  public void onDone(Void result) {

Log.i("Awex", "Task finished successfully!");

  
}
 
}
);

Promises

Inspired by JDeferred library, Awex use Promises to let you add subtasks to your task on all possible scenarios, when it finishes correctly, when it fails or in any case.

awex.submit(someTask)
  .done(new DoneCallback<Integer>() {

@Override

public void onDone(Integer result) {

 //Task finishes correctly

}

  
}
)
  .fail(new FailCallback() {

@Override

public void onFail(Exception exception) {

 //Task fails

}

  
}
)
  .progress(new ProgressCallback() {

@Override

public void onProgress(float progress) {

 //Task done some progress

}

  
}
)
  .always(new AlwaysCallback() {

@Override

public void onAlways() {

 //Task either finishes correctly or fail

}

  
}
);

But if you are in Android, many times you want that some of that callbacks gets executed in the main thread to update the UI. For that purpose, Awex provide the same interfaces prefixed by UI. Every time an UI callback is added, its code is executed in the main thread.

awex.submit(someTask)
  .done(new UIDoneCallback<Integer>() {

@Override

public void onDone(Integer result) {

 view.setValue(result);
 //All it's ok, show the result to the user

}

  
}
)
  .fail(new UIFailCallback() {

@Override

public void onFail(Exception exception) {

 view.showError();
 //Can't get the value, show the error message

}

  
}
)
  .progress(new UIProgressCallback() {

@Override

public void onProgress(float progress) {

 view.updateProgress(progress);

}

  
}
)
  .always(new UIAlwaysCallback() {

@Override

public void onAlways() {

 view.hideLoading();
 //In any case, hide the loading animation

}

  
}
);

Waiting for results

You could get the result of the promise at any time, blocking your current thread until it's finished.

Promise<Integer> promise = awex.submit(...);
 try {

  Integer result = promise.getResult();
 
}
 catch (Exception ex) {

  //Task fails with exception ex 
}

But you can get a default value in case of error

Promise<Integer> promise = awex.submit(...);
 Integer result = promise.getResultOrDefault(42);
 //will return 42 when the task fails

Cancelling tasks through promises

A problem you could face when using promises on Android is that sometimes, when the user leaves some Activity or Fragment, any task that is running in the background to load information don't need to be completed and can be cancelled. Even more, you must cancel it and remove any reference to the UI, so you don't leak the Activity or Fragment. For that and many other cases, Awex lets you cancel tasks using the associated promise. Tasks can be interrupted or not when gets cancelled, tasks can query if they are already cancelled or not, so they can stop doing things once cancelled in a gracefully way. In any case, after a promise/task is cancelled, no done/fail/always callback will be executed.

public void onCreate() {

  ...

//Load data to be shown
  mPromise = awex.submit(new Task<Integer>() {

@Override

protected Integer run() throws InterruptedException {

 return 42;

}

  
}
).done(new DoneCallback<Integer>() {

@Override

public void onDone(Integer result) {

 //Task finishes correctly

}

  
}
);
 
}
  public void onDestroy() {

  //On destroy, ensure any pending task is cancelled
  mPromise.cancelTask();
 
}

Tasks and promises could also be cancelled interrupting the worker thread. In that case, the thread will be removed from the thread pool and interrupted. A new thread will be created in the thread pool as soon as a new task is submitted.

public void onCancelSave() {

  mPromise.cancelTask(/* mayInterrupt */true);
 //Tries to abort the worker thread 
}

You could even know when some task in cancelled and add a callback for that case. That will be the only callback that will be executed when a promise/task is cancelled. Remember to not reference any Activity/Fragment from a cancelled callback or it will be leaked!

mPromise = AwexProvider.get().submit(new Task<Integer>() {

  @Override
  protected Integer run() throws InterruptedException {

//Do some work...

 if (isCancelled()) return -1; //This result won't be processed never

 //Do more work...

 return 42;
  
}
 
}
).done(new DoneCallback<Integer>() {

  @Override
  public void onDone(Integer result) {

//Task finishes correctly
  
}
 
}
).cancel(new CancelCallback() {

  @Override
  public void onCancel() {

//Task cancelled...
  
}
 
}
);

Not just promises

There are many more things you can do with Awex and its promises.

OR operator

awex.submit(new Task<Integer>() {

  @Override
  protected Integer run() throws InterruptedException {

throw new RuntimeException("Some error");

  
}
 
}
) .or(awex.of(42)) .done(new DoneCallback<Integer>() {

  @Override
  public void onDone(Integer result) {

//This will be executed always with 42
  
}
 
}
);

Awex supports doing an OR of many promises using the anyOf() method in the Awex object.

AND operator

awex.submit(new Task<Integer>() {

  @Override
  protected Integer run() throws InterruptedException {

return 42;
  
}
 
}
) .and(awex.of(43)) .done(new DoneCallback<Collection<Integer>>() {

  @Override
  public void onDone(Collection<Integer> result) {

//result will contain 42 and 43
  
}
 
}
);

Awex supports doing an AND of many promises using the allOf() method in the Awex object.

AfterAll operator

awex.afterAll(awex.of(41), awex.of(42), awex.of(43))
  .done(new DoneCallback<MultipleResult<Integer>>() {

@Override

public void onDone(MultipleResult<Integer> result) {

 //MultipleResult contains all values or error of all promises

}

  
}
);

Filter operator

FilterSingle

Promise<Integer, Void> promise = awex.of(42);
  promise = promise.filterSingle(value -> value > 45);
  promise.getResult();
 //This will throw an exception, promise don't have any value

Filter all

Filter in parallel

Map operator

Mapsingle

MapSingle operator return a new promise with the result mapped using the method provided. For eaxmple in this case we see how we can map an Integer value of a promise to an String value.

Promise<Integer, Void> promise = mAwex.of(42);
  Promise<String, Void> mappedPromise = promise.mapSingle(String::valueOf);
  String value = mappedPromise.getResult();
 //Value will be "42"

Map all

Map in parallel

Foreach operator

Foreach

ForeachParallel

Then operator

Then operator receives the result of the promise and returns a new promise that will be resolved when the inner operation finishes. Imagine some cloud filesystem API that works completely async where we have a getFolder() and getFile() methods that returns Promises; with Awex could be used in this way:

private Promise<Journey, Void> loadJourney(String userId, String journeyId) {

  return getFolder(getRootFolder(), userId).then(userFolder -> getFolder(userFolder, journeyId))

 .then(journeyFolder -> getFile(journeyFolder, "journey.bin"))

 .mapSingle(journeyFile -> deserialize(journeyFile));
 
}

All async, no blocked threads, fluent syntax and no boilerplate.

Download

Download via Maven:

<dependency>
<groupId>com.raycoarana.awex</groupId>
<artifactId>awex-android</artifactId>
<version>0.0.2</version> </dependency>

or Gradle:

compile 'com.raycoarana.awex:awex-android:0.0.2'

Contributors

License

Copyright 2015 Rayco AraƱa  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. 

Resources

The DynamicPagerAdapter extends Android's PagerAdapter to do four important things:

  • Has an accessible HashMap View cache using ViewHolders. The default implementaiton has caching, but it isn't enforced and users of the PagerAdapter don't get access to it.

  • Provides the capability to use multiple view types just like RecyclerView.

  • Handles data set changes in a much more friendly way, allowing items to be removed, added, etc. with less issues and effort on your end.

  • Includes optional discard animations. These are exposed for you to call when you want or to override and create your own.

A customizable water gauge to display devices orientation.

An android library to display a rate dialog in an easy way.

AnimatedLoadingIndicator is a animated Progress Dialog.

SQLBrite helper class to manage database creation and version management using an application's raw asset files.

A simple and easy android library to create your steps in modern view.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes