The Medieval city of Kraków, Poland. Photo by Jacek Dyląg.

A short story about developing an SDK for Android. Part 2.

Paweł Dyląg
9 min readNov 7, 2017

In the previous part of the series I shared with you some of my experience in Android development, and some handy tricks I use while working at Estimote. I’ve got a couple more considerations that might be helpful to you while developing your own SDK/library/application.

We left off when the bartender disappeared behind the doors. You are sitting in a lovely tavern full of happy people, sipping a deep dark beer with a lovely foam, and waiting for the bartender to come back and to continue his story about the restoration of Esdeekya city.

Psst…here is some more tavern music for you to enrich the reading experience!

1. Consider using Dagger 2 for dependency injection

“I am back, my dear adventurer, and I see that you are still here waiting for the story to continue, aye? I hope you enjoy the beer!” He took a dishrag and started polishing the counter. “So we have been talking about how the constructors did the necessary stuff to upgrade the entire city of Esdeekya. I told you, wanderer, about all the preparations, the tools they used, and projects. Now is the time to talk about the work itself”. He scratched his thick black beard. “You have to know, my dear stranger, that the city reconstruction works were divided into separate parts. The most clever thing was to decouple the material processing people, such as stonecutters or woodworkers, from the actual work zone. They were members of several guilds, designing, pre-making, and delivering the stuff needed by the workers to the actual build area.” The bartender yawned.

I must say that I was reluctant to use Dagger 2 at first. The idea seemed really incomprehensible to me. Turns out that Dagger 2 is a wonderful tool:

  1. It lets you control the lifetime of objects in your code.
  2. It allows you to separate class instantiation from the actual use cases.
  3. It forces you to write much more decoupled code, thus making unit testing easier.

You can read some awesome articles by Eugene Matsyuk about Dagger 2 to understand its basics.

The whole idea is focused on three main concepts:

  • MODULE — a factory for objects from a similar domain. Remember the bartender speaking about the Stonecutters’ Guild? They provide various things made out of stone. It’s the same concept.
  • COMPONENT — a group of modules that provides particular functionality together. You can think about it as a supervisor that says: “Woodworkers’ Guild, Stonecutters’ Guild and Blacksmiths’ Guild — you will work now together to renovate our town hall. Mr… err…Mr. MainActivity will oversee your work. What an odd name!”
  • SCOPE — is an annotation that you use for defining the life scope of any particular object. Like @MainActivityScope or @ProximityObserverScope .

So basically the idea is to create Dagger 2 components inside main factories in your SDK — like SensorDataProviderFactory. Take a look at the example below:

I really encourage you to try using Dagger 2 — it looks complicated, and so it took me a good while to understand it, but it is worth it! If you have any doubts about the size of your codebase — Dagger 2 does most of its work at compile time. There are some additional generated classes, but:

Remember that it is better to have a little bit less efficient code that is flexible and easily maintainable, than a super-performant code that is almost closed to the future development and changes.

2. Create a flexible API with interfaces, factories, and fluent builders

“Oh, I see you just finished your beer, stranger. Let me pour you another one, because the story goes on,” he said while grabbing your empty mug. “So, the constructors hired a special type of a worker, called ‘the fluent builder’. You probably wonder what was his responsibility? Well, he could fluently translate the customer needs to the engineering language.”

Designing an API for an SDK is very different to building an application. Other developers will use your classes and will read your documentation. It is your job to deliver them the simplest possible experience. You should ask yourself these three important questions:

  1. What is the main purpose of my SDK?
  2. How will people interact with my code?
  3. What do I want them to be able to modify?

And then start designing your API. I prefer the concept of “top-to-bottom” design, where you first create an API and then write the implementation. Although I would say that sometimes it is better to have a hybrid version, where you need to know how the system will behave, in order to predict the possible API. Contrary to “bottom-to-top” you won’t end up with a messy, one-class-for all, coupled facade object, and you will retain the visibility and cohesion of your API.

Let your API operate on interfaces instead of concrete classes

Unless you are designing a library that is tightly coupled to the Android Framework (such as custom views, or animations) you should use interfaces instead of raw classes. Do not let your users instantiate classes using constructors (like thenew operator in Java). Provide them with a factory or builder object instead, and here is why:

  1. The Factory pattern doesn’t block your future development. You can add more methods for new types of objects if you need.
  2. The Factory pattern hides your objects behind interfaces, so you can change the implementation in the future without breaking user’s code.
  3. If you decide to break an API of your interfaces, you can add an adapter class for the old interface, so your code will be backward compatible.

Use fluent builders — they are cool!

I am a huge fan of fluent builders. They provide an easy way for constructing objects, and with the support of IntelliJ auto-completion, you can create magnificent contextual scopes for parameters. Look at how cool the experience is:

Although this is not the only design pattern people are using. Some people might want to use plain objects with getters and setters , so you might consider adding fluent builder API as an additional feature, leaving them to decide which pattern to use.

3. Test your code!

“You must know, my fellow adventurer, that the constructors put their shoulder to the wheel, and focused on the quality of their work. No rushing. No improvising. Every single part provided by the Guilds had to be durable and fully tested.”

