Singleton for Retrofit and Network Request Optimization

Singleton for Retrofit and Network Request Optimization


I. Background

Background: Currently, most requests in the project are made using Retrofit by declaring interfaces to request the network. However, there are issues with this approach, such as creating a new interface instance for each network request, which unnecessarily consumes resources. Additionally, old business logic that uses HttpClient for network requests poses a risk of memory leaks due to listeners being held, and the internal encapsulation of RxJava operators fails to fully leverage the advantages of RxJava. Many requests do not use Gson for automatic serialization of objects but declare a JsonObject object as the return type and then serialize it in the main thread.

  • Technical level: Intermediate
  • Application scenario: Network requests
  • Overall approach:Retrofit Singleton: Provide RetrofitManager to cache the ApiService interfaces of each module uniformly, while also supporting each module to define its own Retrofit instance. For ease of use, if an ApiService needs to specify Retrofit, it only needs to declare an annotation on the Service's interface to provide the implementation class of Retrofit. Accessing its ApiService implementation only requires passing ApiService.class.Remove HttpClient: When adjusting the old Service code of each module to use RetrofitManager, requests that used HttpClient are unified to directly use RxJava callbacks, controlling network requests based on the lifecycle context to eliminate memory leak risks, and ultimately remove the HttpClient class.Use Gson for automatic serialization for requests that do not use Gson.Other optimizations:Adjust the method of creating OkHttpClient to unify parameters and avoid redundant instances.Remove the use of RxJava1.

II. Implementation

2.1 Retrofit Singleton Implementation

RetrofitService singleton caching implementation:

Unified method to get Retrofit singleton:

  • RetrofitManager.get(ApiService.class)

It allows the interface to define its own Retrofit instance and declares annotations in the corresponding Service interface.

@Provider(ServiceGenerator.MapRetrofitProvider.class)
public interface OrangeDotServiceApi {
    // Request for the orange dot
    @GET("xxxx")
    Observable<Response<ResultX<SuggestLocInfo>>> getOrangeDot(@QueryMap HashMap<String, Object> params);
}        

Implementation class for a custom Retrofit instance:

public class ServiceGenerator {
    public static class MapRetrofitProvider implements RetrofitProvider {
        @Override
        public Retrofit createRetrofit(Retrofit.Builder builder) {
            String baseUrl = ApiUtils.getMeta2(Utils.getContext()).getLbs_rgeo_url();
            if (TextUtils.isEmpty(baseUrl)) {
                baseUrl = https://github.com/MicroKibaco;
            }
            return builder.client(OkHttpClientManager.getOkhttpClient()).baseUrl(baseUrl).build();
        }
    }
}
        

Using RetrofitManager to make requests:

private void requestByMapAPi(SuggestRequest suggestRequest, Stop stop, LifecycleOwner lifecycleOwner) {
    RetrofitManager.get(OrangeDotServiceApi.class)
            .getOrangeDot(getRequestParamsMap(suggestRequest))
            .compose(RxjavaUtils.commonTransform())
            .as(AutoDisposeUtils.bindToLifecycle(lifecycleOwner))
            .subscribe(new Observer<Response<ResultX<SuggestLocInfo>>>() {
                // ...
            });
}        

RetrofitManager Implementation:

public class RetrofitManager {
    private static final Map<Class<?>, Object> sInstanceMap = new ConcurrentHashMap<>();
    
    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> service) {
        if (service == null) {
            return null;
        }
        Object instance = sInstanceMap.get(service);
        if (instance == null) {
            Retrofit retrofit;
            Provider provider = service.getAnnotation(Provider.class);
            if (provider != null) {
                try {
                    retrofit = provider.value().newInstance().createRetrofit(DefaultRetrofitProvider.getInternalRetrofitBuilder());
                } catch (IllegalAccessException | InstantiationException e) {
                    throw new RuntimeException("RetrofitProvider: " + provider.value().getSimpleName() + " creation failed");
                }
            } else {
                retrofit = DefaultRetrofitProvider.getDefaultRetrofit();
            }
            sInstanceMap.put(service, instance = retrofit.create(service));
        }
        return (T) instance;
    }
    
    public static void destroy(Class<?> cls) {
        sInstanceMap.remove(cls);
    }
    
    public static void reset() {
        sInstanceMap.clear();
    }
}
        


DefaultRetrofitProvider Implementation:

public class DefaultRetrofitProvider implements RetrofitProvider {
    private static Retrofit SIMPLE_RETROFIT;

    @Override
    public Retrofit createRetrofit(Retrofit.Builder builder) {
        return getDefaultRetrofit();
    }

    public static Retrofit getDefaultRetrofit() {
        if (SIMPLE_RETROFIT == null) {
            String baseUrl = ApiUtils.getMeta2(Utils.getContext()).getApiUrlPrefix2();
            if (TextUtils.isEmpty(baseUrl)) {
                baseUrl = https://github.com/MicroKibaco;
            }
            SIMPLE_RETROFIT = BUILDER.client(OkHttpClientManager.getOkhttpClient()).baseUrl(baseUrl).build();
        }
        return SIMPLE_RETROFIT;
    }

