Adel Nizamutdinov

I wanted to share this

Using RxJava Observable’s Completion Semantics for Greater Good

When I first started with RxJava, for me it was all about easy and parametric concurrency. Later I discovered the power of functional composition and real code reuse. But it never ceases to amaze me, so today I want to talk about Observable’s completion semantics and Subscriber.add(Subscription) method.

Unsubscription callback

Let’s look at the simple Observable implementation for OkHttp calls. Nothing fancy, we make a call, we pass the response to the stream and immediately end it:

1
2
3
4
5
6
7
8
9
10
11
Observable<Response> call(OkHttpClient client, Request request) {
  return Observable.create(subscriber -> {
    final Call call = client.newCall(request);
    try {
      subscriber.onNext(call.execute());
      subscriber.onCompleted();
    } catch (IOException e) {
      subscriber.onError(e);
    }
  });
}

This works, but sometimes we need to make a huge amount of HTTP calls and ignore some of the responses: for example, when adding autocompletion to the input field.

And guess what, RxJava works great for that kind of thing, we just need to add the unsubscription callback:

1
2
3
4
5
6
7
8
9
10
11
12
Observable<Response> call(OkHttpClient client, Request request) {
  return Observable.create(subscriber -> {
    final Call call = client.newCall(request);
    subscriber.add(Subscriptions.create(call::cancel));
    try {
      subscriber.onNext(call.execute());
      subscriber.onCompleted();
    } catch (IOException e) {
      subscriber.onError(e);
    }
  });
}

Now if we use Subscription.unsubscribe() or Observable.takeUntil(Observable) RxJava will call Call.cancel() reducing the unneeded network load. And we get that completely for free, thanks to Observable’s powerful semantics.

Long ongoing processes

Let’s move to the Android land and look at the MediaPlayer class. It’s one of the most “imperative” classes in the whole SDK with it’s implicit state, throwing Exceptions on almost every method and really weird error propagation. So how can we bring it over to the safe and predictable RX world?

Ok, so if we want to represent an ongoing process as an RX stream, we should first figure out what kinds of values it’s emitting. For MediaPlayer it’s the time of the playback – an Integer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Observable<Integer> stream(MediaPlayer mp) {
    return Observable.create(subscriber -> {
        subscriber.add(Subscriptions.create(() -> {
            if (mp.isPlaying()) {
                mp.stop();
            }
            mp.reset();
            mp.release();
        }));
        mp.start();
        subscriber.add(ticks(mp)
                               .takeUntil(complete(mp))
                               .subscribe(subscriber));
    });
}

Observable<Integer> ticks(MediaPlayer mp) {
    return Observable.interval(16, TimeUnit.MILLISECONDS)
            .map(y -> mp.getCurrentPosition());
}

Observable<MediaPlayer> complete(MediaPlayer player) {
    return Observable.create(subscriber -> player.setOnCompletionListener(mp -> {
        subscriber.onNext(mp);
        subscriber.onCompleted();
    }));
}

The main idea here is:

  1. A stream of ticks is a an interval observable mapped to the MediaPlayer’s current position, and it’s triggered 60 times per second.
  2. A complete stream emits just one item – the moment MediaPlayer stops playing.
  3. We start() the player, then emit the ticks until stopped or unsubscribed
  4. I’m adding a second Subscription so that ticking also stops on unsubscription, because I’m calling it as an external Observable
  5. The most important part is the first unsubscription callback, where the player gets released.

So now every time we subscribe to this stream, it starts playing audio and emitting the current playback position. If it stops – it releases the player. If we unsubscribe – it releases the player. If it errors – it releases the player.

You can think of asynchronous Subscriber.add(Subscription) as a finally block for a synchronous try-catch. We can represent any long-running task as a stream and get auto-cleanup and decent error-propagation for free. It could be anything: phone calls, http long-polling, audio recording. Hell, even UI animations!

This way any complex asynchronous long-running task may be reduced to a simple and composable Observable:

1
2
3
Observable<Pair<Integer, Integer>> play(MediaPlayer mp) {
    return prepare(mp).flatMap(RxMediaPlayer::stream);
}

Keep composing!

PS

Here’s the complete code for the RxMediaPlayer

And bonus code for the RxMediaRecorder :)

Please note that they’re not production-ready because they both lack OnErrorListeners for decent error propagation and recovery.

RxJava on Android: PopupMenus and Dialogs

This post is about how to treat PopupMenus and Dialogs as ordinary events, and being able to put them into your RxJava event pipelines. I’m assuming that you’re pretty comfortable with RxJava, so I won’t go into all the deepest details.

