Hot Article Guide | Click Title to Read
Welcome to join the Java and Android Developer Community, Get Learning Videos
Awesome! 74 Complete APP Source Codes!
Google I/O 2017: Officially Released Android App Development Architecture Guide, with Demo and Official Documentation
Brief Review: Although the architecture choices in Android have always been quite free, with MVP, MVC, MVVM each having its supporters. However, Google recently released a practical guide on app architecture, providing quite detailed steps and some guiding suggestions. I hope everyone can take a look, learn a bit, create better and more user-friendly APPs, and contribute to the improvement of the Android ecosystem. 🙂
Recently, the official team released a best practices guide on app architecture. Here’s a brief introduction:
First of all, Android developers must know that there are four major components in Android, each with its own lifecycle and to some extent, uncontrollable by you. At any time, the Android operating system may reclaim these components based on user behavior or resource constraints.
This leads to the first guideline: “Do not save any application data or state in application components, and components should not depend on each other”.
The most common mistake is writing code unrelated to UI and interaction in Activity or Fragment. Reducing dependencies on them as much as possible can avoid many lifecycle-related issues, providing a better user experience.
The second guideline: “Drive the application UI through the model and persist as much as possible”.
There are two main reasons for this:
-
If the system reclaims your application resources or some other unexpected situation occurs, it will not lead to data loss for the user.
-
The model should be responsible for handling application data. Independent of the view and application components, it keeps the view code simple, and makes your application logic easier to manage. Moreover, placing application data within model classes is also more conducive to testing.
Official Recommended App Architecture
Here, the official team demonstrated how to build an application using the newly released Architecture Components.
Imagine you are planning to develop an interface that displays user personal information, with user data fetched from the backend via REST API.
First, we need to create three files:
-
user_profile.xml: Defines the interface.
-
UserProfileViewModel.java: Data class.
-
UserProfileFragment.java: Displays data from ViewModel and responds to user interactions.
For simplicity, we will omit the layout file here.
Note that ViewModel and LifecycleFragment are newly introduced in Android, and you can refer to the official documentation for integration.
Now that we have completed these three modules, how do we connect them? That is, when the user field in the ViewModel is set, we need a way to notify the UI. This is where LiveData comes into play.
LiveData is an observable data holder (utilizing the observer pattern). It allows Activity, Fragment, and other application components to observe it without creating strong dependencies between them. LiveData also automatically responds to lifecycle events of the components, preventing memory leaks, thus ensuring the application does not consume more memory. Note: The main difference between LiveData and RxJava or Agera is that LiveData automatically handles lifecycle events, avoiding memory leaks.
So, let’s modify the UserProfileViewModel:
Then observe it in UserProfileFragment and update our UI:
Fetching Data
Now, we have connected ViewModel and Fragment, but how does ViewModel fetch data?
In this example, we assume the backend provides a REST API, so we use Retrofit to access our backend.
First, define a Webservice:
Do not directly fetch data through ViewModel; here we will delegate the work to a new Repository module.
The Repository module is responsible for data handling, providing a clean and reliable API for other parts of the application. You can consider it as an intermediary layer between different data sources (Web, cache, or database) and the application.
Managing Dependencies Between Components
According to the code above, we can see that UserRepository has an instance of Webservice; do not directly create a new Webservice in UserRepository. This can easily lead to code duplication and complexity. For example, UserRepository is likely not the only class that uses Webservice. If every class that uses it creates a new Webservice, it will lead to resource wastage.
Here, we recommend using Dagger 2 to manage these dependencies.
Now, let’s connect ViewModel and Repository:
Caching Data
In real projects, the Repository often has more than one data source. Therefore, we will add a cache here:
Persisting Data
Now, when the user rotates the screen or temporarily leaves the application and returns, the data is directly visible because it is fetched from the cache. But what if the user closes the application for a long time and Android completely kills the process?
In our current implementation, data will be fetched again from the network. This is not a good user experience. This is where data persistence comes into play. We will introduce a new component, Room.
Room helps us easily implement local data persistence, abstracting many common database operations, and verifying each query at compile time, so that corrupted SQL queries lead to compile-time errors rather than runtime crashes. It also works perfectly with LiveData introduced above and helps developers handle many threading issues.
Now, let’s see how to use Room. 🙂
First, add @Entity above the User class to declare it as a table in your database.
Then create a database class that extends RoomDatabase:
Note that MyDatabase is an abstract class, and Room will automatically add the implementation.
Now we need a way to insert user data into the database:
Note that the load method above returns LiveData<User>, Room will know when the database changes and automatically notify all observers. This is the wonderful use of LiveData and Room together.
Now continue to modify UserRepository:
As you can see, even if we change the data source in UserRepository, we do not need to modify ViewModel and Fragment at all. This is the benefit of abstraction. It is also very suitable for testing; we can provide a test UserRepository when testing UserProfileViewModel.
The following content is presented as an appendix in the original text, but I personally think it is very important, so I moved it up to introduce it to everyone. 🙂
In the example above, those with a keen eye might have noticed that we did not handle network errors and loading states. However, in actual development, this is very important. Here, we will implement a utility class to choose different data sources based on different network conditions.
First, implement a Resource class:
Since loading data from the network and loading from disk are quite similar, we will create a new NetworkBoundResource class for easier reuse. Below is the decision tree of NetworkBoundResource:
API Design:
Note that ApiResponse is used as a wrapper for Retrofit2.Call, which converts its response into LiveData.
Below is the specific implementation:
Now, we can use NetworkBoundResource to fetch data based on different situations:
At this point, our code is complete. The final architecture looks like this:
Finally, some guiding principles
The following principles are not mandatory, but based on our experience, following them can make your code more robust, testable, and maintainable.
-
All components defined in the manifest – activity, service, broadcast receiver… are not data sources. Because the lifecycle of each component is relatively short and depends on the current user and device interaction and system operation. Simply put, these components should not serve as data sources for the application.
-
Establish clear responsibility boundaries between the various modules of your application. For example, do not place code unrelated to data caching in the same class.
-
Expose as few internal implementations as possible for each module. From past experience, never expose a large amount of internal implementation directly for the sake of convenience. This will lead to heavy technical debt in the future (making it difficult to replace new technologies).
-
When defining interactions between modules, consider how to isolate each module as much as possible and interact through well-designed APIs.
-
The core of your application should be something that makes it stand out. Do not waste time reinventing the wheel or repeatedly writing the same template code. Instead, focus on making your application unique and delegate some repetitive work to the Android Architecture Components or other excellent libraries introduced here.
-
Persist data as much as possible, so that your application remains usable in offline mode. While you may enjoy fast networks, your users may not.
Additionally, multiple official Kotlin samples have been released, so interested students should check them out quickly: Android Kotlin Samples
Original text: Guide to App Architecture
https://zhuanlan.zhihu.com/p/27026614
For more learning materials, click the “Read Original” below to obtain
Java and Android Architecture
Welcome to follow us, let’s discuss technology together. Scan and long press the QR code below to quickly follow us. or search for WeChat public account: JANiubility.
Public Account:JANiubility
Leave a Comment
Your email address will not be published. Required fields are marked *