Skip to content
This repository has been archived by the owner on Mar 1, 2023. It is now read-only.
Magnus Ernstsson edited this page Mar 3, 2016 · 19 revisions

Agera (Swedish for “to act”) is a super lightweight Android library that helps prepare data for consumption by the Android application components (such as Activities), or objects therein (such as Views), that have life-cycles in one form or another. It introduces a flavor of functional reactive programming, facilitates clear separation of the when, where and what factors of a data processing flow, and enables describing such a complex and asynchronous flow with a single expression, in near natural language.

Below is an sample demonstrating some of the features of Agera. This wiki together with the javadoc, explains how each part of Agera works.

public final class MainActivity extends Activity implements Updatable, Receiver<Bitmap> {
  public static final String IMAGE_URL =
    "http://www.gravatar.com/avatar/4df6f4fe5976df17deeea19443d4429d?s=";
  private Repository<Result<Bitmap>> backgroundRepository;
  private ExecutorService networkExecutor;
  private ExecutorService decodeExecutor;
  private ImageView imageView;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    imageView = (ImageView) findViewById(R.id.background);

    networkExecutor = newSingleThreadExecutor();
    decodeExecutor = newSingleThreadExecutor();

    TimerObservable timerObservable = new TimerObservable(2000);

    Repository<Integer> sizeRepository = repositoryWithInitialValue(0)
        .observe(timerObservable)
        .onUpdatesPerLoop()
        .thenTransform(new Function<Integer, Integer>() {
          @NonNull
          @Override
          public Integer apply(@NonNull final Integer integer) {
            return (integer + 100) % 500;
          }
        }).compile();

    backgroundRepository = repositoryWithInitialValue(Result.<Bitmap>absent())
        .observe(sizeRepository)
        .onUpdatesPerLoop()
        .getFrom(sizeRepository)
        .transform(new Function<Integer, HttpRequest>() {
          @NonNull
          @Override
          public HttpRequest apply(@NonNull final Integer size) {
            return httpGetRequest(IMAGE_URL + size).compile();
          }
        })
        .goTo(networkExecutor)
        .attemptTransform(httpFunction()).orSkip()
        .goTo(decodeExecutor)
        .thenTransform(new Function<HttpResponse, Result<Bitmap>>() {
          @NonNull
          @Override
          public Result<Bitmap> apply(@NonNull final HttpResponse response) {
            final byte[] body = response.getBody();
            return absentIfNull(decodeByteArray(body, 0, body.length));
          }
        })
        .onDeactivation(CANCEL_FLOW)
        .compile();
  }

  @Override
  public void update() {
    backgroundRepository.get().ifSucceededSendTo(this);
  }

  @Override
  public void accept(@NonNull final Bitmap background) {
    imageView.setImageBitmap(background);
  }

  @Override
  protected void onResume() {
    super.onResume();
    backgroundRepository.addUpdatable(this);
  }

  @Override
  protected void onPause() {
    super.onPause();
    backgroundRepository.removeUpdatable(this);
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    networkExecutor.shutdown();
    decodeExecutor.shutdown();
  }

  private static final class TimerObservable extends BaseObservable {
    private final int period;
    private Timer timer;

    TimerObservable(final int period) {
      this.period = period;
    }

    @Override
    protected void observableActivated() {
      timer = new Timer();
      timer.schedule(new TimerTask() {
        @Override
        public void run() {
          dispatchUpdate();
        }
      }, 0, period);
    }

    @Override
    protected void observableDeactivated() {
      timer.cancel();
    }
  }
}