PopupMenus

PopupMenu

So the regular way to show a PopupMenu on a View is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
view.setOnClickListener(v -> {
  final PopupMenu menu = new PopupMenu(context, v);
  menu.inflate(R.menu.xxx);
  menu.setOnMenuItemClickListener(item -> {
    switch (item.getItemId()) {
      case R.id.xxx1:
        // do 1
        break;
      case R.id.xxx2:
        // do 2
        break;
      case R.id.xxx3:
        // do 3
        break;
    }
    return true;
  });
  menu.show();
});

But the menu click is an event – let’s represent it with an Observable<MenuItem> that will emit exactly one item – a menu item click:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Observable<MenuItem> popupMenu(View view, int menuRes) {
  return Observable.create((Subscriber<? super MenuItem> subscriber) -> {
    final PopupMenu menu = new PopupMenu(view.getContext(), view);
    menu.inflate(menuRes);

    // cleaning up in case of unsubscribe() call
    subscriber.add(Subscriptions.create(() -> {
      menu.setOnMenuItemClickListener(null);
      menu.dismiss();
    }));

    menu.setOnMenuItemClickListener(item -> {
      subscriber.onNext(item);
      // PopupMenu always emits exactly one item
      subscriber.onCompleted();
      return true;
    });
    menu.show();
  });
}

So what can we do with this? Compose-compose-compose!

1
2
3
4
5
6
7
8
9
10
11
12
ViewObservable.clicks(view)
    .flatMap(v -> popupMenu(v, R.menu.xxx))
    .subscribe(item -> {
      switch (item.getItemId()) {
        case R.id.xxx1:
          // do 1
          break;
        case R.id.xxx2:
          // do 2
          break;
      }
    });

We flatMap each View click into a PopupMenu item click, and get MenuItem at the end of our stream. We can even go further (and in fact we should, because MenuItem is never what we want in the end) and flatMap them into some async REST API call, or filter them by id. The possibilities are endless.

Dialogs

Dialog

Exactly the same story here: in the simpliest case Dialog is just an Observable<Boolean> where true stands for clicking on Accept and false for clicking on Decline

So this is the function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Observable<Boolean> dialog(Context context, int title, int message) {
  return Observable.create((Subscriber<? super Boolean> subscriber) -> {
    final AlertDialog ad = new AlertDialog.Builder(context)
        .setTitle(title)
        .setMessage(message)
        .setPositiveButton(android.R.string.ok, (dialog, which) -> {
          subscriber.onNext(true);
          subscriber.onCompleted();
        })
        .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
          subscriber.onNext(false);
          subscriber.onCompleted();
        })
        .create();
    // cleaning up
    subscriber.add(Subscriptions.create(ad::dismiss));
    ad.show();
  });
}

Now you can use it exactly the same way you use your ordinary Observable<>s:

