Loading...
Android

Great Android App Architecture [ Part 2 – View model ]

As discussed in last part here , we will start with our little project. It is efficient to separate out view data ownership from UI controller logic. View model is responsible for preparing data for UI. Our basic requirement is the app which will fetch me a new Quote from mentioned category every time I open it. But while fetching, we should not be kept waiting and instead be presented with the last quote which we fetched. Slow internet can potentially become long lasting quote fetching operation and irritating for user if shown empty UI. Plus we need a mechanism by which we can refresh the quote from app by pushing the content down. While doing all this, we need to make sure that our code is maintainable, have proper architecture and testable!

 

I am assuming at this time that you know the basics of Android Development like how to create new Android Project in android studio and the file structure of the same. If you don’t, I would suggest you visit this link and familiarize yourself with Android project structure and gradle builds.

Getting started with Android App Development


Create new Android Project by following this guide. You can choose any activity you like, I am choosing bottom navigation activity. We will create package structure as shown below for keeping our code organized.

view model

I have divided code into different packages as explained below,

  • Activity : This package will contain all the activities and fragments source files.
  • Constants : This package will contain all the constants we will use throughout our code base.
  • View Model : This package will contain our view model. Business logic will come under this package.
  • OnlineData : This package will contain source files related to data fetching from REST API.
  • PersistentData : This package will contain source related to offline cached data.
  • POJO: Plain Old Java Objects

Later on, we will add more packages as code base grows like adapters, network etc.

Now you can argue that this is not the right package classification but hey, programming is creative field and you are always welcome to try out new things and suggest me as well!


Below are our starting implementations. Please note even though I have given code fragments here, I would recommend not to copy those from here but instead from github (by forking the project). Reason being, there might be syntax error in these codes and code is written with proper comments on git. Plus you would be able to contribute there as well.

We need Quote and Author for our UI. This is simple POJO class Quote which holds our quote data. Getters and Setters for author and category are omitted for brevity. But you need to include them while writing this code.

public class Quote{

    private String quote;
    private String author;
    private String category;

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = quote;
    }
}

We will create a QuoteViewModel based on the View Model class to keep this information.

A ViewModel provides the data for a specific UI component, such as a fragment or activity, and handles the communication with the business part of data handling, such as calling other components to load the data or forwarding user modifications. The View Model does not know about the View and is not affected by configuration changes such as recreating an activity due to rotation.


public class QuoteViewModel extends ViewModel {
    private LiveData<Quote> quote;  //pojo object
    private String category;        //quote category
    private int count;              //number of quotes
    private QuoteRepository quoteRepository; //data fetching object

    public void init(String category, int count){
        //make sure quote object should be created only once
        if(this.quote!=null){
            return ;
        }
        this.category = category;
        this.count=count;
        quoteRepository=new QuoteRepository();
        quote = quoteRepository.getQuote(category, count);
    }

    public LiveData<Quote> getQuote(){
        return quote;
    }

    //will be used when user manually wants to load new quote
    public void  refreshData(){
        quote = quoteRepository.getQuote(category, count);
    }
}

We have wrapped our Quote in LiveData.

LiveData is an observable data holder. It lets the components in your app observe LiveData objects for changes without creating explicit and rigid dependency paths between them. LiveData also respects the lifecycle state of your app components (activities, fragments, services) and does the right thing to prevent object leaking so that your app does not consume more memory. So no more Out of Memory Error (duh!)

So whenever quote data is changed, fragment will be notified to update it UI. (only if fragment is active, because we are using Lifecycle aware component i.e. LiveData)


In our fragment, we will override two methods onCreateView and onActivityCreated.

We are inflating fragment view from fragment_quote.xml.

We are obtaining instance of our QuoteViewModel from ViewModelProviders class. We are sending context of fragment as argument to ViewModelProvider, so even if activity is destroyed and recreated (while auto-rotation), we will get the same QuoteViewModel object. You can learn more about scope of View Model object here.

view model

As you can see from above figure, ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider (fragment in our case) when getting the View Model. The View Model stays in memory until the Lifecycle scope goes away permanently—in the case of an activity, when it finishes; in the case of a fragment, when it’s detached.

public class FragmentQuote extends LifecycleFragment{
    private QuoteViewModel quoteViewModel;
    private TextView quotetv;
    private TextView authortv;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_quote,container,false);
        quotetv = (TextView) view.findViewById(R.id.quote);
        authortv = (TextView) view.findViewById(R.id.author);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        quoteViewModel = ViewModelProviders.of(this).get(QuoteViewModel.class);
        quoteViewModel.init( getArguments().getString(Constants.stringConstants.CATEGORY_ID), Constants.quoteParam.count);
        quoteViewModel.getQuote().observe(this, new Observer<Quote>() {
            @Override
            public void onChanged(@NonNull Quote quote) {
                quotetv.setText(quote.getQuote());
                authortv.setText(quote.getAuthor());
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/quote"
        android:gravity="center"
        android:textSize="20sp"
        android:layout_margin="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

    <TextView
        android:id="@+id/author"
        android:gravity="center"
        android:textStyle="italic"
        android:layout_margin="10dp"
        android:textSize="18sp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

</LinearLayout>

I have delegated work of getting actual quote (either from Internet or SQLite) from QuoteRepository.java

Even though it may seem little overkill to have different class for such a small task but it helps keeping the separation of concern principle strong. And plus it will help once our code base grows. We will discuss more about API we are using to fetch the quote and use of Retrofit library along with QuoteRepository class in next article.

 

I will be writing more articles about tips and tricks for making most of your android application in future, subscribe The Tech Guru newsletter for getting the articles directly in your inbox. Subscription box can be found on top right side of this article of you are on PC and at the bottom of this article if you are on mobile device.

7 comments
  1. Werner Wehmann

    Hi there! Someone in my Myspace group shared this site with us so I came to give it a look. I’m definitely enjoying the information. I’m bookmarking and will be tweeting this to my followers! Terrific blog and terrific design.

  2. important site

    I simply want to tell you that I’m newbie to blogging and site-building and absolutely liked this web blog. Very likely I’m going to bookmark your site . You surely come with outstanding well written articles. Bless you for sharing with us your blog.

Leave a Reply

Your email address will not be published. Required fields are marked *