Tag

viewmodel

Browsing

LiveData is introduced as lifecycle-aware data holder with the observer pattern. That means, When something is new in data set it will notify. It automatically changes the view for the changes in the data set. In this tutorial, we will demonstrate LiveData android example

When we have LiveData object(eg. list of customers, can be a list of users, it can be any data sources ), we add some Lifecycle Owner (such as Activity or Fragment) as an observer to this data update. Same as observer pattern but with respecting lifecycle states.

In large application there can be two type of data source. Local Sqlite database, remote Rest APIs . With Live we can write code for observer these data source for data changes and update views accordingly.

Benefits of using LiveData

  • Ensures your UI matches your data state- It based on observer pattern. So we’ll be notified every time the data changes instead of requesting the data each time from ViewModel
  • Avoid memory leaks -- Observers bounded with life cycle and when lifecycle is destroyed LiveData object also destroy.
  • No more crashes due to stopped activities- If the observer’s lifecycle is inactive, such as in the case of an activity in the back stack, then it doesn’t receive any LiveData events.
  • No more manual lifecycle handling –  UI components just observe relevant data and don’t stop or resume observation. LiveData automatically manages all of this since it’s aware of the relevant lifecycle status changes while observing.
  • Always up to date data -- If a lifecycle becomes inactive, it receives the latest data upon becoming active again. For example, an activity that was in the background receives the latest data right after it returns to the foreground.
  • Manage all configuration changes --If an activity or fragment is recreated due to a configuration change, like device rotation, it immediately receives the latest available data.

Step for implementation LiveData

  • Add dependencies into our app/build.gradle file
  • Create a subclass of AndroidViewModel or ViewModel (as per instance )
  • Declare an of the mutable live data object. That takes input from the repository or other sources.
  • In Activity create an instance of LiveData and subscribes the observer

LiveData Demo App

Now, I will demonstrate how to implement LiveData in Android application. In this LiveData android example, we use Retrofit2 for Rest API calling and will fetch data from a remote server and display it on RecyclerView with the help of ViewModel and LiveData.

The following component we will using this demo application
  • LifeCycle Awareness
  • ViewModel
  • LiveData
  • Retrofit
  • RxJava we are not using because I don’t want complexity this demo
  • Glide
  • Lamda Expression

1. Create a new Project with EmptyActivity Template

In AndroidStudio, create a new project with EmptyActivity template named is LiveDataExample . Now just add dependency into the app/build.gradle .

    def lifecycle_version = "1.1.1"
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"

    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    // Glide for image loading
    implementation 'com.github.bumptech.glide:glide:4.8.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

We also use lambda expression in this project so set compile option 1.8 inside android tag in app/build.gradle.

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

2. Create Wrapper class for parsing JSON response

I will use retrofit and LiveData for getting data from remote server. The rest API endpoint is this https://androidwave.com/api/feed.json. and JSON data like below

{
  "status": "ok",
  "error":false,
  "message":"AndroidWave RSS feed found",
  "data": [
   {
      "title": "Background Limitations Android Oreo",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/background-limitations-android-oreo/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/background-limitations-android-oreo-370x247.png",
      "description": "If you are looking at what exactly limitations are applied in background service in Android in Oreo. You are in right place."
    },
   {
      "title": "ViewModel Android Example",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/viewmodel-android-example/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/viewmodel-android-example-370x247.png",
      "description": "When you are developing professional-level android application, one of the most common things we need the consider is configuration changes."
    },
   {
      "title": "Working with JobIntentService",
      "pubDate": "2019-02-09 11:22:31",
      "link": "https://android.com/working-with-jobintentservice/",
      "author": "admin",
      "thumbnail": "http://35.197.65.22/wp-content/uploads/2019/02/working-with-job-intent-service-370x247.png",
      "description": "In our previous article, I have explained the limitation of using the IntentService in Android Oreo devices. To overcome this problem android introduced JobIntentService"
    }]
}
2.1 Just create a wrapper class name is BlogWrapper and set getter setter like below
package com.wave.livedataexample.model;

import com.google.gson.annotations.SerializedName;

import java.util.List;

@SuppressWarnings("unused")
public class BlogWrapper {

    @SerializedName("data")
    private List<Blog> mData;
    @SerializedName("error")
    private Boolean mError;
    @SerializedName("message")
    private String mMessage;
    @SerializedName("status")
    private String mStatus;

    public List<Blog> getBlog() {
        return mData;
    }