Anyone who has pushed code to the production without properly testing it,
raise your hand.

*raises hand*

Yup, I did it. And it taught me not to do this anymore. Wonder why?

First of all, it’s like having sex without protection (unless you want to have a baby of course). If you like living on the edge — go for it. But if you deeply care about the quality of your sleep at night, you would better start writing unit tests.

Secondly, always remember that your code will evolve. It’s almost never an one night stand. It’s a long relationship that you need to care about. You will eventually start adding new code and modify existing code— and how will you make sure, that the old functionality remains the same? Certainly you will end up in a vicious cycle, where adding new code will scatter bugs in the existing code. You will desperately try to fix them, causing more to appear. “And it goes on and on and on, Heaven and Hell.”

On the other hand, it is extremely difficult to cover 100% of your code with tests. Sometimes you have got classes tightly coupled to an external framework, sometimes it is just a type of an interaction that is too difficult to mock; but in most cases, if you write your code having SOLID principles in mind, you should be able to design elegant unit tests with high coverage.

No matter how advanced you are, I have got a couple of pieces of advice that you might find useful. I use all of them in my day-to-day work.

May TDD be with you

Ever heard of TDD? It stands for ‘Test Driven Development’ and it is for people obsessed with testing and quality. I must say that I really want to reach pure TDD someday, but it is mildly difficult to apply this technique in a dynamic environment such as startup. This is why I prefer a more “hybrid” version.

So the idea is to use a RED, GREEN, REFACTOR circle as much as you can.
This involves a different approach — first you need to write a test, and then implement your class in order to pass this test. Let us take a detailed look:

First, the RED phase. You start with implementing raw tests for the functionality of your class. Then you launch tests — boom, none has passed! That is why it is called the RED phase.

Second, the GREEN phase. You implement the actual class so that it passes your tests. Don’t bother with elegant code right now. Hit the button. All tests have passed? That is why it is called GREEN phase.

Third, the REFACTOR phase. You may refactor your code now. Make it beautiful. Make it amazing. Be proud of it!

Finally you go back to the RED phase. Try adding more tests to make your code more bulletproof, and repeat the circle until you are happy with the results.

Use Kotlin back-ticks to make test names more readable

This is funny — Kotlin lets you use back-ticks for function name declaration. I use this to make names of my tests much more readable, and I can write almost whatever I want there! It is much better than using standard camel case or underscores:

@Test
fun `returns zero for negative input`(){
...
}

Disclaimer: Android Studio will probably highlight those names as an error. It will compile anyway, but if you want, you can turn this error off in the preferences.

Use GIVEN-WHEN-THEN pattern

Some simple advice to arrange the code inside your tests. I suggest creating a Live Template for this. If you are unable to organise your code in this way, you should definitely rethink its design — are you following the dependency inversion principle? Or single responsibility principle?

@Test
fun `returns zero for negative input`(){
// GIVEN
val operator = UsefulOperator()
// WHEN
val returnedValue = operator.compute(-1)
// THEN
assertThat(returnedValue).equals(0)
}

Consider using AssertJ for fluent assertions

AssertJ is a wonderful library that let you construct assertions fluently. It will adapt to the type of given object, providing you with a set of human-readable assertions. Look at the example from AssertJ docs:

// collection specific assertions (there are plenty more) 
// in the examples below fellowshipOfTheRing is a
// List<TolkienCharacter>
assertThat(fellowshipOfTheRing)
.hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);

Avoid using Robolectric

Robolectric is a unit test framework that gives you a set of mocked Android framework classes to run your tests without an emulator. Well, this is basically like making a hole in your ship and then trying to fix it with various tools. Some might be effective, but why would you make a hole in the first place?

Whenever your unit tests require you to mock any Android framework class, you are probably doing something wrong.

You want to test your code, not the third-party framework. Better isolate your codebase from any external code/classes/objects using interfaces and abstractions. In such way you will eventually have to mock your own interfaces, and not the external stuff.

Summary

  1. Make a module responsible for creating all the objects used across your system — using Dagger 2 might be very helpful! This will let you control the lifetime of your objects, and will make your library less prone to memory leaks.
  2. Think twice about your API. It is the front page of your SDK/library. Hide as much as you can behind interfaces. Use factories to create objects,
    and consider using fluent builders for customisable parameters.
  3. Always remember to test your code. This additional work will save you lots of work in the future — you will be sure, that your new features won’t break the old functionality. TDD may skyrocket your code quality.

“Oh, I need to bring another barrel of beer, the current one is empty. Would you mind waiting for me for a while? I’ll be right back with more story to tell!”

I hope you learned something useful — part 3 is waiting for you!
I had lots of fun writing the story, hope you feel the same reading it :)

If you’ve got any comments, feedback, or questions — feel free to ask me! You can reach me via my email: pawel@paweldylag.com,
follow me on
Twitter, Github, or LinkedIn,
or just ask questions directly in the comments :)

--

--

Paweł Dyląg

Android Software Engineer, ex @Estimote, tech geek, music addict, science enthusiast, RPG fan, book lover, and just a nice guy from Kraków, Poland.