    public static Retrofit.Builder getInternalRetrofitBuilder() {
        return BUILDER;
    }

    private static final Retrofit.Builder BUILDER = new Retrofit.Builder()
            .addConverterFactory(StringConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(GsonUtil.getCollectionGson()))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create());
}
        


2.2 Refactoring Old Network Requests

Example of refactoring HttpClient network requests:

java// Before refactoring
private void getHotfix() {
    new HttpClient.Builder()
            .baseUrl(ApiUtils.getMeta2(this).getApiUrlPrefix2())
            .listener(new OnHttpResultListener<JsonObject>() {
                @Override
                public void onSuccess(JsonObject jsonObject) {
                    // ...
                }
                @Override
                public void onError(Throwable throwable) {
                }
            })
            .build()
            .request(new BaseApi<JsonObject>() {
                @Override
                public Observable<JsonObject> getObservable(Retrofit retrofit) {
                    return retrofit.create(MainApiServcie.class).vanGetHfdata(map);
                }
            });
}

// After refactoring
private void getHotfix() {
    RetrofitManager.get(MainApiServcie.class)
            .vanGetHfdata(map)
            .compose(RxjavaUtils.commonTransform())
            .as(AutoDisposeUtils.bindToLifecycle(this))
            .subscribe(new SingleObserver<JsonObject>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {
                }
                @Override
                public void onSuccess(@NonNull JsonObject jsonObject) {
                    // ...
                }
                @Override
                public void onError(@NonNull Throwable e) {
                }
            });
}
        

Refactoring ServiceGenerator custom RetrofitService network requests:

// Before refactoring
public class ServiceGenerator {
    public static <S> S createService(Class<S> serviceClass) {
        String baseUrl = ApiUtils.getMeta2(Utils.getContext()).getApiUrlPrefix2();
        if (TextUtils.isEmpty(baseUrl)) {
            baseUrl = https://github.com/MicroKibaco;
        }
        Retrofit retrofit = builder.baseUrl(baseUrl)
                .client(OkHttpClientManager.getOkhttpClient())
                .build();
        return retrofit.create(serviceClass);
    }
}

private void getOrder() {
    ServiceGenerator.createService(FreightApiService.class)
        .orderRepeat(param)
        .compose(RxjavaUtils.commonTransform())
        .compose(RxProgress.bindToLifecycle(activity))
        .as(AutoDisposeUtils.bindToLifecycle(activity.getLifecycle()))
        .subscribe();
}

// After refactoring
private void getOrder() {
    RetrofitManager.get(FreightApiService.class)
        .orderRepeat(param)
        .compose(RxjavaUtils.commonTransform())
        .compose(RxProgress.bindToLifecycle(activity))
        .as(AutoDisposeUtils.bindToLifecycle(activity.getLifecycle()))
        .subscribe();
}        

III. Summary

Summary of the code and experience accumulated from this technical application:

  1. Adjust RxjavaUtils.commonTransform to ensure many callbacks are not executed on the main thread.
  2. Further optimization points for the network:Identify and eliminate redundant requests to reduce backend load.Encapsulate common parameters in callbacks within interfaces.Address the issue of weak networks not canceling loading states; bind loading states appropriately.Optimize network timeout settings.

要查看或添加评论,请登录

ZhengYou Yang的更多文章

  • Cronet Metrics Monitoring

    Cronet Metrics Monitoring

    iOS How to Enable Metrics Monitoring It's crucial to initiate metrics monitoring before calling , as many configuration…

  • How to Download and Compile Cronet Source Code

    How to Download and Compile Cronet Source Code

    Introduction This blog will guide you through the steps to download and compile the Cronet source code. Cronet is a…

  • Analysis of Cronet Source Code - Native Implementation

    Analysis of Cronet Source Code - Native Implementation

    JNI Header Files Compiled outputs can be found in the directory. The key header files include: Set Minimum Log Level…

  • Analysis of Cronet Source Code - Java Implementation

    Analysis of Cronet Source Code - Java Implementation

    Based on Chromium Tag 94.0.

  • SDK Design Specifications

    SDK Design Specifications

    Meeting Functional Requirements Firstly, when designing an SDK, it is essential to consider user needs, define…

  • Screen Adaptation Plan

    Screen Adaptation Plan

    Background Due to the severe fragmentation of Android devices, the same layout displays differently on various phones…

  • Screen Adaptation

    Screen Adaptation

    PPI & DPI PPI PPI (Pixels Per Inch) refers to the number of pixels in one inch of a screen. Typically, pixels are…

  • Layout and UI Optimization

    Layout and UI Optimization

    Background Project Context: In the iterative process of project version implementation, some pages have complex UI…

  • Using Charles for Packet Capture

    Using Charles for Packet Capture

    Download Link Charles Web Debugging Proxy ? HTTP Monitor / HTTP Proxy / HTTPS & SSL Proxy / Reverse Proxy Cracked…

  • Computer Network Security

    Computer Network Security

    A. Draw the current LAN network for the above case description.

社区洞察

其他会员也浏览了