reason

Retrofit doesn’t officially provide an interface to modify the BaseUrl, but we do. For example, when I was developing a quick accounting APP, I had four BaseUrl, and I used HomeBase EURL when I was developing at home. Then WHEN I went out for something, I pulled out my mobile phone and left without switching BaseUrl to a formal environment. When I went out for consumption, I tried to keep an account and found that I could not remember it.

I did a web search on how to dynamically modify BaseUrl, and most talked about writing interceptors or multiple Retrofit instances. I felt like it was too much trouble, so I gave up.

twist

We’ve recently been looking at Retrofit’s source code and found that the BaseUrl we passed in is wrapped as HttpUrl, and there is only one instance of the APP, so if we change the methods or properties of the instance, we can implement the desired functionality. Most properties in HttpUrl are final or Val modified, so they can only be modified by reflection.

Below is how to switch between formal and test BaseUrl, and then replace BaseUrl with reflection.

Formalize and test BaseUrl

The most basic requirement is that I use the test environment when developing, and package with the formal environment. This is the first requirement. Divide two kinds of circumstance, one kind is whole project, one kind is componentized development.

The whole project

Declare our API address BaseUrl in the gradle.propertis file of the project:

# # formal environment BASE_URL_RELEASE = "https://juejin.cn/user/888061125471917" test environment BASE_URL_DEBUG="https://github.com/Android-XXM/XXM-BLOG"Copy the code

Add the following to your app component’s build.gradle file to configure BuildConfig.

 buildTypes {
        release {
          	...
            buildConfigField("String", "BASE_API", project.BASE_URL_RELEASE)
        }

        debug{
        	...
            buildConfigField("String", "BASE_API", project.BASE_URL_DEBUG)
        }
    }
Copy the code

That way, when configuring Retrofit’s BaseUrl, we can configure it with buildConfig.base_API, which automatically switches API addresses based on whether we’re calling a formal package or a test package.

Retrofit. The Builder (). The baseUrl (BuildConfig. BASE_API) / / here. AddCallAdapterFactory (CoroutineCallAdapterFactory. Invoke ()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build()Copy the code

componentization

The difficulty with this is that we usually put the network request in the base library, such as the Base component. You can’t switch by determining BuildConfig, so you need to use the code to determine whether the current environment is formal or testing.

Public static Boolean isDebug(Context Context) {if (debugFlag == -1) {isDebug = context.getApplicationInfo() ! = null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) ! = 0; } return isDebug; }Copy the code

Great, now that we can judge the formal and test environments, we also set up BaseUrl in code.

# # # Kotlin code / / testing const val BASE_URL_API_RELEASE = "https://juejin.cn/user/888061125471917" / / formal const val BASE_URL_API_DEBUG = "https://github.com/Android-XXM/XXM-BLOG" fun getBaseUrl(): String { return if (SystemUtils.isDebug(BaseApplication.instance!! .applicationContext)) { BASE_URL_API_DEBUG } else { BASE_URL_API_RELEASE } }Copy the code

This is great, because now we know how to switch between the test environment and the formal environment through configuration, so we don’t need to change it manually.

Reflection modifies BaseUrl

If you look at the HttpUrl class, there are a number of attributes that can be modified, such as protocol, host, port number, etc., we are interested in modifying url attributes.

The BaseUrlHelper class is used as a singleton to ensure that HttpUrl has only one instance of the entire APP. The entire implementation is shown directly below, sometimes copied directly if necessary.

/** * @author https://juejin.cn/user/888061125471917 * / public class BaseUrlHelper {/ / protocol: HTTP or HTTPS private static final Field schemeField; // host: www.baidu.com or 118.25.3.6 private static final Field hostField; // Port: 80 private static final Field portField; Private static final Field urlField; private final HttpUrl httpUrl; static { Field scheme = null; Field host = null; Field port = null; Field url = null; try { scheme = HttpUrl.class.getDeclaredField("scheme"); port = HttpUrl.class.getDeclaredField("port"); host = HttpUrl.class.getDeclaredField("host"); url = HttpUrl.class.getDeclaredField("url"); } catch (NoSuchFieldException e) { e.printStackTrace(); } urlField = url; hostField = host; portField = port; schemeField = scheme; } public HttpUrl getHttpUrl() { return httpUrl; } public void setHostField(String host) { try { hostField.setAccessible(true); hostField.set(httpUrl, host); } catch (IllegalAccessException e) { e.printStackTrace(); } } public void setUrlField(String url) { try { urlField.setAccessible(true); urlField.set(httpUrl, url); } catch (IllegalAccessException e) { e.printStackTrace(); } } public void setSchemeField(String scheme) { try { schemeField.setAccessible(true); schemeField.set(httpUrl, scheme); } catch (IllegalAccessException e) { e.printStackTrace(); } } public void setPortField(int port) { try { portField.setAccessible(true); portField.set(httpUrl, port); } catch (IllegalAccessException e) { e.printStackTrace(); } } private BaseUrlHelper(HttpUrl httpUrl) { this.httpUrl = httpUrl; } public static BaseUrlHelper getInstance() { return Instance.getInstance(); } private static class Instance { private static final BaseUrlHelper helper = new BaseUrlHelper( HttpUrl.get(getBaseApi())); public static BaseUrlHelper getInstance() { return helper; } // BaseApi generation, See the previous section for details. Private static String getBaseApi() {String URL = BUtilsKt.getSpValue(BaseApplication.Companion.getContext(), "base_api", BuildConfig.BASE_API, "local_data"); return url; }}}Copy the code

The BaseUrlHelper class provides methods to modify BaseUrl properties as well as HttpUrl properties. For more information, see HttpUrl properties. I define the default BaseUrl based on my own requirements. You can refer to the configuration in the previous section to implement your own requirements.

How do I use BaseUrlHelper?

Retrofit. The Builder (). The baseUrl (BaseUrlHelper getInstance () httpUrl) / / look here .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build()Copy the code

Where you want to make changes, call the following method:

 BaseUrlHelper.getInstance().setUrlField(BASE_URL_API_RELEASE)
Copy the code

Great, Get another skill without damaging a single brain cell. Give it a thumbs up and do it.