    public void setBlog(List<Blog> data) {
        mData = data;
    }

    public Boolean getError() {
        return mError;
    }

    public void setError(Boolean error) {
        mError = error;
    }

    public String getMessage() {
        return mMessage;
    }

    public void setMessage(String message) {
        mMessage = message;
    }

    public String getStatus() {
        return mStatus;
    }

    public void setStatus(String status) {
        mStatus = status;
    }

}
2.2 Now create Wrapper class for Blog Item.
package com.wave.livedataexample.model;

import com.google.gson.annotations.SerializedName;

public class Blog {

    @SerializedName("author")
    private String mAuthor;
    @SerializedName("description")
    private String mDescription;
    @SerializedName("link")
    private String mLink;
    @SerializedName("pubDate")
    private String mPubDate;
    @SerializedName("thumbnail")
    private String mThumbnail;
    @SerializedName("title")
    private String mTitle;

    public String getAuthor() {
        return mAuthor;
    }

    public void setAuthor(String author) {
        mAuthor = author;
    }

    public String getDescription() {
        return mDescription;
    }

    public void setDescription(String description) {
        mDescription = description;
    }

    public String getLink() {
        return mLink;
    }

    public void setLink(String link) {
        mLink = link;
    }

    public String getPubDate() {
        return mPubDate;
    }

    public void setPubDate(String pubDate) {
        mPubDate = pubDate;
    }

    public String getThumbnail() {
        return mThumbnail;
    }

    public void setThumbnail(String thumbnail) {
        mThumbnail = thumbnail;
    }

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

}

3. Create a Repository for interacting to LiveData

For now create a java class named BlogRepository and declare mutable LiveData object like below.

public class BlogRepository {
    private ArrayList<Blog> movies = new ArrayList<>();
    private MutableLiveData<List<Blog>> mutableLiveData = new MutableLiveData<>();
    private Application application;
    
    public BlogRepository(Application application) {
        this.application = application;
    }

}
3.1 For interacting remote create a retrofit interface and expose below methods.

Now setup a retrofit with LiveData for interacting WebService.

package com.wave.livedataexample.service;

import com.wave.livedataexample.model.BlogWrapper;
import retrofit2.Call;
import retrofit2.http.GET;

public interface RestApiService {

    @GET("feed.json")
    Call<BlogWrapper> getPopularBlog();

}
3.2 Furthermore create a retrofit instance like below
package com.wave.livedataexample.service;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import static com.wave.livedataexample.BuildConfig.BASE_URL;

public class RetrofitInstance {

