Recently, I have been working on a niche App with small functions and uncomplicated business. In the past, I found that I had never considered some problems in architecture when I was working on an App. Instead, I just wrote codes in accordance with my previous habits and ignored the design of the App. This share mainly includes some small experience and skills in App development, to share App development and design.

Let me share with you the design and organization of entity classes

Organization of entity classes

There are a lot of entity classes in App development. The more complex the project is, the more entity classes there will be. After some thinking, entities can be divided into the following major numbers:

  • database-oriented
  • The data entity returned by the server
  • The entity used to render the View (using Databinding)

In general, entity class operations go through the following steps:

  1. App requests data from the server
  2. Storing data into a database (optional)
  3. Render the page to display the data
21

At present, the creation of entities only needs to be created when the server data is requested. The subsequent database and page rendering can actually use a set of entities:

22

Regardless of whether this is a good idea or not, using the same entity in three places can cause field ambiguity. For example, if the server data has an Id, and the local data has an Id, then the two Id fields conflict and you have to change the name of the field.

On the other hand, rendering and data itself do not correspond one to one. Sometimes the back-end data is given a pure number while the front-end page is displayed as a string, neither of which correspond, forcing them together will cause more problems.

The correct form of organization for entity classes should be: isolated from each other;

23

Data entities need to be prepared before rendering, such as converting int data into text data in ViewModel and then rendering the page using Databinding+ page rendering entities.

Gracefully handle network data

Most of the network libraries developed for Android today are Okhttp + Retrofit. With Retrofit network interaction has become very simple and a single Service interface can do everything. Meizz ~~ now most of the data returned by the back end is in the following form:

{

    "code":0.

    "data": {},

    "msg"""

}

Copy the code

Not everything, but great data, news, success or failure — everything! As for the data field, other MSG, code and other fields belong to the auxiliary field. The entity object corresponding to the front end should look like this (dummy code) :

public class ApiResponse<T{

    private int code;

    private T data;

    private String msg;

}

Copy the code

The corresponding Service should be defined like this (using RxJava) :

public intface UserService {

    @GET("/xx/{id}")

    Single<ApiResponse<UserInfo> getUserInfoById(@Path("id") Long userId);

}

Copy the code

ApiResponse -> UserInfo: ApiResponse -> UserInfo: ApiResponse -> UserInfo:

.



if(ApiResponse.code == 0) {

    UserInfo info = ApiResponse.getData();

}



.

Copy the code

To eliminate this redundant code, we can use the CallAdapter to make the data returned by the Service method directly be the entity class:

public intface UserService {

    @GET("/xx/{id}")

    Single<UserInfo> getUserInfoById(@Path("id") Long userId);

}

Copy the code

CallAdapter code is not posted, you can find their own. Another problem with this is how the business code can determine whether the interface succeeds or fails. The front end must be friendly to the user, rather than Loading. The most convenient way to pass errors at this stage is to use Java exceptions. The front end can define business exceptions or network exceptions:

public class BizException extends RuntimeException {

.

}

Copy the code

Check the return value of the ApiResponse successfully in the CallAdapter:

if(! ApiResponse ! =0) {

    throw new BizExcepiton(ApiResponse);

}

Copy the code

If the back end returns a business exception, the front end will throw a BizExcepiton. If it is an HTTP error such as 404 or 400, it can throw HttpException. In addition to BizExcepiton and HttpException, specific exceptions such as the back-end return password error exception can be used:

public class InvalidPasswordException extends BizException {

.

}

Copy the code

If special treatment is required, it can also meet the requirements.

Robust data layer

A lot of applications today are developed using MVVM development model and the data layer is represented by Repository. In a data-driven development model, page changes need to be updated as the data changes and then the page responds. It is not recommended to simply create a UserRepository that contains operations such as logging in, registering, and updating passwords.

  1. Interface oriented programming
  2. Keep the single principle
  3. Functional boundaries should be clear (e.g., login and registration can be separated)
  4. Minimal business logic (complex business considerations for Presenters)

One way to check for good design is if a login page from Activive/Fragment to ViewModel to Repository has extra code. For example, UserRepository contains login and registration but does not require registration on a single login page, which is redundant (some apps are logged in/registered on the same page).

A simple UserRepository diagram with login and registration:

Another point is to try to centralize some of the things that Repository uses, by introducing a basic repository:

public class SimpleRepository {



    protected final  <T> getService(Class<T> clz){

        return Services.getService(clz);

    }

}

Copy the code

As a subclass of SimpleRepository, you don’t need to worry about where to get the Service.

Simple UI layer

The UI level can be divided into ViewModel and View (Activity/Fragment). The responsibilities of View should only have two points:

  1. Show business data
  2. Collecting Service Data

For example, some data organization and judgment should not appear in View. For example:

 if (Strings.isNullOrEmpty(phone)) {

.

        return;

 }



 if (Strings.isNullOrEmpty(pwd)) {

.

        return;

  }

Copy the code

Code like this should not be in the View, but when placed in the ViewModel, the View only collects user data and passes it to the ViewModel for validation. For example, if/else code like this should also be placed in ViewModel:

 int age = 10;

 String desc = "";

 if(age < 18) {

    desc = "Youth";

 }else if(age < 29) {

    desc = "Middle-aged";

 }

Copy the code

If too much data is displayed and collected, it is recommended to use Databinding for bidirectional binding of data. With LiveData, View is used as an observer to monitor data changes in real time:

registerViewModel.getRegistryResult().observe(this.new SimpleObserver<RegistryInfo>(this));

Copy the code

LiveData notifies the Observer of updates whenever data changes, updating individual page data via DataBinding.

Besides, the ViewModel should only contain some simple code to judge, check and get through the data. If the business is too complicated, you can consider adding Presetner. If it is really too complicated, you can reflect on whether this complicated logic should be put in the front end, can it be put in the back end?





Welcome to pay attention to the wechat public account “Architecture Digest”, the first time to push high-quality technical articles.