The original address: www.jianshu.com/p/5832c7766…

I didn’t write a line of code to implement the Toolbar! And you’re still wrapping BaseActivity? It was a month ago, when some people said THAT I was a title party, and some people did not recognize my content, but this did not hinder me, two days won the nuggets as the week’s top list, and was reprinted by hongyang public account, the cumulative reading of more than 30,000

The research results of the previous article enable MVPArms to monitor the life cycle of all activities and fragments in the entire App (including tripartite libraries) and insert code into their life cycle. This time, I report another recent research results to you, of course, the same New features on MVPArms

Github: Your Star is my motivation ✊

GIF list requirements upload and download is a necessary function of most apps, display progress bar is also an important part of improving user experience, of course, as the author of configurable integration framework MVPArms, I want to improve the use of experience and development efficiency of developers, then I must provide a set of solutions

So I opened Github and searched the progress monitoring library related to Retrofit, Okhttp and Glide. There were a lot of libraries, but none of them met the needs I wanted. So I rolled up my sleeves and prepared to masturbate

This library must support multiple platforms,Okhttp, Retrofit, Glide must support these three libraries at the same time, but the library does not contain these three libraries, let the user to introduce, reduce the size of the library must be easy to use!! , best can one line of code with low invasive, does not need to change before the written code, network request to introduce and not into the library, any effects of the code before the low coupling, users do network request code, the receiver must not and schedule associated code too much Any location in the App can be accepted into a network request progress information Not only need to meet, a data corresponding to a schedule one-on-one relationship at the receiving end, also need to satisfy a corresponding multiple data sources progress, at the receiving end a one-to-many relationship, so you can update a number of different location of the progress bar Run in the main thread by default, allow users to switch threads less troubles demand analysis and research Cool all of a sudden, write so many needs, when the product manager is a word cool!

A careful look at these 8 needs, the moment meng forced, younger sister of this is not pit yourself? In addition to the last item, I know that I can use Handler to achieve, other completely have no idea ah, well, as a high-quality male youth I have to know the difficulties ah, first from the first demand to start analysis!

Requirement 1 (Multi-platform support)

When Interceptor intercepts the RequestBody and ResponseBody of each request, the RequestBody and ResponseBody of each request are added to the RequestBody and ResponseBody of each request ResponseBody replacement, it’s all template code, you can just copy and paste it, whereas Retrofit uses Okhttp underneath, so you can do the same thing

But how does Glide implement progress monitoring? My first thought was that Retrofit could be easily implemented using the Okhttp request network, so it would be possible to switch to Okhttp from Glide’s underlying request framework, and for such a cool library, there must be a way to extend it, so I immediately turned to Glide Glide bottom is found to use the HttpConenction to request the network, and this class can be replaced, quickly Google under

The compile ‘com. Making. Bumptech. Glide: okhttp3 – integration: 1.4.0 @ aar’ ok, find solutions, the above offer classes are available, and the underlying request frame to replace Okhttp The core of this framework has been found, mainly through Okhttp, which is like a comfort to eat

Demand 2 (volume reduction)

This requirement has been googled and is very simple. Provided introduces the dependency framework, and the framework introduced when packaging is not included

Requirement 3 (One-line implementation)

For such external Api design requirements, we should achieve the main function, and then slowly optimize to achieve the goal, so first analyze the following requirements

Requirement 4 (low invasive)

As mentioned in Requirement 1, the key to implementing listening for upload and download progress is to replace the original RequestBody and ResponseBody for each request with a rewritten one in the Interceptor

How do I identify requests that need to listen for progress?

Replacement is simple, but not every request need to upload and download progress monitoring, can’t be replace each request, I started to think of is to need to monitor the progress of the request to generate a tag, and then resolves to the tag in the Interceptor, means that the request to upload or download progress monitoring, and then began to replace it

The easiest way is for the Interceptor to add a custom Header to the request so that it doesn’t need to define another class. When the Interceptor iterates through all the headers, it can replace it if it finds one

But that doesn’t address the need 4, because it allows users to request an operation more than usual, if you want the code before have progress monitoring function, is to change one by one, increased the labor, and the operation is for me the library, when the user does not want to use the library, will be involved in modifying the code before, so also increased the invasive

Url as tag

A flash of thought, but also what mark, Url is the only, can not be used as a mark!!

Requirements 5 (low coupling), 6 (reception anywhere), and 7 (one-to-many)

Borrow from EventBus

