Codementor Events

Billion dollar tips for every Android developer

Published Jan 10, 2018Last updated Jul 09, 2018
Billion dollar tips for every Android developer

Architectural Patterns

One of the most important things that you should focus on while developing a world-class app is the separation of concerns. It is good to separate and define clear roles for each component in your app. A class shouldn’t be a multi-tasking component. Many developers make a mistake of writing almost all their code in an Activity or Fragment. Any code that doesn’t handle a UI or operating system interaction should not be in those classes.

Having a good design pattern saves you lots of problems. A good pattern ensures that all your code is organized and thoroughly covered by Unit tests. It makes debugging easier, since you know where to look for a particular problem. It also greatly improves the maintainability of your code.

There several architectural patterns, but the most popular being;

  • MVC (Model View Controller)
  • MVP (Model View Presenter)
  • MVVM (Model View ViewModel)

I’m going to casually introduce each of the above architectural patterns.

i. MVC (Active Model)

MVC is commonly known as active model. The model notifies the view, when it is changed by the controller.


credit: codeproject.com

It also notifies other classes through the help of the Observer pattern. The Model contains a collection of observers that are interested in updates. The View implements the observer interface and registers as an observer to the Model.


credit: upday.github.io

Model: This is the data layer. It is responsible for managing the business logic and network or database API requests.

View: This is the UI layer. Its responsible for rendering the user interface. It is responsible for visualizing data from the data model.

Controller: This is the logic layer. It gets notified of the user’s behavior and updates the view accordingly. It is also in charge of updating the model as needed.
For more information about MVC, this blog has you covered.

ii. MVP (Model View Presenter)

This is also known as the passive model. The presenter is the only class that manipulates the model. The model doesn’t not notify the view when it is changed by the presenter. Notification is done by the presenter. So the model is referred to as inactive or passive.


Model-View-Controller — passive Model — behavior (credit: upday.github.io)

Model: This is similar to what we have defined above. It is the data layer responsible for managing the business logic and network or database API requests.

View: This is responsible for presenting data in a way decided by the presenter. The view is usually implemented by Activities or Fragments.

Presenter: The presenter acts as a link between the model and the view. All presentation logic must be defined here. It is responsible for querying the model and updating the view. It is also responsible for reacting to user interactions and updating the model accordingly.

This architectural pattern aims at ensuring that the view does as little work as possible. In other words, we have to keep the view as dumb as possible.

The presenter should also be framework-independent and shouldn’t depend on any Android classes. It should be a simple POJO class.

The backbone of this architectural pattern is an interface called a contract that describes the communication between the view and the presenter. It cleans up the interactions between your view and presenter.

An example of MVP implementation;

Here are some sample code snippets from a simple stories App.

The contract file is an interface file that contains the presenter and view interfaces. The view interface provides methods that are responsible for drawing the user interface, whilst the presenter interface contains methods that are responsible for the doing logic that is not dependent on the Android framework. It also contains methods that fetch data to be displayed on the user interface.

Below is how the contract file StoriesFragmentContract is structured.

public interface StoriesFragmentContract {
  interface View {
    // methods for rendering the UI
    void showLoading();
    void hideLoading();
    void displayStories(List<Story> storiesList);
    void displayNoStories();
    void showError();
  }

  interface Presenter {
    // methods that contain business logic
    void setView(StoriesFragmentContract.View view);
    void getAndDisplayStories();
    void pullToRefreshStories();
  }
}

StoriesFragmentContract.java

The presenter file implements the Presenter interface. The presenter interface defines the business logic of the application.

public class StoriesFragmentPresenter implements StoriesFragmentContract.Presenter {
  private StoriesFragmentContract.View mView;
  
  @Override public void setView(StoriesFragmentContract.View view) {
    mView = view;
  }

  @Override public void getAndDisplayStories() {
    List<Story> storiesList = new ArrayList<Story>();
    view.showLoading();
    
    // Logic for getting stories
    
    view.displayStories(storiesList);
    view.hideLoading();
  }

  @Override public void pullToRefreshStories() {
    // Logic for updating displayed stories
  }
}

StoriesFragmentPresenter.java

Finally, we have a Fragment (StoriesFragment) which is responsible for displaying our stories. This fragment implements the view interface, which is responsible for drawing the android UI.