    private static Retrofit retrofit = null;
    public static RestApiService getApiService() {
        if (retrofit == null) {
            retrofit = new Retrofit
                    .Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit.create(RestApiService.class);
    }
}
3.3 Now Open the app/build.gradle and define BASE_URL inside the config/defaultConfig tag
 buildConfigField "String", "BASE_URL", '"https://androidwave.com/api/"'
3.4 Now open the BlogRepository and expose getMutableLiveData()
public MutableLiveData<List<Blog>> getMutableLiveData() {

        RestApiService apiService = RetrofitInstance.getApiService();
        Call<BlogWrapper> call = apiService.getPopularBlog();
        call.enqueue(new Callback<BlogWrapper>() {
            @Override
            public void onResponse(Call<BlogWrapper> call, Response<BlogWrapper> response) {
                BlogWrapper mBlogWrapper = response.body();
                if (mBlogWrapper != null && mBlogWrapper.getBlog() != null) {
                    movies = (ArrayList<Blog>) mBlogWrapper.getBlog();
                    mutableLiveData.setValue(movies);
                }
            }
            @Override
            public void onFailure(Call<BlogWrapper> call, Throwable t) {
            }
        });
        return mutableLiveData;
    }
3.5 The complete BlogRepository class looks like this
package com.wave.livedataexample.model;

import android.app.Application;
import android.arch.lifecycle.MutableLiveData;
import com.wave.livedataexample.service.RestApiService;
import com.wave.livedataexample.service.RetrofitInstance;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class BlogRepository {
    private ArrayList<Blog> movies = new ArrayList<>();
    private MutableLiveData<List<Blog>> mutableLiveData = new MutableLiveData<>();
    private Application application;

    public BlogRepository(Application application) {
        this.application = application;
    }

    public MutableLiveData<List<Blog>> getMutableLiveData() {

        RestApiService apiService = RetrofitInstance.getApiService();
        Call<BlogWrapper> call = apiService.getPopularBlog();
        call.enqueue(new Callback<BlogWrapper>() {
            @Override
            public void onResponse(Call<BlogWrapper> call, Response<BlogWrapper> response) {
                BlogWrapper mBlogWrapper = response.body();
                if (mBlogWrapper != null && mBlogWrapper.getBlog() != null) {
                    movies = (ArrayList<Blog>) mBlogWrapper.getBlog();
                    mutableLiveData.setValue(movies);
                }
            }

            @Override
            public void onFailure(Call<BlogWrapper> call, Throwable t) { }
        });
        return mutableLiveData;
    }
}

4. Create ViewModel

Create a subclass of ViewModel named MainViewModel (Main is taking from the name of an activity, such as for login activity we can use LoginViewModel ). MainViewModel class is extends AndroidViewModel. So basically two superclasses for ViewModel in Android. ViewModel and AndroidViewModel. different is only visibility of Context. AndroidViewModel has an application Context that by we are using here AndroidViewModel.

package com.wave.livedataexample.viewmodel;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;

import com.wave.livedataexample.model.Blog;
import com.wave.livedataexample.model.BlogRepository;

import java.util.List;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public class MainViewModel extends AndroidViewModel {
    private BlogRepository movieRepository;

    public MainViewModel(@NonNull Application application) {
        super(application);
        movieRepository = new BlogRepository(application);
    }

    public LiveData<List<Blog>> getAllBlog() {
        return movieRepository.getMutableLiveData();
    }


}

Now the LiveData and View Model part is ready to use.

5. Create a XML layout for blog item

In this demo we are showing the blog list over the RecyclerView. So now create a layout for blog item named blog_item.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:background="#fff"
    android:padding="8dp">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:gravity="left"
        android:includeFontPadding="true"
        android:letterSpacing="-0.02"
        android:lineSpacingExtra="5sp"
        android:text="Dagger2 Android Example"
        android:textAlignment="gravity"
        android:textColor="@color/navy"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/ivThumbnail"
        android:layout_width="0dp"
        android:layout_height="210dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:scaleType="fitXY"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle"
        tools:srcCompat="@tools:sample/avatars" />

    <TextView
        android:id="@+id/tvDescription"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:text="https://androidwave.com/dagger2-android-example/"
        android:textColor="@color/navy"
        android:textSize="14sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ivThumbnail" />

    <TextView
        android:id="@+id/tvLink"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:fontFamily="@font/montserrat"
        android:text="https://androidwave.com/dagger2-android-example/"
        android:textColor="@color/windows_blue"
        android:textSize="15sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvDescription" />
</android.support.constraint.ConstraintLayout>

6. Create a RecyclerView adapter named BlogAdapter

package com.wave.livedataexample.ui;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.wave.livedataexample.R;
import com.wave.livedataexample.model.Blog;

import java.util.List;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public class BlogAdapter extends RecyclerView.Adapter<BaseViewHolder> {
    private static final String TAG = "BlogAdapter";

    private List<Blog> mBlogList;

    public BlogAdapter(List<Blog> blogList) {
        mBlogList = blogList;
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        holder.onBind(position);
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(
                LayoutInflater.from(parent.getContext()).inflate(R.layout.blog_item, parent, false));


    }

    @Override
    public int getItemViewType(int position) {
        return 0;
    }

    @Override
    public int getItemCount() {
        if (mBlogList != null && mBlogList.size() > 0) {
            return mBlogList.size();
        } else {
            return 0;
        }
    }

    public class ViewHolder extends BaseViewHolder {

        ImageView ivThumbnail;
        TextView tvTitle;
        TextView tvDescription;
        TextView tvLink;

        public ViewHolder(View itemView) {
            super(itemView);
            ivThumbnail = itemView.findViewById(R.id.ivThumbnail);
            tvTitle = itemView.findViewById(R.id.tvTitle);
            tvDescription = itemView.findViewById(R.id.tvDescription);
            tvLink = itemView.findViewById(R.id.tvLink);

        }

        protected void clear() {
            ivThumbnail.setImageDrawable(null);
            tvTitle.setText("");
            tvLink.setText("");
        }

        public void onBind(int position) {
            super.onBind(position);

            final Blog mBlog = mBlogList.get(position);

            if (mBlog.getThumbnail() != null) {
                Glide.with(itemView.getContext())
                        .load(mBlog.getThumbnail())
                        .into(ivThumbnail);
            }

            if (mBlog.getTitle() != null) {
                tvTitle.setText(mBlog.getTitle());
            }

            if (mBlog.getDescription() != null) {
                tvDescription.setText(mBlog.getDescription());
            }

            if (mBlog.getLink() != null) {
                tvLink.setText(mBlog.getLink());
            }

            tvLink.setOnClickListener(v -> {
                if (mBlog.getLink() != null) {
                    try {
                        Intent intent = new Intent();
                        intent.setAction(Intent.ACTION_VIEW);
                        intent.addCategory(Intent.CATEGORY_BROWSABLE);
                        intent.setData(Uri.parse(mBlog.getLink()));
                        itemView.getContext().startActivity(intent);
                    } catch (Exception e) {
                        Log.e(TAG, "onClick: Image url is not correct");
                    }
                }
            });
        }
    }

}
6.1 BaseViewHolder
package com.wave.livedataexample.ui;

import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Created on : Feb 26, 2019
 * Author     : AndroidWave
 */
public abstract class BaseViewHolder extends RecyclerView.ViewHolder {