Why put these three requirements together, because they remind me of EventBus, where multiple observers register themselves into a container using the same tag and are posted by observers using that tag An event, and then pull out all the observers registered with the tag from the container and notify them one by one, which is both decoupled and can be listened on anywhere in the App as long as the tag is known, and also supports one-to-many

With the use of the Url as a tag mentioned in requirement 4, I was able to achieve the above requirements without changing any of the previous request code, just by writing the code on the receiving side

Conception of the Api

Since we are talking about EventBus, I will use the Api of EventBus to design. Users can monitor upload and download progress with only one line of code, passing in a tag and an event. Yes, the tag is the Url. An event is a listener to get progress information, which satisfies the one-line implementation of Requirement 3

Like this

Progressmanager.post (flag, event); After the user calls this line of code, I put the Url as the Key and the listener as the value into a globally unique Map

And so on? What about one to many? So the value must be a List< listener >, which satisfies the one-to-many condition

How are listeners notified internally?

We registered all listeners to listen for urls in the container, so when should we notify listeners of progress, in RequestBody and ResponseBody When you start to write or read the binary stream, because they’re the only ones who know the read and write times in the first place, all you need to do is put all the listeners for that Url into his Body

As mentioned in Requirement 4, we don’t know which requests need to be monitored for upload or download progress and which are not, but now we can tell by the Url, because we can get the Url of the Request from the Interceptor

Previously, we have registered the Url as the Key in the container. If the container contains this Url, it means that the request needs to monitor the upload or download progress. Then we will replace it with the rewritten Body and pass in the listener Or constantly iterate over all listeners of this Url while writing, call listener listener methods, and pass in progress information, you can execute the user’s update logic, and you’re done

Requirement 8 (Main thread execution)

This is very simple, using handler.post (Runnable) to call listener methods in Runnable

Frame details are optimized without manual logout

As we all know, EventBus requires manual logout when it does not need to accept events after registering the observer. However, when applied to my library, event reception may not be so strict. Therefore, in order to save the user unnecessary steps, I use WeakHashMap instead of the previous Map container WeakHashMap will find the unused Key when the Java VIRTUAL machine reclaims the memory and remove the entire item, so manual remove() is not needed.

lock

In the above mentioned users need only one line of code, add Url and listener to container, but this line of code, may be invoked in the different threads, and of some of the logic in this line of code in multithreading is unsafe, all I need to join the thread lock at this moment, this is important for third-party libraries, because you can’t predict the operation of some users

Throw clear errors to the user

As I mentioned in Requirement 2, the library only introduces Okhttp with the provided, so Okhttp is not called into the ARR package, so a NoClassDefFoundError is raised if the user does not introduce Okhttp in his project Class. ForName (” okHttp3.OkHttpClient”); Class. ForName (” okHttp3. If the class for Okhttp is not found, the consumer did not introduce Okhttp, and I throw a very clearly explained error

To improve performance

Because as I mentioned, I’m constantly iterating through all the listeners and calling its listener methods to achieve one-to-many synchronous updates when the Body starts reading or writing to the binary stream

But when you get to a certain number of listeners, you’re going to have performance problems, and you’re going to have to keep adding new listeners, and you’re going to have to change the length of the container during the iteration

Therefore, when I pass the List into the Body, I pass the list.toarray (). The array allocates contiguous memory area and its length is fixed, so the index efficiency is advantageous. Therefore, I use the array for traversal

Distinguish between multiple progress at the same Url

Because the App user may continue to use the same Url to start a new request before the previous progress has been uploaded or downloaded, the same Url will be used if the frame user does not do the operation of removing repeated clicks at the upper level There will be multiple progress updates being executed at the same time, and an identifier will be needed to distinguish which progress information is being executed (all progress updates to this Url will call the listener that was previously registered with this Url), so I will create the System.currenttimemillis () is stored as a unique ID, passing progress information along with the ID to the user each time

conclusion Actually this library was relatively simple, the core of the implementation way in many areas are to be able to copy and paste, but after I have a package or than before, simple and elegant, and the purpose of writing this article is to share, how to analyze the requirements, and how to encapsulate the optimization of a small library, of course, also want to read more at ordinary times the source code, to accumulate and draw lessons from excellent Ideas will be constantly inspired during creation. For example, my library is based on the ideas of EventBus. When writing code, we should dare to think and try new ideas that are different from the previous ones, so as to make continuous progress

Github: The implementation depends on the source code is not? Remember to give Star ✊ thanks!