Adel Nizamutdinov

I wanted to share this

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.

Comments