If you enjoy talks from 360 AnDev, please support the conference via Patreon!

Architecture Components, which were released at I/O’17, are a new set of libraries and guides focusing on application development. In this talk, I am going to talk about why this project started, how we’ve developed it with the community feedback and what we are trying to achieve with it.If you enjoy talks from 360 AnDev, please support the conference via Patreon!

Android Development Is Hard

Android development is hard. As developers, you are difficult to replace, and as a result are also paid very well. This comes with disadvantages, if a developer doesn’t know the ins and outs of Android, they are not going to write decent applications. Moreover, the barrier to entry is high. If development is hard, no one will start with it.

When we met with a focus group of developers last August, we were surprised by all of the complaints about the framework that we thought was straightforward. With feedback from these meetings, we worked on making some improvements.

Approaching the Problems

We approached the problems with the following in mind:

We are not able to start from scratch, and our improvements will need to work well with existing solutions.

We need to be opinionated with high-level solutions.

We need to consider scale as applications grow.

Reach, to include backward compatibility on devices.

Pragmatism, in terms of choosing between a 90% correct solution that is easy to achieve versus a 100% correct solution that is difficult.

Embracing the Lifecyles

Let’s start with embracing the lifecycles.

Get more development news like this

Take a simple example such as setting a text when a click listener is called:

// in activity void doSomething () { findViewById ( "button" ). setOnClickListener ( { view -> api . fetch (). onResponse ( data -> textView . setText ( data . getValue ()); }); }); }

This does not work, as it’s not handling the lifecycle. To make it work, you need to keep a reference to the response and manage that reference. All of this boilerplate code needs to be done for something simple to work:

// in activity void doSomething () { findViewById ( "button" ). setOnClickListener ( { view -> request = api . fetch (); request . onResponse ( data -> textView . setText ( data . getValue ()); request = null ; }); }); }

Android is infamous for having boilerplate code, and we need to fix this as it’s part of the fundamentals. The lifecycle is a very fundamental thing; it’s going to be there, so it should be easier.

We started with a prototype, and turned the methods we have into states. Every time an activity or fragment receives one of these messages it moves to another state.

But the reality is different. These things are not states, instead, these are events.

Reach

What does it mean to have better reach? If you’re making an activity observable, this is probably the code you would write.

val activity : Activity .. . activity . observe ({ onStop -> //cancel ops })

For activity , just observe and do whatever you want. We have this capability, but it would only work in API 26+. Java does not support multiple inheritance, so we cannot provide you with another base class.

For this issue, we came up with a lifecycleRegistryOwner - this extends the lifecycle on the interface. This allows us to make any of your activities a lifecycle owner without writing too much code. This code works on API 14+, and you can change any activity to be like this with just three lines, giving us 100% reach.

class MyActivity extends Activity implements LifecycleRegistryOwner { LifecycleRegistry registry = new LifecycleRegistry ( this ); public LifecycleRegistry getLifecylce () { return registry ; } } activity . getLifeCycle (). observe ({ onStop -> //cancel ops })

Let’s say we have a listener that observes an activity, and then there’s an activity that also overrode that method.

// listener.java @OnLifecycleEvent ( ON_STOP ) fun stop () { println ( "listener is called" ) } //activity.java override fun onStop () { super . onStop (); println ( "activity is called" ); }

You may question which is called first? If you think about a normal observer, usually when a component changes state, the component first changes the state, and then you call the observer saying that the component’s state has changed. This is the answer outside of Android.

In Android, however, we should call the listener first. If there is code where it listens for the activity to stop, and if it stops, it marks itself and does not call back the activity. Instead, it should be written like so:

@OnLifecylceEvent ( ON_STOP ) void stop () { stopped = true ; } void onNewData ( Data data ) { if (! stopped ) callbackInActivity . onChanged ( data ) }

Persistent Data

Android provides SQLite (which is great), but the APIs we provide around SQLite have a lot of boilerplate code, and you need to build string SQLs on your own. As a solution, we needed it to be fast and convenient.

Currently, using SQLite means using ContentValues to input values:

ContentValues organization = new ContentValues (); organization . put ( "id" , 1 ); organization . put ( "name" , "AnDev 360" ); myDataBase . insert ( "organizations" , null , organization );

In addition, query builders that do not look like SQL:

Organization org = new Select (). from ( Organization . class ) . where ( Condition . column ( Organization$Table . NAME ) . is ( "AnDev 360" )) . querySingle ());

This is not straightforward.

We decided to let you write SQL because it’s concise, and for every single SQL or SQLite problem you will have, there’s already an answer on StackOverflow. This is how we came up with Room.

We were then looking at more examples; this is how you do a join in one of the ORM libraries.

new Select () . from ( Organization . class ) . join ( Admin . class , Join . JoinType , INNER ) . on ( Condition . column ( Organization . Columns . ADMIN_ID ) . eq ( Condition . column ( Admin . Columns . ID ))). queryList ();

As soon as your query starts getting complex, your query builder code becomes really unreadable. If I do the same thing in SQL, it’s so much easier, and it’s so much more understandable.

Architecture

Android developers often have no idea how they should be writing opinionated Android applications. Today, there are many good libraries that help you do things very easily. For example, if you want to fetch data, you can use retrofit, it’s a super nice API. If you want to show a list of things, use RecyclerView.

These components are, very nice and scalable, If you want to put them together, or you want to write something that fetches a list of something that you can modify offline, then sync to the backend, it becomes problematic, and we don’t provide anything here. That’s why the community came up with all these mobile architecture talks.

First, there is no such one perfect architecture that works everywhere in the best case. Secondly, Android is so open, we don’t want to pick something that hinders the development of other cooler things.

The architecture we came up with is the ViewModel . We introduced it because people are writing bad activities. During codelab, developers were concerned that we merely introduced another problem and shifted the GOD activity problem to a God ViewModel problem. We kept iterating on this issue and provided an extra layer.

In this codelab, people said, no. If you do that, you just solve the GOD activity problem and you just created GOD ViewModel problem. I’m so happy with the GOD ViewModel, and corporate legal activity because at least it won’t crash, but, it is ugly. We debated a bunch more. It’s worth introducing one more layer.

By Google I/O, we released our alpha one version, and people liked it. Alpha four is currently out, and we will continue to do iterations until 1.0.