public class StoriesFragment extends Fragment implements StoriesFragmentContract.View {

  // declare your layout widgets
  private RecyclerView recyclerView;
  private ProgressBar progressBar;
  private LinearLayout error_layout;
  
  private StoriesAdapter adapter;
  private StoriesFragmentPresenter presenter;

  public StoriesFragment() {
    // Required empty public constructor
  }

  public static StoriesFragment newInstance() {
    StoriesFragment fragment = new StoriesFragment();
    return fragment;
  }

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // inflate the fragment layout view
    View view = inflater.inflate(R.layout.fragment_stories, container, false);
    // provide view to the presenter
    presenter.setView(this);
    return view;
  }

  @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // Code for UI Binding
    recyclerView = (RecyclerView) view.findViewById(R.id.stories_recycler);
    progressBar = (ProgressBar) view.findViewById(R.id.storiesLoadingProgressBar);
    error_layout = (LinearLayout) view.findViewById(R.id.stories_error_layout);
    recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    
    // Call to the presenter to provide stories
    presenter.getAndDisplayStories();
  }

  @Override public void showLoading() {
    progressBar.setVisibility(View.VISIBLE);
  }

  @Override public void hideLoading() {
    progressBar.setVisibility(View.GONE);
  }

  @Override public void displayStories(List<Story> storiesList) {
    recyclerView.setVisibility(View.VISIBLE);
    adapter = new StoriesAdapter(storiesList, getContext());
    recyclerView.setAdapter(adapter);
  }

  @Override public void displayNoAssignments() {
    error_layout.setVisibility(View.VISIBLE);
  }

  @Override public void showError() {
    error_layout.setVisibility(View.VISIBLE);
  }

}

StoriesFragment.java

However, the above is a very simple and basic example of the MVP architectural pattern. you can refer to this blog post for further details about the same.

iii. MVVM (Model View ViewModel)

This is a very useful pattern that was introduced together with the Android Architecture Components.
With the help of the ViewModel class, this pattern enables your app to survive configuration changes that come up as a result of device orientation changes.
This is because it combines the advantages of separation of concerns as provided by MVP while leveraging the advantages of data bindings. With this pattern, the model takes care of as many operations as possible while minimizing the logic in the view.

Unlike in MVP where the presenter holds a reference to the view, the ViewModel doesn’t need to hold a reference to the view, because it exposes streams of events to which the Views can bind. The View however, has a reference to the ViewModel, but the ViewModel has no information about the view, hence the principle; The consumer of the data should know about the producer, but the producer (ViewModel) doesn’t know about who consumes the data.

The main players herein are;


credit: en.wikipedia.org

Model: This is also referred to as the DataModel. Its main purpose is to abstract the data source. The ViewModel works with the DataModel to get and save data. This is the component in charge of business logic.

View: The job of this component is to inform the ViewModel about the user’s action. It is also responsible for drawing the UI. In the Android framework, this can be an Activity or Fragment. This shouldn’t have any logic. Any underlying logic should be handled by the ViewModel.

ViewModel: This component is responsible for exposing streams of data relevant to the View. It retrieves data from the Model, applies the UI logic and then provides the relevant data for the view to consume.

The biggest plus on the above pattern is that it makes your app very easy to test. Unit tests can be easily written for the ViewModel and Espresso tests for the View are also easy to do. This is a broad pattern which can’t be wholly introduced here. More information about the same can be found here.

Libraries

Ben Jakuben, in his post about Android Libraries clearly states that;

“A good developer knows to never reinvent the wheel, unless you plan on learning more about wheels!”

There are tons of open source libraries that developers can freely use in their apps. Some of these are so helpful that you shouldn’t start any App without them. One line of code in your app’s build.gradle is sufficient to include any library of choice automatically.
Below, I’m going to casually introduce some must know libraries that make your Android development life fun and much easier.

1. Retrofit


credit: tsurutan.com

This is my favorite library. It is the best library for making REST API calls. It makes HTTP Requests easy through annotations and automatically handles JSON parsing through the use of POJO classes.
All you need to do is to define an API interface and a Retrofit class that automatically generates an implementation of your API interface. In your interface, you use annotations to describe the HTTP request.

A simple example of an API interface is;

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

GitHubService.java

