Porcupine Programmer

Programming rants, random stuff and some more programming.

2013 Summary

| Comments

One year ago I set myself few goals for 2013. Let’s take a look how it went:

  1. I gave two talks.
  2. My blog was viewed over 13k times.
  3. I have published another game on Nokia Store.
  4. I have authored or contributed to several open source projects.

Meeting two out of three goals and some open source bonus ain’t that bad. The gloating part is done, let’s move to the wishful thinking part, a.k.a. goals for 2014:

  1. Keep giving the talks. I find that preparing the presentation forces me to do thorough investigation of the topic, question all my assumptions and prepare answers for potential questions. This is probably the best way to learn about something, and as a bonus you’re sharing that knowledge afterwards.
  2. Blog more. This year for each post I wrote I have added at least one more topic to my blog todo list. The resolution for this year is clearing this backlog and generating at least 25k views. BTW: when I started this blog I feared that I won’t have enough content to write regularly. Bollocks.
  3. Publish at least one app on Google Play.
  4. Keep working on the libraries I have published this year. It might not be a perfect metric of how useful to others my work turns out to be, but I’d like to accumulate 200 Github stars total on the projects I authored or co-authored.

The only thing left to be done is to wish you a happy New Year!

To Guava or Not to Guava?

| Comments

I faced this dilemma recently, when I was preparing first release of Cerberus utility for Android. On one hand, in Cerberus I used a tiny subset of Guava features which can be trivially rewritten in vanilla Java in 15 minutes, so maybe I should not force Guava down peoples throat? On the other hand I’m a huge fan of Guava and I think you should definitely use it in anything more complicated than “Hello, world!” tutorial, because it either reduces a boilerplate or replaces your handrolled utilities with better, faster and more thoroughly tested implementations.

The “this library bloats my apk” argument is moot, because you can easily set up the ProGuard configuration which only strips the unused code, without doing any expensive optimizations. It’s a good idea, because the dex input will be smaller, which speeds up the build and the apk will be smaller, which reduces time required to upload and install the app on the device.

I found the problem though, which is a bit harder to solve. Modern versions of Guava use some Java 1.6 APIs, which are available from API level 9, so when you try to use it on Android 2.2 (API level 8), you’ll get the NoSuchMethodException or some other unpleasant runtime error (side note: position #233 on my TODO list was a jar analyzer which finds this problem). On Android 2.2 you’re stuck with Guava 13.0.1.

This extends also to Guava as a library dependency. If one library supports Android 2.2 and older, it forces old version of Guava as dependency. And if another library depends on more recent version of Guava, you’re basically screwed.

One conclusion you can draw from this blog post is that you shouldn’t use Guava in your open source libraries to prevent dependency hell, but that’s spilling the baby with the bathwater. The problem is not Guava or any other library, the problem are Java 1.6 methods missing from Android API level 8! The statistics from Google indicates that Froyo is used by 1.6%, in case of Base CRM user base it’s only 0.2%. So more reasonable course of action is finally bumping minSdkVersion to 10 (or even 14), both for your applications and all the libraries.

Offline Mode in Android Apps, Part 1 - Data Migrations

| Comments

This year I gave a talk on Krakdroid conference about offline mode in Android apps. By offline mode I mean implementing the app such way that the network availability is completely transparent to the end users. The high level implementation idea is to decouple the operations changing the data from sending these changes through unreliable network by saving the changes in local database and sending them at convinient moment. We have encountered two major problems when we implemented this behavior in Base CRM: data migrations and identifying entities. This blog post describes the first issue. It might not be obvious why do you need the data migrations, so let’s clear this out. Let’s say on your mobile you have some data synced with backend (green squares on left and right) and some unsynced data created locally on mobile (red squares on the left).

Now let’s say that we introduce new functionality to our app, which changes the schema of our data models (the squares on the backend side are changed to circles).

The schema of the local database have to be changed as well. The naive way of handling this situation is dropping old database with old schema, creating new one with new schema and resyncing all the data from backend, but there are two issues with this approach: if there is a lot of data the resyncing might take a while, which negates the most important advantage of offline mode – that the app is fully functional all the time.

More serious issue is that dropping the old database means that the unsynced data will be dropped along with it.

The only way to provide the optimal user experience is to perform schema migrations locally for both synced and unsynced data:

Migrating the data doesn’t sound like a challenging thing to code, but the combination of obscure SQLite and Android issues complicates the matter. Without proper tools it’s quite easy to make your code unmaintainable in the long run. I’ll describe this issues and our solutions in the further posts.

Krakdroid 2013

| Comments

At the beginning of the December I had an opportunity to gave a talk on a Krakdroid conference. The organizers outdid themselves this year, the venue, other speakers and the overall event atmosphere was amazing. Definitely a place to be at if you’re in Krakow at the end of the year.

This year I talked about the offline mode in Android apps. The talk was 30% sales pitch, 10% shameless plug and 60% describing the pitfalls one can fall into when implementing offline mode. I’m going to describe two major problems with offline mode in details on my blog and here are the slides:

Besides giving the talk, I have attended few other very interesting talks: +Wojtek Erbetowski has shown the way they test Android apps at Polidea using RoboSpock; +Mateusz Grzechociński introduced Android devs to new build system and shared an awesome gradle protip: use —daemon command line parameter to shave off few seconds from gradle startup. +Mateusz Herych described the Dagger basics and warned about few pitfalls. Mieszko Lassota described some UI blunders not only from the programming world. Finally, Krzysztof Kocel and Paweł Urban summarised the security pitfalls.

All in all, this years Mobilization conference was a great place to be at. See you there next year!

More Guava Goodies - AbstractIterator

| Comments

A while ago I wanted to perform a certain operation for every subsequent pair of elements in collection, i.e. for list [1, 2, 3, 4, 5] I wanted to do something with pairs [(1, 2), (2, 3), (3, 4), (4, 5)]. In Haskell that would be easy:

1
2
3
Prelude> let frobnicate list = zip (init list) (tail list)
Prelude> frobnicate [1..5]
[(1,2),(2,3),(3,4),(4,5)]

The problem is, I needed this stuff in my Android app, which means Java. The easiest thing to write would be obviously:

1
2
3
4
5
6
7
List<T> list;
for (int i = 1; i != list.size(); ++i) {
  T left = list.get(i-1);
  T right = list.get(i);

  // do something useful
}

But where’s the fun with that? Fortunately, there is Guava. It doesn’t have the zip or init functions, but it provides tool to write them yourself – the AbstractIterator. Tl;dr of the documentation: override one method returning an element or returning special marker from endOfData() method result.

The zip implementation is pretty straightforward:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static <TLeft, TRight> Iterable<Pair<TLeft, TRight>> zip(final Iterable<TLeft> left, final Iterable<TRight> right) {
  return new Iterable<Pair<TLeft, TRight>>() {
    @Override
    public Iterator<Pair<TLeft, TRight>> iterator() {
      final Iterator<TLeft> leftIterator = left.iterator();
      final Iterator<TRight> rightIterator = right.iterator();

      return new AbstractIterator<Pair<TLeft, TRight>>() {
        @Override
        protected Pair<TLeft, TRight> computeNext() {
          if (leftIterator.hasNext() && rightIterator.hasNext()) {
            return Pair.create(leftIterator.next(), rightIterator.next());
          } else {
            return endOfData();
          }
        }
      };
    }
  };
}

The tail can be achieved simply by calling the Iterables.skip:

1
2
3
4
public static <T> Iterable<T> getTail(Iterable<T> iterable) {
  Preconditions.checkArgument(iterable.iterator().hasNext(), "Iterable cannot be empty");
  return Iterables.skip(iterable, 1);
}

For init you could write similar function:

1
2
3
4
public static <T> Iterable<T> getInit(final Iterable<T> iterable) {
  Preconditions.checkArgument(iterable.iterator().hasNext(), "Iterable cannot be empty");
  return Iterables.limit(iterable, Iterables.size(iterable));
}

But this will iterate through the entire iterable to count the size. We don’t need the count however, we just need to know if there is another element in the iterable. Here is more efficient solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static <T> Iterable<T> getInit(final Iterable<T> iterable) {
  Preconditions.checkArgument(iterable.iterator().hasNext(), "Iterable cannot be empty");

  return new Iterable<T>() {
    @Override
    public Iterator<T> iterator() {
      final Iterator<T> iterator = iterable.iterator();
      return new AbstractIterator<T>() {
        @Override
        protected T computeNext() {
          if (iterator.hasNext()) {
            T t = iterator.next();
            if (iterator.hasNext()) {
              return t;
            }
          }
          return endOfData();
        }
      };
    }
  };
}

All methods used together look like this:

1
2
3
4
List<T> list;
for (Pair<T, T> zipped : zip(getInit(list), getTail(list))) {
  // do something useful
}