Android App Architecture

Source: Internet
Author: User

The pace of the Android development ecosystem is very fast. Every week, new tools are born, libraries are updated, blogs are published, and technology is explored. If you're out on vacation for one months, you may have released a new version of support library or play Services when you came back

I've been working with Ribot team for Android for over three years. During this time, the architecture and technology of Android applications we have built are evolving. This article will explain to you our experience, errors, and the reasons behind the changes in the architecture.

The architecture of the past

Dating back to the 2012 our code base uses the basic structure, and at that time we didn't use any third-party network class libraries, and we were AsyncTask good friends. The architecture at that time could be broadly expressed as.

The code is divided into two tiers: the data layer, which is responsible for retrieving and storing data from the rest API or the persistent data store, and the view layer's responsibility is to process and present the data on the UI.

Apiprovider provides methods that enable activity and fragment to easily interact with the rest API's data. These methods use URLConnection and Asynctask to execute a network request within a separate thread, and then return the result to the activity through a callback.

In the same way, Cacheprovider contains methods for retrieving and storing data from sharedpreferences and SQLite databases. Also use callbacks to return the results back to the activity.

Problems that exist:

The main problem with this structure is that the view layer holds too much responsibility. Imagine a simple and common scenario where the app needs to load a list of blog posts, then cache the entries to the SQLite database, and finally show them to a list view such as the ListView. Activity to do the following steps:

    1. Call method via Apiprovider loadPosts (callback)

    2. Wait for the callback result of the Apiprovider, and then call the method in the Cacheprovider savePosts (callback)

    3. Wait for the callback result of the Cacheprovider, and then present the articles to a list view such as a ListView

    4. Handles the potential exceptions in the Apiprovider and Cacheprovider callbacks, respectively.

This is a very simple example where the data returned by the rest API in the actual development environment may not be directly required by the view. As a result, activity has to transform or filter the data in some way before it is shown. Another common scenario is to call the required loadPosts( ) parameters, which need to be obtained in advance from other places, such as requiring the play Services SDK to provide an email address parameter. Just as the SDK returns an email address via an asynchronous callback, which means that we now have at least three layers of nested callbacks. If you continue to add complex business logic, this architecture will fall into the well-known callback Hell (Callback Hell).

Summarize:

    • Activitty and fragment become very large and difficult to maintain.

    • Too many callback nesting means ugly code structure and is not easy to read and understand. If you make changes on this basis or add new features, you will feel very miserable.

    • Unit testing becomes very challenging and, if possible, because many of the logic is left in the activity or fragment, it's hard to do unit testing.

The new architecture of the Rxjava drive

We use the organizational structure mentioned above for almost two years. During that time, we made some improvements that eased the problem slightly. For example, we have added helper Class (helper classes) to reduce the code in activity and fragment, using volley in Apiprovider. Despite these changes, the code of our application cannot be tested in a friendly way, and the problem of callback Hell (callback Hell) often occurs.

Until 2014 we began to understand Rxjava. After trying a few sample projects, we realized that she might eventually help us solve the problem of nested callbacks. If you are not yet familiar with responsive programming, you can read this article (Translator Note: The target point here for those years we missed the responsive programming). In short, Rxjava allows data to be processed by an asynchronous stream, and provides many operators that you can manipulate on a stream for transformations, filtering, or merging data.

Considering the pain of the previous years, we began to consider what a new application architecture would look like. So, we came up with this.

Similar to the first architecture, this architecture is also divided into data layer and view layer. Data layer holds DataManager and a series of helper classe. The View layer is made up of Android's framework components, such as Fragment,activity,viewgroup.

Helper classes (the third column in the icon) has a very special responsibility and a simple way to implement it. For example, many projects require some help classes to access the rest API, read data from a database, or interact with a three-party SDK. Different applications have different numbers of help classes, but there are some commonalities:

    • Preferenceshelper: from SharedPreferences reading and storing data.

    • Databasehelper: processing operation SQLite database.

    • Retrofit Services: Performing the access rest API, we now use Retrofit instead of volley because it is inherently support for Rxjava. And it's better to use.

Most of the public methods inside the helper class will return Rxjava Observable .

DataManager is the brain in the entire architecture. It extensively uses Rxjava operators to merge, filter, and transform the data returned from the helper class. DataManager is designed to reduce the workload of activity and fragment, and they (the translator notes: Activity and Fragment) do is to show the prepared data without needing to convert.

The following code shows the possible appearance of a DataManager method. This simple example method is as follows:

    1. Call the retrofit service to load a list of blog posts from the rest API

    2. Use Databasehelper to save the article to the local database, to achieve the purpose of caching

    3. Filter out the blogs that are published today, because that's what the view layer wants to show.

      Public Observable loadtodayposts () {
      Return mretrofitservice.loadposts ()
      . Concatmap (New func1<list, observable> () {
      @Override
      Public Observable Call (List apiposts) {
      Return mdatabasehelper.saveposts (apiposts);
      }
      })
      . Filter (New Func1<post, boolean> () {
      @Override
      Public Boolean call (Post post) {
      Return IsToday (post.date);
      }
      });
      }

