During my years of learning and growth, I gradually realized that it is very difficult and painful to build an excellent Android development framework. It not only needs to meet the growing business needs, but also needs to ensure that the framework itself is clean and extensible, which makes things very challenging. But we have to do this. Because a robust Android development framework is the foundation of a good APP.

Why do I need a framework?

In the early stage of development, we often do not need any Framework, because the good fault tolerance of Android Framework helps us avoid many problems, and even you can write a relatively perfect APP without in-depth study. A few simple Material Design-style interfaces plus some data will allow anyone to become an Android developer, but is that really enough?

Of course not!!

As our project more and more large, various problems and confusion of data storage, access, flexibility, high enough code that will be our late projects, one of the biggest obstacles to allow the free development of consequence is that a bad project, it will be difficult for us to add new functions, only for refactoring even overthrow redo it. We should not underestimate the complexity of an application before we begin programming.

In addition, in the field of software engineering, there are always some principles worth learning and abiding by, such as: single responsibility principle, dependency inversion principle, avoid side effects and so on. The Android Framework doesn’t force us to follow these principles, or it doesn’t impose any restrictions on us. Think of tightly coupled implementation classes, activities or fragments that handle a lot of business logic, EventBus everywhere, Unreadable data flows, chaotic callback hell, and so on, won’t cause the system to crash right away, but as the project grows, they can become difficult to maintain or even hard to add new code, which can be a terrible impediment to business growth.

Therefore, it is very important for developers to have a good architectural guidance specification.

Choice of architecture

There are a lot of articles about MVVM, MVP, MVC, AndroidFlux selection and analysis on the web, but I won’t describe them here. If you are interested, you can check out my Android refactoring tour: In architecture, we finally choose MVP as our development architecture. MVP has many advantages, but we finally choose it because it is easy for ordinary developers to use, and it can clearly plan the business boundary of our Activity.

Refused God Activity

Over the years of development, you’ve often seen activities with thousands of lines of code that can do anything:

  • Redefine the life cycle
  • To handle the Intent
  • Data update
  • thread
  • Basic business logic…… Even with BaseActivity defining every conceivable subclass variable and so on, it is now truly “God,” a handy and omnipotent God! As the project grows, it becomes too big to add any more code, so you write lots and lots of help classes to help the God slim down:

god activity

Of course, some people will separate out different abstract classes based on different business functions, but they are still versatile compared to that business scenario.

Regardless of the reason for the creation of “god” should be avoided, we should not focus on writing those a lot of classes, but the effort to write the ease of maintenance and testing of low coupling class, if you can, it is best not to let the business logic into pure Android world, this also is I have been trying to target.

Clean architecture and The Clean rule

The circles that look like “crust” are called Clean Architecture. The different colored “rings” represent different system structures that make up the whole system, and the arrows represent dependencies.

  • Hierarchical principle
  • Rely on the principle of
  • Abstract principle

So let me explain what I think these principles are and why.

Hierarchical principle

First of all, the framework should not limit the specific layering of the application, but from the perspective of collaborative multiplayer development, I usually divide Android into three layers:

  • Outer layer: Event Guide layer (View)
  • Intermediate layer: interface adaptation layer (generally generated by Dagger2)
  • Inner layer: Business logic layer It’s easy to think of the MVP structure by looking at the three layers above. Here’s what they contain.

Event guidance layer

It is mainly responsible for the direction of View events, such as onClick, onTouch, onRefresh, etc., and is responsible for passing events to the business logic layer.

Interface adaptation layer

The purpose of the interface adaptation layer is to connect business logic to framework-specific code and act as a bridge between the outer layer and the inner layer, which is typically generated using Dagger2.

Business logic layer

The business logic layer is the most important part of the framework, where we solve all the business logic. This layer should not contain code for event direction and should be able to be tested independently with Espresso, meaning that our business logic can be tested, developed and maintained independently, which is the main benefit of our framework architecture.

Depend on the rules

Dependency rules follow the direction of the Clean Architecture arrow. The outer layer “depends” on the inner layer. By Dependency, I don’t mean the Dependency statements you write in Gradle. Or the outer layer knows how the inner layer defines the abstraction, but the inner layer does not know how the outer layer is implemented. As mentioned earlier, the inner layer contains the business logic and the outer layer contains the implementation details, combined with the dependency rule that the business logic neither sees nor knows the implementation details.

For a project, the exact dependency is entirely up to you. You can divide them into different packages and manage them through the package structure. Be careful not to use external package code in internal packages. Using packages for management is very simple, but it also exposes fatal problems. Once someone does not know the dependency rules, they can write the wrong code. Because this management method does not prevent people from breaking the dependency rules, SO I prefer to group them into different Android Modules. Adjust the dependencies between modules so that the inner code doesn’t know about the outer layer at all.

Abstract principle

The so-called “abstraction principle” refers to the extraction of common patterns from specific problems and the use of common solutions to deal with them. For example, in our development, we often encounter switching interface without network and no data. We define a ViewLayoutState interface in the framework. On the one hand, the business logic layer can directly use it to switch interface, and on the other hand, we can also implement this interface in the View layer to rewrite the style of switching different interfaces. The business logic layer just notifies the interface. It doesn’t know the implementation details, how it is implemented, or even whether the carrier of the interface is an Activity or a View.

This is a good example of how to use the principle of abstraction. When abstraction is combined with dependencies, you find that business logic using abstract advice is invisible and unknown. Right

The concrete implementation of ViewLayoutState, that’s what we want: the business logic doesn’t notice the concrete implementation details, much less know when it will change. The principle of abstraction helps us do this very well.

Build this library

With all these design principles in mind, let’s take a look at the design of Library, which is divided into three modules:

  • Instance
  • Util
  • Base

Util, Instance

Util and Instance are both essentially positioned as tools and auxiliary classes. One is static tool class for “use and go”, such as determining whether text is empty, and the other is Instance form for “long use”, such as Activity management stack, etc.

Base

The main job of Base is to give different capabilities to BaseActivity and BaseFragment. We mentioned above that we should avoid creating “God”, but it is difficult to avoid this situation during project development. In the Library, we extracted all the capabilities of BaseView. BaseActivity and BaseFragment will only be responsible for displaying views.

BaseActivity

The main functions of BaseActivity are divided into:

  • ActivityMvp provides context

  • ViewResult provides cross-screen refresh

  • ActivityToolbarBase provides the top bar

  • ViewLayoutState Provides a switching interface

  • Callback LifecycleCallbackStrategy life cycle management

BaseListActivity

PresenterViewListImpl

Let’s try out the new framework for a simple list of data.

conclusion

As a common development framework within the company, the selection of functions should keep the minimum principle and only use the necessary functions, and the architecture should maintain good expansibility.

I believe you and I, in the process of building frame in all kinds of challenges, learn from your mistakes, and constantly optimize the code, adjust the dependencies, even reorganize module structure, these changes are you want to let the architecture become more robust, we have been hoping that applications can become easy to develop easy to maintain, this is the true sense of the team to benefit.

I have to say that there are many different ways to build an application architecture, and I don’t think there is a one-size-fits-all architecture, it should be iterative and tailored to the business. So, you can try to build your application with the business in mind by following the ideas provided in this article.

Finally, I hope this article will be helpful to you. If you have other better architectural ideas, please share them with me