Android Design Pattern - MVVM (Part 1)

  • Ivan Žarković

Hello reader,

I have challenged myself one more time to make a series of instructions on How to Build Weather Application for Android using Jetpack, Retrofit and Open Weather Api. This guide will be divided into four notes:

1. Creating a project and connecting it to an API using Android Architectual Components
2. Separating Retrofit interface within MVVM Component called Repository
3. Designing and styling UI
4. Debugging and testing

Android Jetpack

Jetpack is "a suite of libraries, tools, and guidance for Android developers" created and released by Google. It holds a collection of components libraries such as Architecture components. The architecture components help us in building:

  • Robust Apps
  • Testable Apps
  • Maintainable Apps

Architecture components we will use in this project:

LiveData: Notify views of any database changes.
ViewModel: Manage UI-related data in a lifecycle conscious way.

Why should we use Jetpack?

It helps us managing activity lifecycles, surviving configuration changes and controlling memory leaks. Practically Jetpack is used to help developers avoid writing a boiler-plate code, to follow best Android development architecture, and to simplify complex setup. In my humble opinion, it is a very powerful tool for developers to develop great modular applications. The focus of Jetpack is to help us implement modularization and to maintain scalability of our applications.

Prerequisite

You should be familiar with Java or Kotlin syntax, OOP concepts and RESTful API. You need to have installed the Android Studio or equivalent tool for mobile app development.

I am using Version Control system from Android Studio to connect to my GitHub repo. It is very simple to use, you can find instructions on How to connect GitHub to Android Studio.

Let's begin with the first part of the project:

1. Open Android Studio and create a new project with Empty Activity, I have named mine WeatherApp and chosen Java for language.

2. Add dependencies for Retrofit and ViewModel inside Gradle Scripts/build.gradle Module:app and Sync project

//retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
//Android Jetpack
    implementation "android.arch.lifecycle:extensions:1.1.0"
    implementation "android.arch.lifecycle:viewmodel:1.1.0"


3. Next, we are going to use the free weather API from https://openweathermap.org/api. We need to log in or to create a new account if we don't already have one, to be able to use API and to get API key (unique ID) and construct the desired URL (http://api.openweathermap.org/data/2.5/weather?q=belgrade&appid=YOURKEY). There is quite good documentation for you to use the desired location and parameters.


4. When our API URL is ready, open it and select and copy the whole JSON response, then navigate to jsonschema2pojo, select GSON annotation style and generate model classes.


5. Grant network access permissions and internet connection in AndroidManifest.xml

<!--permissions to use internet-->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


6. This is the part when it all starts to be interesting. Using the Retrofit library we are going to connect our application to Wheater API. Inside of com.example.wheaterapp create a new package and name it api.service then create Java interface and name it RetrifitApiService:

package com.example.weatherapp.api.service;

import com.example.weatherapp.pojo.Model;


import retrofit2.Call;
import retrofit2.http.GET;

public interface RetrofitApiService {
    String BASE_URL = "https://api.openweathermap.org/data/2.5/";
    @GET("weather")
    Call<Model> getWeatherData(@Query("q") String q,
                               @Query("appid") String appid,
                               @Query("units") String units);
}


7. Create a new package for the ViewModel and new Java class WeatherViewModel.java

package com.example.weatherapp.view.model;

import android.util.Log;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.example.weatherapp.api.service.RetrofitApiService;
import com.example.weatherapp.pojo.Model;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class WeatherViewModel extends ViewModel {

    private MutableLiveData<Model> weatherData;

}

8. Inside WeatherViewModel class add LiveData method to return data from the server

public LiveData<Model> getWeather() {
//if the response is null
        if (weatherData == null) {
            weatherData = new MutableLiveData<Model>();
//we will load it asynchronously from server in method below
            loadWeatherData();
        }
//returning the data
        return weatherData;
    }

9. Copy the code below to declare a method which uses Retrofit to get the JSON data our API

    private void loadWeatherData() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(RetrofitApiService.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        RetrofitApiService api = retrofit.create(RetrofitApiService.class);
        Call<Model> call = api.getWeatherData();
        call.enqueue(new Callback<Model>() {
            @Override
            public void onResponse(Call<Model> call, Response<Model> response) {
//finally we are setting the response to our MutableLiveData
                Log.d("responser", "Response body successfuly written");
                weatherData.setValue(response.body());
            }

            @Override
            public void onFailure(Call<Model> call, Throwable t) {
                Log.d("responser", t.getMessage());
            }
        });

    }


10. Use ViewModel to display data to the UI through Fragment or Activity:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d("init", "Hello world");

        //creating an viewmodel instance
        WeatherViewModel model = ViewModelProviders.of(this).get(WeatherViewModel.class);

        //setting the observer
        model.getWeather().observe(this, new Observer<Model>() {


            @Override
            public void onChanged(@Nullable Model model) {
                
                //later this data will be used to update UI
                Log.d("responser", "City: " + model.getName());
                Log.d("responser", "Current temperature in F: " + model.getMain().getTemp().toString());

            }
        });

    }
}

Run the app on an emulator and check the logcat.

 

If you have any struggles with the code you can always clone/download source code from GitHub.

 

Thank you for reading, see you soon with the next phase of the project.

Article Subscription