1
2
3
4
5
6
7
clicks
  .flatMap(x -> dialog(context, title, message))
  .filter(x -> x == true) // only 'Accept' clicks
  .flatMap(x -> api.confirmSomething().subscribeOn(Schedulers.io()))
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(x -> // do stuff,
             e -> // recover);

Also remember, you can represent any of your custom Dialogs as any type of Observable<>. If you have some kind of Date picker, it can be Observable<Date>, if it’s a captcha Dialog, you can represent it as an Observable<String>. And this is when RxJava REALLY starts to shine.

Suppose we have some api call

1
2
3
4
5
6
7
8
9
10
11
Observable<Data> apiCall(Arg arg);

class Data {
  Result result;
  Error error;
}

class Error extends Throwable {
  boolean isCaptcha();
  String captchaImgUrl;
}

And a captcha dialog:

1
Observable<String> captchaDialog(String captchaImgUrl);

And we want to abstract away from Captchas, and never worry about them again, here’s what we can do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiClient.apiCall(arg)
  .flatMap(x -> x.result != null
             ? Observable.just(x.result)
             : x.error.isCaptcha()
               ? captchaDialog(x.error.captchaImgUrl)
                   // send the captcha
                   .flatMap(str -> apiClient.submitCaptcha(str))
                   // call the api method again
                   // this should be done recursively
                   // simplicity for the sake of blog post
                   .flatMap(success -> apiClient.apiCall(arg))
               : Observable.error(x.error))
  .subscribe(x -> // at this point we recovered from any captcha requests we encountered, and got our desired data,
             e -> // failure);

My favourite part about RxJava is that it allows to abstract over all this event-based and async failure situations that we can recover from, or maybe ask the user for assistance. And when you abstract from all this failure, programming becomes such a breeze.

Kotlin on Android

NOTE: this is not a tutorial, or a HOWTO for Kotlin on Android, those are my thoughts about the current state of the tooling around it

15 Feb 2015 UPDATE: Kotlin M10 contains fixes for my problems with traits, forcing !!s and varargs

I wanted to try it out for quite a while now, but never really got into writing anything with some real UI and infrastructure.

This weekend I finished this small app – Last.fm Love Button, which was supposed to be written in Kotlin, but turned out to be mostly Java.

I craved for a better language, but realized that Android (the framework) is so poorly designed, that the only thing that keeps it afloat is Android (the ecosystem) and the tooling that was built for it over the years.

Code generation libraries

Today most of my Android toolkit consists of the code generating libraries and plugins that reduce the ridiculous amount of boilerplate needed to get things done and overcome framework’s poor design choices (I need at least two libraries to inject dependencies to my Fragments – Dagger and FragmentArgs), the same goes for my Activities and Services and Views, since I can’t use a normal Java constructors.

But then again, I can overcame the lack of code generation libraries in Kotlin by just subclassing an annotated Java class:

1
2
3
4
5
6
7
8
9
public class LoginFragmentBase extends RxFragment {
  @Inject @NotNull Lazy<LastFm>                              lastFmLazy;
  @Inject @NotNull Lazy<Preferences>                         preferencesLazy;
  @Inject @NotNull Subject<Effect<Session>, Effect<Session>> subject;

  @InjectView(R.id.username) @NotNull        EditText               username;
  @InjectView(R.id.password) @NotNull        EditText               password;
  @InjectView(R.id.submit_progress) @NotNull CircularProgressButton submit;
}

Kotlin:

1
2
3
4
5
6
class LoginFragment : LoginFragmentBase() {
  override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    Dagger.inject(this)
    ButterKnife.inject(this, view)
  }
}

This feels a little bit like writing .h and .cpp files, no big deal right? You get the best of both worlds: modern language and code generation for Java.

Java interop

Kotlin (unlike Scala) uses the same arrays and collections as Java, so it’s almost 100% interoperable with it, and the only problem I had with it is communication between KAnnotator and Android Studio.

To call Java code from Kotlin, kotlinc needs to know which methods can and which can not return null, and I just couldn’t make it work with Android Studio, so I had to write things like this:

1
2
3
tracksSubject
    .takeUntil(viewDestroys)!!
    .observeOn(AndroidSchedulers.mainThread())!!

to constantly ensure the compiler that those won’t be null (!!)

Also, Kotlin and Java’s varargs just don’t do well together, so I had to user Java code to call methods with them.

Other Java tools and libraries

  • Retrofit doesn’t work with Kotlin’s traits, because those extend KObject, and Retrofit does not allow any inheritance in it’s interfaces
  • Parcelables: those are a complete pain, I had a Kotlin file that looked like this:
1
2
3
4
5
6
7
8
9
10
11
12
data class Track {
  var artist: Artist = Artist()
  var loved: Int = 0
  var name: String = ""
  var url: String = ""
  var image: Array<Image> = array()
  JsonProperty("@attr") var attr: Attr? = null

  class object {
    val EMPTY = Track()
  }
}

turned into this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@NoArgsConstructor
public class Track implements Parcelable {
  public static final @NotNull Track EMPTY = new Track();

  public @NotNull                         Artist  artist;
  public                                  int     loved;
  public @NotNull                         String  name;
  public @NotNull                         String  url;
  public @NotNull                         Image[] image;
  public @JsonProperty("@attr") @Nullable Attr    attr;

  @Override public int describeContents() { return 0; }

  @Override public void writeToParcel(@NotNull Parcel dest, int flags) {
    dest.writeParcelable(this.artist, 0);
    dest.writeInt(this.loved);
    dest.writeString(this.name);
    dest.writeString(this.url);
    dest.writeParcelableArray(this.image, flags);
    dest.writeParcelable(this.attr, 0);
  }

  private Track(Parcel in) {
    this.artist = in.readParcelable(Artist.class.getClassLoader());
    this.loved = in.readInt();
    this.name = in.readString();
    this.url = in.readString();
    this.image = (Image[]) in.readParcelableArray(Image[].class.getClassLoader());
    this.attr = in.readParcelable(Attr.class.getClassLoader());
  }

  public static final Parcelable.Creator<Track> CREATOR = new Parcelable.Creator<Track>() {
    public Track createFromParcel(@NotNull Parcel source) {return new Track(source);}

    @NotNull public Track[] newArray(int size) {return new Track[size];}
  };
}

because no current Parcelable generator will generate from Kotlin code, so I had to use Java here

  • Intellij Kotlin plugin needs a ton of work to catch up with IDEA’s Java autocompletion and refactoring capabilities.

Who should use it?

Kotlin doesn’t really fit Android (for me) at the moment, unless you do some heavy lifting on your client. For a regular app, full of UI boilerplate, Kotlin will just slow you down, unless it will get some better tooling support.

It has some pretty neat features for string processing and working with ADTs through pattern matching. So if you rely heavily on those, that could also play well with you.

Android Adapters

This post is about AdapterView adapters, please don’t confuse it with ViewPager ones I have 3 simple rules for writing adapters:

  1. Use immutable collections
  2. Replace ViewHolders with Views
  3. Stop writing adapters

1. Use immutable collections

1
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification.

Ever got an Exception like that? It’s easy to track down in a development stage, but when you get that in production – you’ll have to go through all of your codebase, looking for a desired List<> mutation. The easiest way to prevent those kinds of crashes, is to use immutable collections: arrays or Collections.unmodifiableList. Here’s the quick gist of how your adapter can look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ImmutableAdapter<T> extends BaseAdapter {
    final LayoutInflater inflater;
    final int            layout;
    List<T> items;

    public ImmutableAdapter(Context context, int layout, List<T> items) {
        this.inflater = LayoutInflater.from(context);
        this.layout = layout;
        this.items = Collections.unmodifiableList(items);
    }

    public void setItems(List<T> items) {
        this.items = Collections.unmodifiableList(items);
        notifyDataSetChanged();
    }
}

2. Replace ViewHolders with Views

Let’s say we have a download manager, and for each list item we have a following layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:id="@+id/textView"/>
    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"/>
</LinearLayout>

And a ViewHolder:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ViewHolder {
    @InjectView(R.id.textView)    TextView    name;
    @InjectView(R.id.progressBar) ProgressBar progressBar;

    public ViewHolder(View convertView) {
        ButterKnife.inject(this, convertView);
    }

    public void draw(Download download) {
        name.setText(download.name);
        progressBar.setProgress(download.progress);
    }
}

Now suppose we want to update those progressbars in realtime, we need to subscribe ViewHolders to some event source. We also need to unsubscribe them to prevent an Activity leak. Our adapter is now a bit too complex:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class DownloadsAdapter extends BaseAdapter {
    final Set<ViewHolder> holders = new HashSet<>();
    final LayoutInflater inflater;
    final int            layout;
    List<Download> items;

    public DownloadsAdapter(Context context, int layout, List<Download> items) {
        this.inflater = LayoutInflater.from(context);
        this.layout = layout;
        this.items = Collections.unmodifiableList(items);
    }

    public void setItems(List<Download> items) {
        this.items = Collections.unmodifiableList(items);
        notifyDataSetChanged();
    }

    @Override public int getCount() { return items.size(); }

    @Override public Download getItem(int position) {
        return items.get(position);
    }

    @Override public long getItemId(int position) { return position; }

    public static class ViewHolder {
        @InjectView(R.id.textView)    TextView    name;
        @InjectView(R.id.progressBar) ProgressBar progressBar;
        Subscription sub = Subscriptions.empty();

        public ViewHolder(View convertView) {
            ButterKnife.inject(this, convertView);
        }

        public void draw(Download download) {
            name.setText(download.name);
            progressBar.setProgress(download.progress);

            sub.unsubscribe();
            sub = download.progressEvents.subscribe(progressBar::setProgress);
        }

        public void release() {
            sub.unsubscribe();
        }
    }

    @Override public View getView(int position,
                                  View convertView,
                                  ViewGroup parent) {
        if (convertView == null) {
            convertView = inflater.inflate(layout, parent, false);
            ViewHolder holder = new ViewHolder(convertView);
            holders.add(holder);
            convertView.setTag(holder);
        }
        ViewHolder holder = (ViewHolder) convertView.getTag();
        holder.draw(getItem(position));
        return convertView;
    }

    public void release() {
        for (ViewHolder holder : holders) {
            holder.release();
        }
    }
}

And we also have to make sure that we release() our adapter in our Fragment/Activity

Use Views, Luke

Why?

  • They have lifecycle callbacks (for free)
  • You can reuse them outside of your adapters
  • Adapters should just pass data to the views, views should manage the drawing.

How?

First, define your View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class DownloadView extends LinearLayout {
    @InjectView(R.id.textView)    TextView    name;
    @InjectView(R.id.progressBar) ProgressBar progressBar;

    Subscription sub = Subscriptions.empty();

    public DownloadView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override protected void onFinishInflate() {
        super.onFinishInflate();
        if (!isInEditMode()) {
            ButterKnife.inject(this);
        }
    }

    public void draw(Download download) {
        name.setText(download.name);
        progressBar.setProgress(download.progress);

        sub.unsubscribe();
        sub = download.progressEvents.subscribe(progressBar::setProgress);
    }

    @Override protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        sub.unsubscribe();
    }
}