    private int mCurrentPosition;

    public BaseViewHolder(View itemView) {
        super(itemView);
    }

    protected abstract void clear();

    public void onBind(int position) {
        mCurrentPosition = position;
        clear();
    }

    public int getCurrentPosition() {
        return mCurrentPosition;
    }
}

7. Now Open the activity_main.xml and add RecyclerView inside SwipeRefreshLayout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f2f2f2"
    android:padding="8dp"
    tools:context=".ui.MainActivity">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">


        <android.support.v7.widget.RecyclerView
            android:id="@+id/blogRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>

8. Now open the MainActivity and do following operation

  1. initialize views such as SwipeRefreshLayout, RecyclerView with help of findViewById
  2. Create the instance of MainViewModel with help of ViewModelProviders
  3. Applied the getAllBlog() observer on ViewModel that observe List<Blog>>
  4. Finally, prepare BlogAadapter and set on RecyclerView
  5. At last add internet permission in manifest (<uses-permission android:name=”android.permission.INTERNET” />)

Finally, MainActivity source code seems like below

package com.wave.livedataexample.ui;

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.wave.livedataexample.R;
import com.wave.livedataexample.model.Blog;
import com.wave.livedataexample.viewmodel.MainViewModel;

import java.util.List;

public class MainActivity extends AppCompatActivity {


    RecyclerView mRecyclerView;
    SwipeRefreshLayout swipeRefresh;
    private MainViewModel mainViewModel;

    BlogAdapter mBlogAdapter;

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

        initializationViews();
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        getPopularBlog();
        // lambda expression
        swipeRefresh.setOnRefreshListener(() -> {
            getPopularBlog();
        });
    }

    private void initializationViews() {
        swipeRefresh = findViewById(R.id.swiperefresh);
        mRecyclerView = findViewById(R.id.blogRecyclerView);
    }

    public void getPopularBlog() {
        swipeRefresh.setRefreshing(true);
        mainViewModel.getAllBlog().observe(this, new Observer<List<Blog>>() {
            @Override
            public void onChanged(@Nullable List<Blog> blogList) {
                swipeRefresh.setRefreshing(false);
                prepareRecyclerView(blogList);
            }
        });
        /**
         * Replace this statement with lambda expression
         * For using set you have to set following lines in app/build.gradle
             // add below line
             compileOptions {
             sourceCompatibility JavaVersion.VERSION_1_8
             targetCompatibility JavaVersion.VERSION_1_8
             }
             // reduce line of code
             mainViewModel.getAllBlog().observe(this, blogList -> prepareRecyclerView(blogList));

         */

    }

    private void prepareRecyclerView(List<Blog> blogList) {

        mBlogAdapter = new BlogAdapter(blogList);
        if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        } else {
            mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));

        }
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setAdapter(mBlogAdapter);
        mBlogAdapter.notifyDataSetChanged();

    }

}

After following all the above step, build the project and RUN it. The output will be as expected. If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Sample Project- LiveData Android Example

Problem Statement

While you are developing professional-level android apps, one of the most common things we need the consider is configuration changes. These configuration changes are Screen Rotations, Keyboard Changes, Language changes and  Enable multi-window windows. Let us understand android ViewModel example.