The retrofit class that generates an implementation of the GithubService interface shall look like this;

public class GithubApi {
  
  // Constructor to create the GithubApi
  public GithubApi() {
    this.createApi();
  }
  
  // Method that creates the Retrofit object
  private void createApi() {
    Gson gson = new GsonBuilder().create();
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create(gson));
        .build();
    GitHubService service = retrofit.create(GitHubService.class);
  }
  
}

GithubApi.java

I won’t dive so deep into the intricacies of the Retrofit implementation. You can however go read about it from here;

2. GSON


credit: welbornmachado.com

In the Retrofit example above, I introduced GSON while creating the Retrofit object. I guess you wondered why! 
GSON is a library that coverts Java Objects (POJOs) into their JSON representation. It can also be used to convert a JSON string to an equivalent Java Object. The library provides simple toJson() and fromJson() methods to convert Java objects to JSON and vice versa. 
However, when using the Retrofit library, you never have to make direct calls to GSON. The Retrofit class handles all that automatically when you add GSON as the converterFactory. More information about the same can be found here.

3. ButterKnife


credit: appcodelibrary.com

Butterknife uses annotations to inject views by creating boilerplate code for you. It is small, simple and lightweight. It makes your code more readable on top of being easy to use.

Typically, you would call references to views in your Activity or fragment like so;

public class MainActivity extends AppCompatActivity {
  
  private TextView mWelcomeLabel;
  private EditText mUsernameField;
  private EditText mPasswordField;
  private Button mSubmitButton;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    mWelcomeLabel = (TextView) findViewById(R.id.welcomeLabel);
    mUsernameField = (EditText) findViewById(R.id.usernameField);
    mPasswordField = (EditText) findViewById(R.id.passwordField);
    mSubmitButton = (Button) findViewById(R.id.submitButton);
  }

}

MainActivity.java

However, when we use Butterknife, our code becomes much more readable and concise. The same Activity code shall hence look like this;

public class MainActivity extends AppCompatActivity {
  
  @BindView(R.id.welcomeLabel) TextView mWelcomeLabel;
  @BindView(R.id.usernameField) EditText mUsernameField;
  @BindView(R.id.passwordField) EditText mPasswordField;
  @BindView(R.id.submitButton) Button mSubmitButton;

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

}

MainActivity.java (with ButterKnife)

For more information about ButterKnife refer to the Butterknife documentation here.

4. Dagger2


credit: Mert Şimşek

Dagger2 is a dependency injection framework, built on a simple concept called Inversion of Control. According to that concept, a class should not configure its dependencies statically, but should be configured from the outside. In Java, a class has a dependency on another class, if it uses an instance of that class. This is referred to as class dependency.

Since dependency injection in most world class Apps is a must know, Dagger2 is a true solution for problems that may arise during providing dependencies to particular classes. Ideally, classes should be as independent as possible from other classes.

Dagger2 uses code generation and is based on annotations. The generated code is very easy to read and debug. The annotations used are; @Module, @Provides, @Inject and @Component among others.

For more information about dependency injection, Vogella has got you covered here.

5. Glide


credit: ssaurel.com

If you have used Picasso before, trust me, Glide is a better option for fast and efficient image loading.
Glide is a fast and efficient media management and image loading framework that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.

In simple terms, Glide takes up the burden of managing your media files especially if you are loading files from a network to itself. It automatically manages memory consumed by the image loading process and also automatically does caching for you, in a guise of reducing the pinch on network resources.

An image or drawable resource can easily be loaded into an image view by just calling;

GlideApp .with(CONTEXT) .load(URL) .into(IMAGE_VIEW);

Glide supports fetching, decoding, and displaying of video stills, images, and animated GIFs. For more information about the same, refer to Glide’s Comprehensive documentation here.

6. Room


credit:cdn-images

In order to build world-class apps, you need a clean ORM mapping library. Room was introduced during Google I/O 2017 as a persistence library aimed at cleaning up architecture in developing Android Apps.

Room in simple terms is one of the best SQLite object mapping libraries. It provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

Persisting data offline greatly improves the experience of your users in cases where the internet connection is saggy. The goal of every app developer is to ensure that users have a seamless experience when using your App. Room hence bridges the gap by providing the easiest mechanism for data persistence.