Components such as activity or fragment in the view layer simply call this method, and the subscription returns Observable . Once the subscription is complete, different blogs sent through observable can be added to the adapter immediately to show up on Recyclerview or other similar controls.

The last element of this architecture is event bus. It allows us to send events in the data layer so that multiple components in the view layer can subscribe to these events. For example, the logout method in DataManager can send an event, and multiple activity that subscribes to the event can change their UI view after receiving the event, thus displaying a logout status.

Why is this architecture better?

    • Rxjava's observable and operators prevent nested callbacks from appearing.

    • DataManager took over part of the previous view layer's responsibilities. As a result, it makes activity and fragment a lighter amount.

    • The transfer of code from activity and fragment to DataManager and helper classes means that writing unit tests can be made simpler.

    • Clear separation of duties and DataManager as the only point of interaction with the data layer, making this architecture test-friendly. Help classes and DataManager can be easily simulated.

What problems do we have?

    • For large and complex projects, DataManager can become very bloated and difficult to maintain.

    • Although the view layer components such as activity and fragment become lighter, they allow a lot of logic to be handled, such as managing Rxjava subscriptions, parsing errors, and so on.

Integrated MVP

Over the past year, several architectural design patterns, such as MVP or MVVM, have become increasingly popular within the Android community. by exploring in [Sample project] and articles, we found that MVP could bring valuable improvements to our existing architecture. Since our architecture is now divided into two layers (view layer and data layer), it is more natural to add the MVP. We just need to add a new presenter layer and then transfer some of the code from view to presenter.

The left data layer remains the same, but in order to maintain consistency with this pattern, it is now called model.

Presenter is responsible for loading data from the model and then invoking the corresponding method in view when the data is ready. Also responsible for subscribing to the observable returned by DataManager. So, they also need to deal with schedulers and subscriptions. In addition, they can parse the error code or provide additional operations for the data flow if needed. For example, if we need to filter some data and this same filter is not likely to be reused elsewhere, then it might be more meaningful to implement in presenter than in DataManager.

Below you will see what a public method will look like in presenter. This code subscribes to the returned observable that we defined in the previous section dataManager.loadTodayPosts( ) .

123456789101112131415161718192021222324 public void loadtodayposts() { mmvpview. Showprogressindicator(true); msubscription = mdatamanager. Loadtodayposts(). ToList()             . Observeon(androidschedulers. Mainthread())             . Subscribeon(schedulers. Io())             . Subscribe(new subscriber<List<Post>>() { @Override Public void oncompleted() { mmvpview. Showprogressindicator(false);                 } @Override Public void onError(throwable e) { mmvpview. Showprogressindicator(false); mmvpview. ShowError();                 } @Override Public void onNext(List<Post> postslist ) { mmvpview. Showposts(postslist);                 }            });    }

mMvpViewis a view component that is assisted with presenter. Typically, it is an Activity Fragment ViewGroup instance of, or.

Like the previous architecture, the View layer holds standard framework components, such as viewgroup,fragment or activity. The main difference is that these components are no longer subscribed directly to observable. Instead, by implementing the Mvpview interface, and then providing some neat method functions, for example, showError( ) or showProgressIndicator( ) . This view component is also responsible for handling user interactions, such as clicking Events and invoking the correct method in the corresponding presenter. For example, I have a button to load a list of blogs that the activity will call in the listener of the Click eventpresenter.loadTodayPosts( )

If you want to see a complete example of working with the MVP basic architecture, you can check out our Android boilerplate project from GitHub. You can also read more about it here. Architecture Guidance for Ribot

Why is this architecture better?

    • Activity and fragment become very lightweight. Their only responsibility is to create/update the UI and handle user events. As a result, they become easier to maintain.

    • Now we can easily write unit tests by simulating the view layer. Previously the code was part of the view layer, so it was difficult to unit test it. The entire architecture becomes test-friendly.

    • If DataManager becomes bloated, we can alleviate this problem by transferring some code to presenter.

What problems are we still having?

    • A single DataManager is still a problem when the codebase becomes very large and complex. Although we have not yet come to this point, it is a truly noteworthy issue that we have been aware of and it may happen.

It is worth mentioning that it is not a perfect architecture. In fact, don't be naïve to think that this is a unique and perfect solution that solves all your problems. The Android ecosystem will keep pace with rapid development and we must continue to explore. Keep reading and trying so we can find a better way to continue building good Android apps.

Android App Architecture

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.