The Android OS manages the lifecycle of activities, fragment and other UI controllers. Android Framework Is decide when UI controller will re-create or destroy on particular user action. In case the system destroys and re-create any activity and the UI data get lost. For better clarity let’s takes one example.

Let’s assume you have some Activity for showing the score of two players like below figure.

In this picture you show, We have used two buttons such as A and B. On these buttons, we have set onClickListener that will increase counter by one on every click. When this activity run first time and the score of both player will zero. Meanwhile, click players button, the score will increase by one in every click. Now change the orientation of phone, you see score is set 0 again. Now increase counter and again goes to portrait mode then counter is set 0 again. What happens in configuration changes.  

As per activity lifecycle, while configuration changes happen activity has to destroy and recreate with new configuration. As a result of that values created during the running period of activity will destroy. That scenario is very simple one, but this is one major problem when it comes in a large application. Let’s suppose you have to get data about 2000 product from remote list APIs. Every time configuration change happens have to call API and download data again and again. That is consumed a lot of time and resource.

ViewModel

ViewModel is best solution for this problem. it just a class for view. We usually create one view model for one activity. One activity can have many fragment, means two or more fragment can share one ViewModel. View model create in the memory when activity creates, It lives until the activity cleared from the memory.    

So ViewModel can hold value belong to the activity. As this diagram to the Google developer documentation shows from the movement app launches an activity, the activity appears on the screen. Activity has to pass three life cycle state create, start and resume. Activity has to pass onPause,onStop and onDestroy.

ViewModel starts on invoke onCreate methods. Through that time activity can recreate again and again but it instance will live in the memory holding activity data.

Summary of ViewModel

  • Live through the configuration changes- If any configuration changes the data will live in ViewModel
  • No worry about leak memory
  • Data will be always updated – If API is calling data from remote server the data will always update
  • Data will wait for you- If you call any APIs and that time you will rotate the phone and result delivered before activity recreation data will store in ViewModel and wait for the re-creation of activity

ViewModel Android Demo App

Step to Implementation ViewModel

I’m going to show you how to use ViewModel. It’s very easy you have to follow below step

  • Add dependency in build.gradle
  • Create a subclass of ViewModel
  • Expose methods for data such as getInitialCount() and getCurrentCount() for both players.
  • Write code in Activity

1. Add dependency in build.gradle

For implementing ViewModel Android example you have go to file menu and create a new project. open build.gradle and add below line of code

    def lifecycle_version = "1.1.1"
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"

2. Create a subclass of ViewModel

Now create a new class with named is MainViewModel which extends ViewModel class and expose below methods.

package com.wave.viewmodelexample;
import androidx.lifecycle.ViewModel;
public class MainViewModel extends ViewModel {
    private int clickCountA, clickCountB = 0;
    public int getInitialCountA() {
        return clickCountA;
    }
    public int getInitialCountB() {
        return clickCountB;
    }
    public int getCurrentCountA() {
        clickCountA += 1;
        return clickCountA;
    }
    public int getCurrentCountB() {
        clickCountB += 1;
        return clickCountB;
    }
}

3. Expose Methods

Now open MainActivity and add below line of code

package com.wave.viewmodelexample;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    MainViewModel mainViewModel;
    TextView tvScoreA, tvScoreB;
    Button btnPlayerA, btnPlayerB;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        /**
         * initialized ViewModel
         */
        mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
        // set initial counter from view model
        tvScoreA.setText(String.valueOf(mainViewModel.getCurrentCountA()));
        tvScoreB.setText(String.valueOf(mainViewModel.getCurrentCountB()));
    }
    private void initView() {
        // initialized all views here
        tvScoreA = findViewById(R.id.tvScorePlayerA);
        tvScoreB = findViewById(R.id.tvScorePlayerB);
        btnPlayerA = findViewById(R.id.btnPlayerA);
        btnPlayerB = findViewById(R.id.btnPlayerB);
        btnPlayerA.setOnClickListener(this);
        btnPlayerB.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnPlayerA:
                //set current counter from view model
                tvScoreA.setText(String.valueOf(mainViewModel.getCurrentCountA()));
                break;
            case R.id.btnPlayerB:
                //set current counter from view model
                tvScoreB.setText(String.valueOf(mainViewModel.getCurrentCountB()));
                break;
            default:
        }
    }
}

Finally, following all these steps just RUN the project, The output will be as expected, the counter will not reset zero on configuration change, such as here.

This is a basic ViewModel Android example. If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Sample Project – ViewModel Android Example