@Entity(tableName = "user")
public class User {
  @PrimaryKey(autoGenerate = true)
  private int userID;
  
  @ColumnInfo(name = "first_name")
  private String firstName;
  
  @ColumnInfo(name = "last_name")
  private String lastName;
  
  @ColumnInfo(name = "email")
  private String email;
  
  // getters and setters
  
}

User Model Class

You can find more information about Room here.

7. RxJava


credit: mytrendin.com

RxJava is one of the best libraries for enabling Reactive Programming in Android development. It is a framework for simplifying concurrency or asynchronous tasks. Multi-threading most times is a pain to developers and if not correctly implemented, it can cause some very difficult bugs to fix. RxJava comes in to make your life easier and help you avoid the nasty memory leaks.

Consider a case where you want to obtain data over the network and update the UI. One approach to achieve that is to use an inner AsyncTask subclass in our Activity or Fragment.

public class MainActivity extends AppCompatActivity {
  
  // class attributes
  private Button mButton;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // activity configurations
    mButton = (Button) findViewById(R.id.user_details_btn);
    
    // make call to asyncTask
    mButton.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        new GetDataTask(1001).execute();
      }
    });
  }
  
  public class GetDataTask extends AsyncTask<Void, Void, User> {
    private final int userId;
    
    public GetDataTask(int userId) {
      this.userId = userId;
    }
    
    @Override protected User doInBackground(Void... params) {
      return userService.getUserData(userId);
    }
    
    @Override protected void onPostExecute(User user) {
      // do something after getting user data
    }
  }
  
}

Making a network call with an AsyncTask

The above seems like a safe approach, but it is not. It has some issues and limitations. Memory/context leaks are easily created by the above approach since GetDataTask is an inner class and holds an implicit reference to the outer class. Notwithstanding, if you wanted to perform another long operation with the returned data, that may result in nesting another AsyncTask which greatly reduces readability.

However, with RxJava, performing the same network call may look like this;

public class MainActivity extends AppCompatActivity {
  // class attributes
  private Subscription mSubscription;
  private Button mButton;
  
  @Override 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // activity configurations
    mButton = (Button) findViewById(R.id.user_details_btn);
    
    // make a network call
    mButton.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        mSubscription = userService.getObservableUser(1001)
          .subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Action1<User>() {
            @Override public void call(User user) {
              // do something with the user data
            }
          });
      }
    });
  }
  
  @Override protected void onDestroy() {
    if (mSubscription != null && !mSubscription.isUnsubscribed()) {
      mSubscription.unsubscribe();
    }
    super.onDestroy();
  }

}

Network call with RxJava

The RxJava approach solves the problem of potential memory leaks that may be a result of running a thread holding a reference to the outer context, by keeping a reference to the returned Subscription object.

For more information about RxJava you can refer to this awesome piece.

In order to keep the article short enough, I’ll stop with the above libraries. But, you can read about; Ciceron, a nice navigation library, SlimAdapter a lightweight, slim, clean and typeable adapter without a need for a ViewHolder, and Spruce Android Android Animation library among many.

Tests

Developing an App without Tests is like driving at 100mph without a seatbelt.

From the look of things, everything may seem fine and okay till you create a billion dollar bug.

In order to ensure quality Apps and a peace of mind when you launch your production ready apps, it is very important that you write sufficient unit tests for your business logic and espresso tests for UI tests. These tests save you from lots of unseen precedencies that may result in large uninstalls of your app due to unforeseen crashes. A thoroughly tested app gives you confidence that your app functions as expected.

Here is an espresso cheat sheet that shall save you tons of time when writing your UI tests.

Do’s & Don’ts


credit: tech.co

Do’s,

  1. Design for multiple screens
  2. Consider supporting multiple languages
  3. Provide your users with a seamless user experience
  4. Do performance tests
  5. Use user analytics tools
  6. Do Performance & Memory usage monitoring

Don’ts

  1. Develop for you device
  2. Reinventing the wheel
  3. Not using intents
  4. Not using fragments
  5. Blocking the main thread
  6. Not assuming success

Those are what I deemed as Billion $ tips for every Android developer. Hope you had a good read.

Discover and read more posts from John Paul Seremba
get started
post commentsBe the first to share your opinion
Show more replies