-
Notifications
You must be signed in to change notification settings - Fork 7.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Builder with clear/addConverterFactory wrong behavior #4240
Comments
I cannot reproduce this. I wrote this test: @Test public void issue4240() {
Converter.Factory factory1 = new Converter.Factory() {
@Override
public String toString() {
return "factory1";
}
};
Converter.Factory factory2 = new Converter.Factory() {
@Override
public String toString() {
return "factory2";
}
};
Converter.Factory factory3 = new Converter.Factory() {
@Override
public String toString() {
return "factory3";
}
};
Retrofit original = new Retrofit.Builder()
.baseUrl("http://example.com")
.addConverterFactory(factory1)
.build();
System.out.println("Original: " + original.converterFactories());
Retrofit.Builder builder1 = original.newBuilder();
Retrofit.Builder builder2 = original.newBuilder();
System.out.println("Builder 1 initial: " + builder1.converterFactories());
System.out.println("Builder 2 initial: " + builder2.converterFactories());
builder1.converterFactories().clear();
builder2.converterFactories().clear();
System.out.println("Builder 1 cleared: " + builder1.converterFactories());
System.out.println("Builder 2 cleared: " + builder2.converterFactories());
builder1.addConverterFactory(factory2);
builder2.addConverterFactory(factory3);
System.out.println("Builder 1 added: " + builder1.converterFactories());
System.out.println("Builder 2 added: " + builder2.converterFactories());
Retrofit new1 = builder1.build();
Retrofit new2 = builder2.build();
System.out.println("Original: " + original.converterFactories());
System.out.println("New 1: " + new1.converterFactories());
System.out.println("New 2: " + new2.converterFactories());
} which produces:
If you can modify my test to produce the errant behavior, I'll re-open. However, you can also see this is impossible by looking at the source. When we create the retrofit/retrofit/src/main/java/retrofit2/Retrofit.java Lines 686 to 713 in 54dd2f5
When a new builder is created from an existing instance, all of its factories are copied into brand new lists which are mutable: retrofit/retrofit/src/main/java/retrofit2/Retrofit.java Lines 486 to 512 in 54dd2f5
|
Retrofit.Builder or retrofit.newBuilder shares same instance for converters. So you are not able to add / remove converter and don't affect original instance. There is missing some copy function in newBuilder.
For example, we need two different converters, due to external library (which need GSON converter), but for app we uses KotlinX Serialization.
In that case, we expect solution like:
But it affects the origin instance as well! Because newBuilder didn't copy instance but just reuse origin one. So
converterFactories().clear()
remove from origin retrofit existing converter and add new oneaddConverterFactory(converterFactory)
. So new request to external library start failing due to wrong converters.Same solution is, if you create singleton instance without converters and try to add later:
But if you will call second instance with different converter, it will not print to console 1 but 2 for converters. So builder again reuses same instance.
Workaround is, not to use newBuilder and not use
@Singleton
for@RetrofitQualifier
. So we create 2 (or more) instances of base retrofit. But it's not much clear and we spend a lot of time checking what's wrong with requests.Expected result:
How to test it:
Create two APIs which uses different converter + serialization with name conversion:
vs
If you will try to use converter for names
data > myData
it will fails when wrong convertor is used (because it will fry to parsemyData
entity instead ofdata
entity).The text was updated successfully, but these errors were encountered: