Recently, I sorted out the code of the project, and then I remembered the time when I was working on the Android client of the company’s project by myself as an intern. I had no practical experience and no way to go. During that month, I did a good job for the project, searched on the Internet and decomcompiled the application research of others. But at any rate, that time of torture still has some value, now think, it is necessary to sort out the summary, share.

I also learned Android by myself by looking at online textbooks. Whether it is Google’s official Training or a variety of textbooks, they mainly focus on the use of various controls and system components, just like teaching children to play with building blocks: “This is a long bar, this is a block. Admittedly, this is very important, but most of the training and teaching material also call it a day, with one or two at most small Demo general practice, and many people began to look for a job hit a trip, it’s like to meet up all blocks and only knows all sorts of building blocks of the children are going to build a skyscraper, the nature will be more difficult.

Here I sort out my self-study experience to share, on the one hand is the summary of their own work and study, on the other hand, also hope to help later 🙂

ps: MVP and MVVM are popular on the Internet recently, and Google officially launched the MVP Sample: Android-architecture, but I have encountered great difficulty in applying most of the Demo level writing methods on the Internet to the project, and GOOGLE-MVP has not been applied on a large scale. I will share my learning experience later.

Ps: The initial idea came from the article: Android application architecture, thanks to the author and translator.


0. Basic idea

First of all, we need to know what business applications we generally do. We agree with the classification of mobile applications in App RESEARCH and Development Record, that is, general mobile applications are divided into three kinds: data display applications, mobile assistant applications, and games.
Data display applications are characterized by multiple pages, requiring frequent invocation of back-end interfaces for data interaction, generally involving payment processes, testing the normal acquisition of data in weak network environment, and reducing power consumption and flow consumption.
Mobile assistant applications mainly focus on system API calls, to assist the purpose of management system.
We generally do applications are data display type applications, but the general scale of the application will more or less involve the system API call, not table.


1. Basic application framework starting from 0

In fact, after learning most Android courses, there is a most basic application framework, the core of which is Activity and Fragment. Some people say that the overall architecture of Android application is MVC, M is Java Entities defined in the application, V is various views. XML configuration files, C is for activities and fragments, but in practical development, you will find that activities and fragments are almost god. They are not only responsible for Controller functions, but also responsible for View management. It also holds system resources such as Context. You can actually think of this architecture as an architecture of MVS interacting with each other.



When you just start a Demo, you usually start from the page, create an Activity or Fragment on a page, initialize the data, initialize the controller, and bind the listener…… All of these operations are done inside the Activity and Fragment defined classes. For some demos with only a few input fields, or a single ListView, this shouldn’t be a problem.
But what if, instead of a Demo, you want to make a shopping cart page for a shopping app? These pages have a lot of controls to initialize, add, delete, modify, and search data, and gesture operations. If these things are left to the Activity/Fragment to do, then the code of a class will quickly break a thousand lines. The Activity/Fragment burden can also be heavy when it comes to applications that share data globally, such as user data, or functions that check caches.


The solution to this problem is to share the work of activities and fragments. Generally speaking, page-independent methods are put into tool classes, such as regex for verifying mobile phone numbers and email addresses, and Log encapsulation. Network calls are generally done through asynchronous network libraries, such as Volley. Retrofit, which is packaged as a Request and Call, is a small improvement on Activity and Fragment stress by extracting this code. (See the blog post on how to use and encapsulate these web frameworks. I won’t go into details or write examples.)
The general structure of the whole project is as follows:



The first application I made was such an architecture. It was relatively simple to use, but still awkward to write, because in addition to network calls, activities/Fragments also needed to operate on the current Java Entity object, operate on the local database, operate on local files, etc. For example, to obtain the cache of current user information, or to obtain the data such as article list and shopping cart list, it may involve multiple page usage data, and there are certain checks to obtain the data, such as the judgment of paging load and pull-down refresh, whether to use cache and so on. These operations should not be done by activities and fragments themselves, and it’s obviously not a good idea to put them in a Web module or Java Entities. The Java Entity itself is supposed to be just a Java object model for database data mapping, and giving it responsibility for managing cached data just makes it confusing and functional. For example, if I define a NewsList in the Model package for a page-loaded list such as a NewsList, is the NewsList a data Model or a data manager? I’m sure you’ll get a little confused when you look at the code. The User data is generally used globally within the application, and if I define it as a singleton, operations like JSON deserialization will be more painful. Why do I need a UserCall object to log out of a user?


