Android was introduced into the world in 2005. During its 12 years of existence, Android has achieved amazing success, becoming the most installed mobile operating system. In the meantime, 14 different versions of operating systems have been launched, and Android has always become more mature. However, a very important area of the platform is still neglected: the standard architecture pattern, which can handle platform features, is simple enough for ordinary developers to understand and adopt.
Well, it's better to be late than not. In the final Google I/O, the Android team finally decided to solve the problem and respond to feedback from developers around the world, announcing the formal recommendation of the Android application architecture and providing the building blocks for its implementation: new architecture components. Better yet, they managed to do this without compromising the openness of systems we all know and love.
In this tutorial, we will explore the standardized architecture proposed by the Android team at Google I/O, and look forward to the main elements of the new architecture components: Lifecycle, ViewModel, LifeData, and Room. Instead of focusing too much on code, we focus on the concepts and logic behind these topics. We'll also look at some simple clips, all written in Kotlin, a magic language now officially supported by Android.
1. What is Android missing?
If you're just starting your journey as a developer, you probably don't know exactly what I'm talking about. After all, application architecture can be a vague topic at first. But believe me, you will soon understand its importance! As applications grow and become more complex, their architecture will become more and more important. It can make your work a happy or living hell.
Application Architecture
Roughly speaking, application architecture is a consistent plan that needs to be formulated before the development process begins. The plan provides how to organize and bundle mappings of different application components. It proposes guidelines to follow in the development process and enforces sacrifices (usually associated with more classes and templates) that will ultimately help you build a well-written application that is easier to test, scalable and maintainable.
Software application architecture is the process of defining structured solutions that meet all technical and operational requirements while optimizing common quality attributes such as performance, security and manageability. It involves a series of decisions based on various factors, each of which has a considerable impact on the quality, performance, maintainability and overall success of the application.
- Microsoft Software Architecture and Design Guide
A good architecture needs to consider many factors, especially system characteristics and constraints. There are many different architectural solutions, all of which have advantages and disadvantages. But some Key concepts It's common among all visions.
Old mistakes
Before the last Google I / O, Android did not recommend any specific architecture for application development. This means that you are completely free to adopt any model: MVP, MVC, MVPP, or even no schema at all. Most importantly, the Android framework does not even provide native solutions to problems created by the system itself, especially the life cycle of components.
Therefore, if you want to adopt Model View Presenter mode in your application, you need to come up with your own solution from scratch, write a lot of boilerplate code, or use libraries without official support. Lack of standards can lead to many poorly written applications whose code base is difficult to maintain and test.
As I said, this situation has been criticized for many years. In fact, I recently wrote an article about this issue and how it works for me. How to Use Android series Model View Demonstrator To solve its problems. But importantly, after 12 years, the Android team finally decided to listen to our complaints and help solve the problem.
2. Android architecture
New Android Architecture Guide Define some key principles that a good Android application should follow, and provide a safe way for developers to create a good application. However, the guide clearly states that the proposed route is not mandatory and the final decision is personal; developers should decide which type of architecture to adopt.
According to this guide, a good Android application should provide a solid foundation Separation of concerns, Drive the UI from a model. Any code that does not deal with UI or OS interactions should not be in Activity or Fragment, because keeping them as clean as possible will allow you to avoid many lifecycle-related problems. After all, the system can destroy activities or debris at any time. In addition, data should be processed by models isolated from the UI to address life cycle issues.
New Recommended Architecture
Android's recommended architecture is not easily annotated in the standard patterns we know. It looks like a model view controller pattern, but it is closely related to the architecture of the system and it is difficult to tag each element with known conventions. But that's not important because it relies on new architecture components to create separation of concerns with excellent testability and maintainability. And even better, it's easy to implement.
To understand the Android team's recommendations, we must understand all the elements of the architecture components, because they will bring us heavy work. There are four components, each specific role: Room, ViewModel, ****** LiveData, and Lifecycle. All these parts have their own responsibilities, and they work together to create a solid framework. Let's look at a simplified diagram of the proposed architecture in order to better understand it.
As you can see, we have three main elements, each of which has its responsibilities.
- In the View layer represented by Activity and Fragment, it is not handled with business logic and complex operations. It configures only views, handles user interaction, and most importantly, observes and displays the element ViewModel that LiveData obtains from it.
- The ViewModel automatically observes Lifecycle's view state and maintains consistency between configuration changes during maintenance and other Android life cycle events. Views also require data Repository from which LiveData is provided as observable. It is important to understand that ViewModel is never directly referenced by View, and that data updates are always done by LiveData entities.
- This Repository is not a special Android component. It is a simple class without any specific implementation. It is responsible for retrieving data from all available sources, from databases to Web services. It processes all these data, usually converting them into observable data LiveData and making them available to ViewModel.
- The Room database is an SQLite mapping library, which helps to process the database. It automatically writes a large number of template files, checks errors at compile time, and most importantly, it can directly return the query LiveData with observable.
I'm sure you've noticed that we've talked a lot about observability. this Observer mode It is one of the bases for LiveData elements and Lifecycle awareness components. This pattern allows the object to notify the observer of any changes in its status or data. Therefore, when an Activity observes a LiveData entity, it receives updates when the data has been modified.
Another Android recommendation is to use Dependency injection Systems to consolidate their architecture, such as Google's Dagger 2 Or use Service Locator Patterns (which are simpler than DI, but have few advantages). We won't introduce DI or service locators in this tutorial, but Envato Tuts + has some excellent tutorials on these topics. Note, however, that there are some particularities in using Dagger 2 and Android components, which will be described in the second part of this series.
3. Architectural components
We must delve into all aspects of the new component so that we can really understand and adopt this architecture model. However, we will not go into all the details in this tutorial. Because of the complexity of each element, in this tutorial, we will only discuss the general concepts behind each element and look at some simplified code snippets. We will try to cover enough space to show the components and get you started. But don't be afraid, because future articles in this series will dig deeper and cover all the features of architecture components.
Life Cycle Awareness Components
Most Android application components add life cycles, which are directly managed by the system itself. Until recently, developers have been able to monitor the status of components and take appropriate measures to initialize and terminate tasks at the appropriate time. However, it is easy to confuse and make errors related to this type of operation. But this [android.arch.lifecycle](https://developer.android.com/reference/android/arch/lifecycle/package-summary.html) package changed all this.
Now, activities and fragments have a [Lifecycle](https://developer.android.com/reference/android/arch/lifecycle/Lifecycle.html) object attached to them, which can be observed through [Lifecycle Observer] (https://developer.android.com/reference/android/arch/lifecycle/Oblifecycle/Lifecycle.html) classes, such as [ViewModel] (https://developer.com/reference/android)./ Arch/lifecycle/ViewModel.html) Any object that implements this interface. This means that the observer will receive updates about changes in the state of the object it is observing, such as pausing activity or when to start. It can also check the current state of the object being observed. Therefore, it is much easier to deal with the framework life cycle now.
Now, to create an Activity or Fragment that meets this new standard, you need to extend it LifecycleActivity or LifecycleFragment . However, this may not always be necessary, because the goal of the Android team is to fully integrate these new tools with its framework.
class MainActivity : LifecycleActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
Lifecycle Observer receives Lifecycle events and reacts through annotations. Method coverage is not required.
class MainActivityObserver : LifecycleObserver, AnkoLogger { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { info("onResume") } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) fun onPause() { info("onPause") }
The LiveData component
The LiveData component is a data holder with observable values. Since observer Lifecycle provides one during LiveData instantiation, LiveData will act according to Lifecycle status. If the observer's Lifecycle state is STARTED or RESUMED, the observer is active; otherwise it is inactive.
LiveData knows when the data is changed and whether the observer should receive the update active ly. Another interesting feature of LiveData is that it can remove an observer in a Lifecycle.State.DESTROYED state to avoid memory leaks during activity and debris observations.
A LiveData must implement onActive and onInactive methods.
class LocationLiveData(context: Context) : LiveData<Location>(), AnkoLogger, LocationListener { private val locationManager: LocationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager override fun onActive() { info("onActive") locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this) } override fun onInactive() { info("onInactive") locationManager.removeUpdates(this) } // .... }
To observe LiveData components, you must call observer (Lifecycle Owner, Observer < T>).
class MainActivity : LifecycleActivity(), AnkoLogger { fun observeLocation() { val location = LocationLiveData(this) location.observe(this, Observer { location -> info("location: $location") }) } }
The ViewModel component
One of the most important components of the new architecture is the ViewModel, which is used to save UI-related data and maintain its integrity during configuration changes such as screen rotation. In ViewModel, you can talk to Repository, gradually LiveData from it and make it viewable through the view. ViewModel also does not need to make new calls to the Repositoryafter configuration changes, which can greatly optimize the code.
To create a view model, extend the ViewModel class.
class MainActivityViewModel : ViewModel() { private var notes: MutableLiveData<List<String>>? = null fun getNotes(): LiveData<List<String>> { if (notes == null) { notes = MutableLiveData<List<String>>() loadNotes() } return notes!! } private fun loadNotes() { // do async operation to fetch notes } }
To access from a view, you can call ViewProviders.of(Activity|Fragment).get(ViewModel::class). This factory method will return the instance ViewModel or retrieve the reserved instance as needed.
class MainActivity : LifecycleActivity(), AnkoLogger { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel = ViewModelProviders.of(this) .get(MainActivityViewModel::class.java) viewModel.getNotes().observe( this, Observer { notes -> info("notes: $notes") } ) } }
The Room component
Android has supported SQLite from the beginning; however, in order for it to work, it always needs to write a lot of templates. In addition, SQLite does not save POJOs (plain old Java objects) or check queries at compile time. Room solves these problems all the way! It is a SQLite mapping library that can persist Java POJO, directly convert queries into objects, check errors at compile time, and generate observable objects from query results by LiveData. Room is a Object Relational Mapping Library, with some cool Android add-ons.
So far, you can accomplish most of the functions that Room can use other ORM Android libraries. However, none of them received official support, and most importantly, they could not produce LifeData results. The Room library is well suited for the persistence layer on the proposed Android architecture.
To create a Room database, you need an @Entity persistence, which can be any Java POJO, an @Dao interface for queries and input/output operations, and an abstract class RoomDatabase that @Database must extend.
@Entity class Note { @PrimaryKey var id: Long? = null var text: String? = null var date: Long? = null }
@Dao interface NoteDAO { @Insert( onConflict = OnConflictStrategy.REPLACE ) fun insertNote(note: Note): Long @Update( onConflict = OnConflictStrategy.REPLACE ) fun updateNote(note: Note): Int @Delete fun deleteNote(note: Note): Int @Query("SELECT * FROM note") fun findAllNotes(): LiveData<Note> // on Kotlin the query arguments are renamed // to arg[N], being N the argument number. // on Java the arguments assume its original name @Query("SELECT * FROM note WHERE id = :arg0") fun findNoteById(id: Long): LiveData<Note> }
@Database( entities = arrayOf(Note::class), version = 1) abstract class Databse : RoomDatabase() { abstract fun noteDAO(): NoteDAO }
Adding Architecture Components to the Project
Currently, to use the new architecture components, you need to add the Google repository to your build.gradle file first. For more details, see Official Guide.
`allprojects {` `repositories {` `jcenter()` `// Add Google repository` `maven { url '[https://maven.google.com](https://maven.google.com/)' }` `}` `}`
conclusion
As you can see, Android's standardized architecture involves many concepts. Don't expect a complete understanding of the subject. After all, we're just introducing the subject. But you now have enough knowledge to understand the logic behind the architecture and the roles of different architecture components.
We discussed most topics related to the proposed Android architecture and its components; however, the first part of Repository cannot cover detailed information about component implementation and some additional content, such as classes and Dagger 2 systems. We will explore these topics in the next article.
Bye!