Then modify your layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<com.example.DownloadView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:id="@+id/textView"/>

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"/>
</com.example.DownloadView>

The Adapter now looks much cleaner, thanks to the free lifecycle callbacks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DownloadsAdapter extends BaseAdapter {
    final LayoutInflater inflater;
    final int            layout;
    List<Download> items;

    public DownloadsAdapter(Context context, int layout, List<Download> items) {
        this.inflater = LayoutInflater.from(context);
        this.layout = layout;
        this.items = Collections.unmodifiableList(items);
    }

    public void setItems(List<Download> items) {
        this.items = Collections.unmodifiableList(items);
        notifyDataSetChanged();
    }

    @Override public int getCount() { return items.size(); }

    @Override public Download getItem(int position) {
        return items.get(position);
    }

    @Override public long getItemId(int position) { return position; }

    @Override public View getView(int position,
                                  View convertView,
                                  ViewGroup parent) {
        DownloadView downloadView = (DownloadView) (convertView == null
                ? inflater.inflate(layout, parent, false)
                : convertView);
        downloadView.draw(getItem(position));
        return convertView;
    }
}

3. Stop writing adapters

Take a closer look at the previous adapter. The only things it need is a collection of items and a layout int, backed by a View with an appropriate draw method. Let’s abstract over both collections and views. Define a view interface like this:

1
2
3
public interface ViewHolder<T> {
    void draw(int position, T t);
}

And a source interface like this:

1
2
3
4
public interface Source<T> {
    T get(int position);
    int getCount();
}

Now we came to the point where we can write a fully generic adapter for our apps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Adapter<T> extends BaseAdapter {
    private final LayoutInflater inflater;
    private final int            layout;
    private       Source<T>      source;

    public Adapter(Context context,
                   Source<T> source,
                   int layout) {
        this.inflater = LayoutInflater.from(context);
        this.source = source;
        this.layout = layout;
    }

    @Override public int getCount() { return source.getCount(); }

    @Override public T getItem(int position) { return source.get(position); }

    @Override public long getItemId(int position) { return position; }

    @Override public View getView(int position,
                                  View convertView,
                                  ViewGroup parent) {
        ViewHolder<T> viewHolder = (ViewHolder<T>) (convertView == null
                ? inflater.inflate(layout, parent, false)
                : convertView);
        viewHolder.draw(position, getItem(position));
        return (View) viewHolder;
    }

    public void setSource(Source<T> items) {
        this.source = items;
        notifyDataSetChanged();
    }
}

When it comes to the views, just implement the view interface:

1
public class AudioListItemView extends LinearLayout implements ViewHolder<Audio>

And create a bunch of Sources:

1
2
3
4
5
6
7
8
9
public class ListSource<T> implements Source<T> {
    final List<T> list;

    public ListSource(List<T> list) {this.list = list;}

    @Override public T get(int position) { return list.get(position); }

    @Override public int getCount() { return list.size(); }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MergeSource<T> implements Source<T> {
    private final Source<T>[] sources;

    public MergeSource(Source<T>... sources) { this.sources = sources; }

    @Override public T get(int position) {
        for (Source<T> source : sources) {
            int size = source.getCount();

            if (position < size) {
                return source.get(position);
            }

            position -= size;
        }
        throw new AssertionError("impossible");
    }

    @Override public int getCount() {
        int total = 0;
        for (Source<T> source : sources) {
            total += source.getCount();
        }
        return total;
    }
}

You can create a Source for any collection of items, even Cursors! Just remember to make your sources immutable, and generate a new one for a ListView update.

Libraries that appear in the code snippets:

Homework:

Implement a generic adapter for a multiple view types, that should be easy enough.