2. Improvements to the base version architecture

With the above confusion in mind, an improved solution is in order: abstract out a new manager to coordinate caching and network calls.
This new data manager has already taken shape while trying to handle paged data caches, and NewsList, the so-called Model, is actually a data manager. Its data refresh depends on the Activity/Fragment calling the network callback and then setting it. For a moment, there is a clear data flow from the client’s UI click response to the server’s request, to the server’s return of data to refresh the UI:
UI initiate request – check cache – Call network module – parse return JSON/unified handle exception – JSON object mapped to Java object – Cache – UI retrieves data and presents it




Through this data flow, a three-tier layer of network data requests is apparent: the UI layer, the data management layer (caching, request network), and the network invocation layer
Continuing with the example of paginating the news list:
We just declared a NewsList Model to store data. For a Java Entity, NewsList might look like this:
  1. public class NewsList {  
  2.     // List of current news items  
  3.     private List<News> newsList = new ArrayList<News>();  
  4.     // Current page number  
  5.     private int currentPage = 1;  
  6.     / / total page number  
  7.     private int totalPage = 1;  
  8.   
  9.     public NewsList() {  
  10. .
  11.     }  
  12.   
  13.     public void addToList(List<News> list, int currentPage) {  
  14.         newsList.addAll(list);  
  15.         this.currentPage = currentPage;  
  16.     }  
  17.   
  18.     public void setTotalPage(int totalPage) {  
  19.         this.totalPage = totalPage;  
  20.     }  
  21.   
  22.     public void getTotalPage() {  
  23.         return totalPage;  
  24.     }  
  25.     /* For a data manager, the following sets and gets will not take up space. 
  26. Do you want to open get/set to external use of private data? If not open, is it Entity? This is also a sign of duty confusion… * /  
  27. }  

Public class NewsList {private List<News> NewsList = new ArrayList<News>(); Private int currentPage = 1; Private int totalPage = 1; public NewsList() { ...... } public void addToList(List<News> list, int currentPage) { newsList.addAll(list); this.currentPage = currentPage; } public void setTotalPage(int totalPage) { this.totalPage = totalPage; } public void getTotalPage() { return totalPage; } /* Do you want to open get/set to external use of private data? If not open, is it Entity? This is also a sign of duty confusion... * /}Copy the code


Now promote it to NewsListManager and it might look more reasonable:


[java]
view plain
copy
print
?

  1. // First define a callback interface to get data  
  2. public interface ActionCallbackListener<T> {  
  3.     void onSuccess(T data);  
  4.   
  5.     void onFailed(Exception e, String message);// It can return either the handled Exception ID or the original Exception object of your own design  
  6. }  

Public interface ActionCallbackListener<T> {void onSuccess(T data); void onFailed(Exception e, String message); // The Exception can return either the handled Exception id or the original Exception object of your own design.Copy the code
[java]
view plain
copy
print
?

  1. // Implementation of the Manager object  
  2. public class NewsListManager extends BaseDataManager {  
  3.     // List of current news items  
  4.     private List<News> mNewsList = new ArrayList<News>();  
  5.     // Current page number  
  6.     private int currentPage = 1;  
  7.     / / total page number  
  8.     private int totalPage = 1;  
  9.   
  10.     public NewsListManager() {  
  11.         getCacheFromDatabase();  
  12.     }  
  13.   
  14.     public List<News> getCachedData() {  
  15.         return mNewsList;  
  16.     }  
  17.   
  18.     public void pageLoadNewsList(boolean isRefresh,  final ActionCallbackListener<List<News>> mActionCallbackerListener) {  
  19.         if(isRefresh) {  
  20.             clearCache();  
  21.             mNewsList.clear();  
  22.             currentPage = 1;  
  23.         }  
  24.         NewsListRequest request = new NewsListRequest(); // Let’s assume that this is a Request object for the list of news items requested by the network call module  
  25.         request.setData(currentPage);  
  26.         request.request(new RequestCallback() {  
  27.             @Override  
  28.             void onSuccess(JSONObject response) {  
  29.                 totalPage = response.optInt(“total_page”);  
  30.                 currentPage = response.optInt(“current_page”);  
  31.                 // Store network data to manager…….  
  32.                 saveToDataBase()  
  33.                 if(mActionCallbackerListener ! =null) {  
  34.                     mActionCallbackListener.onSuccess(mNewsList);  
  35.                 }  
  36.             }  
  37.             @Override  
  38.             void onFailed(Exception e, String message) {  
  39.                 if(mActionCallbackerListener ! =null) {  
  40.                     mActionCallbackListener.onFailed(e, message);  
  41.                 }  
  42.             }  
  43.         });  
  44.     }  
  45.   
  46.     private void getCacheFromDatabase() {  
  47.         // Fetch the cached data from the database  
  48.     }  
  49.   
  50.     private void saveToDatabase() {  
  51.         // Cache the news data to the database  
  52.     }  
  53.   
  54.     private void clearCache() {  
  55.         // Clear the cache in the database  
  56.     }  
  57.     / /…  
  58. }  

Public class NewsListManager extends BaseDataManager {private List<News> mNewsList = new ArrayList<News>(); Private int currentPage = 1; Private int totalPage = 1; public NewsListManager() { getCacheFromDatabase(); } public List<News> getCachedData() { return mNewsList; } public void pageLoadNewsList(boolean isRefresh, final ActionCallbackListener<List<News>> mActionCallbackerListener) { if(isRefresh) { clearCache(); mNewsList.clear(); currentPage = 1; } NewsListRequest request = new NewsListRequest(); Request.setdata (currentPage); request.setData(currentPage); request.request(new RequestCallback() { @Override void onSuccess(JSONObject response) { totalPage = response.optInt("total_page"); currentPage = response.optInt("current_page"); // Store network data to manager....... saveToDataBase() if(mActionCallbackerListener ! = null) { mActionCallbackListener.onSuccess(mNewsList); } } @Override void onFailed(Exception e, String message) { if(mActionCallbackerListener ! = null) { mActionCallbackListener.onFailed(e, message); }}}); } private void getCacheFromDatabase () {/ / will cache the data from the database to retrieve} private void saveToDatabase () {} / news/cache data to database private void ClearCache () {// Clear the cache in the database} //...... }Copy the code

As you can see, with this wrapper, the UI layer doesn’t need to manage the page-loading logic at all, just call the pageLoadNewsList() method of NewsListManager and tell the Manager whether or not to refresh, consistent with the UI processing logic (pull-down refresh, page-loading). This greatly simplifies activities and fragments. Similarly, the same logic can be applied to the user data used by the application. The isRefresh command is used to determine whether the user data needs to be pulled from the server. Most applications modify user data through only a few portals, and in most cases, they do not need to obtain user data from the network through request. The caching mechanism implemented by Manager can greatly reduce unnecessary interface calls, but the way the UI layer requests data remains the same.
Through the encapsulation of Manager, the whole application can be divided into three layers: UI, Managers and NetModules. Note that the so-called layering is not a random division of several packages, but a strict division of responsibilities and permissions, that is, each layer has its own role, each layer provides interfaces to the upper layer, sealing the details. For example, when the UI layer sends a data request to the Manager, it does not need to care whether the Manager uses a cache or a network request, nor does it need to care how the network request encapsulates the packet parameters. Just make a request based on the interface parameters provided by the Manager to get the data. The architecture might look something like this:







Since most of the code is stripped from activities and fragments, now
Activity/Fragment is only responsible for UI control and data acquisition. Most of the other code can be uI-independent. If you do your best to ensure this in development, existing Managers, network modules and other tool classes can form an AppSDK based on proper interface design. Provide support for mobile phone and tablet applications, and the project-irrelevant tool classes and general controls can be classified into the common development resource library module, greatly reducing the repetitive workload.



I have to say, it doesn’t have to be good, even a sound architectural design can greatly improve code quality and reduce workload. This improved version of the application architecture is in good all work on some of my projects, greatly reducing the repetition code, but the most important thing is, even in the crush stage, has been basically what the time, that still maintain the size of the application architecture as a whole and not out of control, because there is no written Billy with framework provides faster…


Always some people say that premature optimization is the root of all evil, things out first, but personally I think both agile and fast things, is a threshold, if short of that threshold, sacrifice quality for speed lead to project architecture is out of control, code chaos, the result is only refactoring more hard, or leave others pit. The idea that efficiency is king is aimed at skilled workers, and it is important to take the time to ensure code quality when you are inexperienced.