The original address: https://juejin.cn/post/6844903488833454093
- Solving Retrofit multi-baseurL and dynamically changing BaseUrl at runtime
- Solving Retrofit multi-baseurL and dynamically changing BaseUrl at runtime (II)
preface
Hello, MY name is JessYan, and I like to explore new solutions. In my last article, I introduced to you how to upload and download and Glide progress monitoring with one line of code. Now I have brought you another solution to a problem that we are all looking forward to One of the Issues of MVPArms, of course, is the need for multiple BaseUrl and dynamic switching of BaseUrl when using Retrofit, which is also often discussed in other places, so here are my ideas and solutions
Github: Your Star is my motivation ✊
Scenarios where requirements arise
Some of you may have encountered both of these scenarios in your daily development, but for those of you who have not encountered these scenarios before, this article will be mentioned first
Requirements scenarios for multiple BaseUrl
If the project is aggregation-based App, such as some news and information client, the data source may come from multiple platforms, such as Zhihu, Douban and Toutiao, so it will involve multiple BaseUrl
If the project uses multiple three-party service providers, for example, picture reading is used by one service provider and file storage is used by another service provider, there will also be multiple BaseurLs in one App
Dynamically change the requirements scenario for BaseUrl
If the BaseUrl of the project will request the server when the App starts, and the final BaseUrl of the project will be determined according to the result returned by the server, it will involve dynamically switching the BaseUrl at runtime
If the project is one of the three party service provider, is not fixed, maybe there will be changes, such as storage services from seven cows migrated to other cloud storage, that we in order to avoid the code changes lead to repackage and send version, will from the server BaseUrl third-party service providers, and then change the BaseUrl dynamically at runtime
The solution
In fact, the official Api has long provided solutions to support multiple BaseurLs and dynamically changing baseurLs at runtime, and there are many private solutions as well
Official static solution
Developers familiar with Retrofit should know that @get, @post Annotations annotated to each interface method can pass not only relative paths but also full paths, so that we can use different baseurLs for different interfaces to achieve the need to use multiple BaseurLs, but the value on the annotation can only be Final Constant, cannot be changed dynamically, so I call this solution static
Official dynamic solution
Developers familiar with Retrofit will also know that @URL is an annotation to each interface method parameter, which can pass the full path as a parameter to the interface as the Url address for each request. Each request interface can take a different full path as a parameter, thus supporting multiple BaseurLs As well as dynamically changing BaseUrl at runtime, so many interfaces that request resources such as images use this solution.
Common folk solutions
I have seen a lot of open source aggregator App source code before, like some integration zhihu, Douban, Gank and other apps with data from multiple platforms have different domain names, so most of these apps will create a Retrofit object for each platform, that is, different BaseUrl will use different Retrofit objects to create ApiService Make the request so that whenever you add a different BaseUrl, you need to create a new Retrofit object from scratch
This can be done simultaneously, supporting multiple BaseurLs and dynamically changing the BaseUrl at run time, but from a personal point of view it would be a waste of resources to create multiple Retrofit objects with the same other configuration properties but different BaseUrl
Private cattle solutions
I came across a Retrofit maintainer, Square’s big shot solution for dynamically changing the BaseUrl at runtime, which is actually a semi-official solution
One interesting thing to note about this solution is that Retrofit used to support dynamically changing BaseUrl at run time by default. It used to have an interface called BaseUrl, while Retrofit.builder # BaseUrl (BaseUrl) HttpUrl. This interface has a method that returns HttpUrl. If you implement BaseUrl and change the value of the method, you can dynamically change BaseUrl
But the big guy decided it wasn’t safe, so he made a Pull request, deleted the BaseUrl interface, and replaced it with the solution above, and dear JakeWharton agreed and merged the PR to get what we have now Retrofit.builder #baseUrl(HttpUrl) This Api does not dynamically change baseUrl
Those of you who are older than Retrofit know that there used to be an Api for this. I mean, what happened to the later version of Retrofit
This solution uses the Interceptor Interceptor to dynamically change the Url of each Request to dynamically change the BaseUrl. However, this solution does not support multiple BaseUrl, as long as the host is set, until the next host change, all subsequent BaseUrl Requests must all use the same Host, and there are some drawbacks that will be discussed later
Comparison and analysis of several schemes
Eliminate programs with obvious defects
Of the 4 solutions, the first one I eliminated was the common folk solution. My opinion was made clear earlier, because I personally think that creating multiple other configuration properties is exactly the same, just BaseUrl is different from Retrofit Object is a waste of resources, so even if it meets all my needs, I won’t choose it unless there is no better solution
Among the remaining three solutions, the official static solution can only solve the problem of supporting multiple BaseUrl in the two requirements. For dynamically changing the BaseUrl, since the Value of the annotation can only be a constant, this requirement is also helpless (it is feasible if both requirements are met).
Who is the best solution?
In fact, the official dynamic solution can implement multiple BaseUrl and runtime dynamic change BaseUrl at the same time, so why don’t I just choose this solution and continue to analyze?
The answer is also very simple, I think this scheme, although flexible, but flexible has brought it on the use of cumbersome, each interface each call must pass the full path as a parameter, not only cumbersome and interface is difficult to manage
How about a civilian solution? But I’ve already said that’s not going to work, right?
This solution can dynamically switch BaseUrl at run time, but it is handled globally. Once used, it changes the URLS of all requests, so it does not support multiple BaseUrl
What’s worse, not only does this solution not support multiple BaseUrl, it also affects both the official static and dynamic solutions that support multiple BaseUrl, because its Interceptor Interceptor will force the request no matter what full path you declare in the annotation The Url is changed to its BaseUrl, so this solution is destined to be only suitable for projects that have only one BaseUrl but need to change dynamically
Wouldn’t none of the four solutions work? Why don’t you talk so much about wool?
Elimination of all schemes? The meeting?
Hold on, though I passed all the existing solutions mentioned in the article from my point of view
But if you think about it, what’s the point of writing this article if the perfect solution already exists online? Must be no solution THAT I am satisfied with, I will start to solve and share ah, after all, I am a promising youth who is not willing to write repeated content, as long as I write content is sure to let everyone learn different knowledge three ✊, or not to hit their own signboard
All right, enough with the teasing. Turn it on!
Don’t worry, there’s more!
Although in the solution of existing found no let me happy, but when they have a problem, to calm analysis existing solutions are necessary, understand the predecessors’ ideas before understanding the problem more thoroughly, many of my articles are based on the analysis and solution is given priority to, to teach fishing is better than teach them to fish, so I won’t tell you the answer directly, first analysis of a wave, ideas
This is not true. When analyzing the folk danniu solution, I finally found that it was not the solution I wanted, but as a divergent thinker, I had a brainwave and decided that it would be feasible to make such a change with the help of the original solution.
How to improve the original plan?
In the Interceptor Interceptor, you can set a global Host(BaseUrl), and the Interceptor will force this Host to be applied to all requests Url, which results in only one Host at a time
So I was wondering if changing the unique Host variable to a set to store multiple hosts could support multiple BaseUrl while applying different hosts to different requests.
Practical ideas
So I built myself a global container to store multiple hosts, so I could add, modify, and delete hosts anytime, anywhere, while my App was running
Have a problem
But the problem is, I want to apply different hosts to different requests, but how do I know which request needs which Host, each request has to have a flag, let me know what kind of Host it needs
So I was wondering what Retrofit would do to mark each request with a different string before it was requested, and I naturally thought of Headers. Retrofit happens to have @headers annotations that allow you to add custom Headers to every interface method, right
Solve the problem again
I’ve added a custom Header to the interface methods that require different baseurLs to specify the Name of the Host that each interface needs. This Name is Host, but this value is not specified in the @headers column, it can change dynamically
The container that stores the Host is a Map, the key is the Name, the value is the Host, and every time the interceptor intercepts a request, it determines if the request has this custom Header, Get (Name) from the global Map that stores the Host, get(Name) from the global Map that stores the Host, and apply it to the request.
If you want to change a Host dynamically, it’s easy to put(Name) the new Host into the global Map with the same Name, and then the interceptor, using the Name get(Name) value, will already be the new Host that changed, and will change the Host Doesn’t it dynamically change the BaseUrl when applied to the request?
This is not, meet both needs at the same time!
Optimization scheme
This solution consists of two steps, setting headers for requests that require a different BaseUrl (if you want to use Retrofit’s default BaseUrl interface, or if you want to use the official static solution, the official dynamic solution does not need to be set), and managing the BaseUrl through the global container
For projects that have only one BaseUrl but need to change dynamically, this framework provides a GlobalDomain to optimize the scenario. Instead of adding headers to the interface, just one step is to put(GlobalDomain) the BaseUrl that you want to change It is ok
It is much simpler than the official dynamic solution that passes the full path as a parameter to every interface, which is destined to be suitable for interfaces where only one or two need to dynamically change the BaseUrl
conclusion
The solution mentioned above has been optimized and packaged into a tripartite library and uploaded to Jcenter for your convenience
This solution is mainly suitable for projects requiring both multiple BaseUrl and dynamically changing BaseUrl, or projects with only one BaseUrl but requiring dynamically changing BaseUrl
For a project that only needs multiple BaseUrl and does not need to dynamically change BaseUrl, the official static solution is actually enough. However, I still recommend using my solution, because the requirements will change. If the requirement of dynamically changing BaseUrl is added, for example, the production environment needs to be dynamically changed And the development environment, how to change the full path in each interface annotation one by one?
Github: See Demo for details, remember Star!
The public,
Scan the code to follow my official account JessYan to learn and make progress together. If there is any update to the framework, I will inform you immediately on my official account
Hello, my name is JessYan. If you like my articles, you can follow me on the following platforms
- Personal homepage: Jessyan.me
- Making: github.com/JessYanCodi…
- The Denver nuggets: juejin. Cn/user / 976022…
- Jane: www.jianshu.com/u/1d0c0bc63…
- Weibo: weibo.com/u/178626251…
— The end