Architectural Design Pattern Android
Architectural Design Pattern Android
Design Patterns
About Course
• Duration (18 hours)
- 3 Lectures (9 hours)
- 3 Labs (9 hours)
• Evaluation Criteria
- 40% for labs
- 60% for projects
Why using Architecture?
Architectural design patterns
As the Android apps became more complex, developers began to think
which best practices available to make apps more extensible,
maintainable and testable.
App architecture patterns they all exist to help you design your app in
such a way that allows the app to be maintainable as it scales.
• For example, when you update the UI of your app with a fancy new
design, you want to do so without having to change any of the other
code, such as the underlying data.
Organizing your code into small pieces make it easier to be testable. Once it
became testable
• Model is the data layer. This includes the data objects, database classes, and other
business logic, concerned with storing the data, retrieving the data, updating the data,
etc.
• View renders the data using the Model in a way suitable for user interface. It is what gets
displayed to the user.
• Controller is the brain of the system. It encapsulates the logic of the system, and it
controls both the Model and View. The user interacts with the system through this
controller.
MVC in Web / Desktop
When MVC was originally conceptualized, it was initially applied to
desktop and web applications. A typical application of this pattern in
the web realm would look like this
• The user inputs are HTTP requests.
• The Controller handles those 4
5
requests by updating the data in the
Model.
• The View returned to the user is a 1
3
rendered HTML page, displaying the
data. 2
Model View Controller
Because components are isolated from each other, each focused on
only one responsibility, the system is more flexible and modular.
The Controller is the Android Activity where it can handle user inputs
(EditTexts and pressed buttons) and respond accordingly.
The Model consists of the data objects, which, in Android, are just regular
POJO Java bean classes, as well as the classes to handle data locally and
remotely.
Model classes are simple classes which the other components can use.
Model
Model
Model Cont’d
View
View is what’s displayed to the user.
It should not be concerned with the values of the actual data it is
displaying, only that it needs to be displayed.
• The controller updates the model when something happens in the view.
The controller will also update the view when the model changes.
• Too often, the controller responsibilities are also found in activities and
fragments.
Controller Cont’d
Android Follows MVC by Default
View - Logic Separation
Adapter is considered a way to organize data on the recycler view.
So, it is considered a part of the view.
In order to prevent having any logic in the view. We don’t provide any code in the
setOnClickListener () instead we we’ll keep the logic to the controller.
There are two commonly used techniques for organizing your app's
package structure.
1. Group by category.
2. Group by feature
Organize by features
• This allows you to place FavoriteActivity, FavoriteFragment,
FavoriteAdapter, FavoriteModel in one big package.
• Inside the feature the files can be located under sub-packages for more
organization based on the architecture you’re following.
• When there are common packages that is used during the whole
application (not limited to only one feature) , these packages can stand
alone not following any feature i.e.: utils, model, network ..etc.
Organize by features
As a solution we can make the retrofit call in method and receive the
returned value.
When calling the start
method. The normal
result is :
No data retrieved!
Retrofit Callback
Retrofit enqueue CallBack to make our network calls.
These callbacks act as a promise that a result will return in some time
in the future (whether it is a successful result or not).
We need to watch this response and execute the proper logic as soon
as the results arrive
Retrofit Callback
To handle this let’s:
1. Create an interface i.e: NetworkCallback
2. In the interface create the proper methods corresponding to
success or failure of the response.
3. Pass a reference from the interface type to the constructor /
Method of the network class.
4. when overriding onResponse and onFailure on the CallBack invoke
the proper methods in the interface using the provided reference
5. Now the proper controller should implement the interface to offer
the proper results to the view.
Retrofit Callback separation
1. Create an interface i.e: NetworkCallback
2. In the interface create the proper methods corresponding to success
or failure of the response.
Retrofit Callback separation Cont’d
3. Pass a reference from the interface type to the required method inside the network class.
Retrofit Callback separation Cont’d
4. when overriding onResponse and onFailure in the CallBack invoke the proper methods
in the interface using the provided reference
Inside MovieClient
Retrofit Callback separation Cont’d
@Delete
void deleteMovie (Movie movie);
}
5. Create your database
@Database(entities = {Movie.class}, version = 1) using the database
public abstract class AppDataBase extends RoomDatabase { builder
private static AppDataBase instance = null;
public abstract MovieDAO getMovieDAO();
public static synchronized AppDataBase getInstance(Context context){
if (instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), AppDataBase.class, "moviesdb")
.build();
}
return instance;
}
}
DataBase Inspector
Higher version of android studio (starting from 4.1 and above) enable database Inspector.
Database Inspector allows you to inspect, query, and modify your app's databases while
your app is running.
The Database Inspector works with plain SQLite and with libraries built on top of SQLite,
such as Room.
Using SQLite
As Room is based on SQLite the same thing can be applied here too.
So you can check the real stored data.
Using Room
By using them, you can move the code of dependent components out of the lifecycle
methods and into the components themselves.
Lifecycles are managed by the operating system or the framework code running in your
process. They are core to how Android works and your application must respect them. Not
doing so may trigger memory leaks or even application crashes.
LifeCycle Owner
LifecycleOwner is a single method interface that denotes that the class has a Lifecycle. It has one
method, getLifecycle(), which must be implemented by the class.
Android provides built-in components that are lifecycle owners. For activities, ComponentActivity,
which is the base class for AppCompatActivity, is the one that implements LifecycleOwner.
Also, there are other classes that already implement this interface, too. For example, Fragment is a
LifecycleOwner
The lifecycle owner needs to know all the components that need to listen for its lifecycle changes.
Using the observer pattern is the best approach to achieve this.
• No memory leaks
LiveData doesn’t have public methods to update its value. But it has
subclasses that are eligible for doing so.
Apply LiveData to Room
• Now we can mix liveData and Room together to get our desired DAO
1
Observing LiveData
3 2
Behind The Scenes
MutableLiveData
The MutableLiveData class exposes the setValue(T) and postValue(T) methods
publicly and you must use these if you need to edit the value stored in a LiveData
object.
Methods:
1. postValue() - Posts a task to a main thread to set the given value.
2. getValue() - Returns the current value.
3. observe() - Adds the given observer to the observers list within the lifespan of
the given owner.
Room with LiveData
The Room persistence library supports observable queries, which
return LiveData objects.
Observable queries are written as part of a Database Access Object
(DAO).
Room generates all the necessary code to update the LiveData object
when a database is updated.
The generated code runs the query asynchronously on a background
thread when needed. This pattern is useful for keeping the data
displayed in a UI in sync with the data stored in a database.
Repository
Repository Cont’d
Repository Cont’d
Project Structure
Lab 1 – (Products App – MVC)
• Build an application that get a list of products from the following
JSON API
https://dummyjson.com/products
Create AllProducts Activity class that shows data from network and
add items to database
Activity almost always contain some view logic, such as showing or hiding
views, displaying a progress bar, toast or updating the text on screen, in
response to user input.
If the Activity must hold references to views and logic for changing them as
well as all the logic for its Controller responsibilities, then the Activity
effectively serves as both the Controller and the View in this pattern
Applying MVC to Android
• The controller then has all the responsibility for what’s displayed on screen.
For a simple screen, this can be manageable but, as features get added,
this file will keep growing.
The problem was that the Android Activity, unfortunately, served the
role of both View and Controller.
Ideally, you would want to somehow move the Controller out of the
Activity into its own class so that the Controller can be unit testable.
MVP
One alternative to the problems presented by MVC is to decouple some of the parts
from each other.
MVP is an architecture pattern that you can use to deal with some of the
shortcomings of MVC, and is a good alternative architecture.
• Any code that does not directly handle UI or other Android framework specific
logic should be moved out of the View and into the Presenter class.
• Model and Presenter do not extend Android framework-specific classes, and, for
the most part, they should not contain Android framework-specific classes to be
easily tested.
4. Presenter then
updates the View 3. Presenter updates
the Model
However, the Presenter, needs to talk to the View, which means it needs a
reference to the Activity, an Android framework specific class.
How can you work around to get a reference to an Android class when you
write your unit tests?
Presenter & View Referencing
The way to resolve this problem is to create interfaces for the Presenter
and View, keeping the interfaces as contracts between View and
presenter.
This way, the Presenter and View interact with each other through
interfaces rather than actual implementations, which is, in general, a
good software engineering principle that allows decoupling of the two
classes.
This allows you to mock the ViewInterface in the Presenter for unit
testing
MVP Advantages
By dividing an Activity into separate Model, View, and Presenter classes
with interfaces, you are able to :
• Achieve separation of concerns as well as
• Unit-testable Models and Presenters.
The business logic is what gives value to your app—it's made of real-
world business rules that determine how application data must be
created, stored, and changed.
Each data source class should have the responsibility of working with
only one source of data, which can be a file, a network source, or a
local database.
Data source classes are the bridge between the application and the
system for data operations.
Naming Convention
• Data source classes are named after the data they're responsible for
and the source they use. The convention is as follows:
Products Remote
Movies Local
etc.
Example: MoviesRemoteDataSource
Remote Data Source
Local Data Source
Local Data Source Implementation
Local Data Source Implementation
Local Data Source Implementation
Repository
Other layers in the hierarchy should never access data sources directly;
the entry points to the data layer are always the repository classes.
And for this topic you can find more on: https://developer.android.com/jetpack/guide/data-layer
Folder Structure
Lab 1 – (Products App – MVP)
• Build an application that get a list of products from the following
JSON API
https://dummyjson.com/products
Create a Repository that manages the Local and Remote data sources.
View displays the UI and informs the other layers about user actions. i.e.:
fragments, activity..
Model retrieves information from your data source and exposes it to the
ViewModels. i.e.: POJO, Repository and data sources
It should also receive any events from the ViewModel that it needs to
create, read, update or delete any necessary data from the backend
(Model don’t change)
ViewModel is also responsible for exposing events that the Views can
observe. i.e.: New movie in your Database.
The ViewModel retrieves the necessary information from the Model, applies
the necessary operations and exposes any relevant data for the Views.
MVVM Flow
In MVVM View serves as the entry point for user
input and starts to handle it. VIEW
UI Events
Update
Data Update
VIEW Events ViewModel Notify Model
Data
Jetpack ViewModel
Configuration Change Problem
During configuration change in Android all data contained within
components is lost
The most common solution to this problem is to save your data in your
onSaveInstanceState()in the bundle and restore it later in your
onCreate()method.
This approach only works for primitive data such as integers or simple classes
that can be serialized and deserialized
The created ViewModel is retained as long as the scope is alive. For example,
if the scope is a fragment, the ViewModel is retained until the fragment is
detached.
How to ViewModel
For our case we need to pass repository to the constructor of our viewModel
ViewModelFactory
The factory method pattern is a creational design pattern that uses
factory methods to create objects. A factory method is a method that
returns an instance of the same class.
• Data Sharing: Data can be easily shared between fragments in an activity using
ViewModels.
Activity
Interface
Fragment_A_Sender Fragment_B_Reciever
Communicator Design Pattern
Communicator Interface
fun respond( . . .)
Remember the
fragment counter lab?
Navigation Components move the data via the bundle which has a very small
size with limitations on the types of data that could be added, primitives,
serializables or parcelables.
In general, you should strongly prefer passing only the minimal amount of
data between destinations
• KTX extensions provide concise, idiomatic Kotlin to Jetpack, Android platform, and
other APIs. To do so, these extensions leverage several Kotlin language features
• To start using anything in the Android KTX, add the following dependency to your
project's build.gradle file:
repositories {
google()
}
Hence, we can create our ViewModel using a simple way using the
by viewModels() which is an extension function makes the creation of a viewmodel easier
2. For prameterized constructor send your factory as a lambda argument to the viewModels()
val myViewModel : MyViewModel by viewModels { factoryInstance }
build.gradle Configuration Needed
Add Kotlin Annotation Processor Tool (kapt) to your plugins section
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
For Room& Coroutine add the following dependencies to your dependencies section
//Room
implementation "androidx.room:room-ktx:2.5.0"
implementation "androidx.room:room-runtime:2.5.0“
kapt "androidx.room:room-compiler:2.5.0“
//ViewModel & livedata
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0’
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1’
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
//Coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0‘
Lab 3 – (Products App – MVVM)
• Build an application that get a list of products from the following JSON API
https://dummyjson.com/products
And displays them on a list, and gives the user the option to add any of them
to favorite.
Hint: Main Screen Favorite Products
All Products
Architecture: MVVM
Network Lib: Retrofit
DB: Room
Observation: LiveData
Language: Kotlin
Use Coroutine.
Lab Milestones
Create Retrofit part that connects to products API and fetches data.
Create a Repository that manages the Local and Remote data sources.