美文网首页
Retrofit之动态更换BaseUrl以及根据Retrofit

Retrofit之动态更换BaseUrl以及根据Retrofit

作者: 小仙女喂得猪呀 | 来源:发表于2022-01-10 15:06 被阅读0次

    我懂,先上关键代码:
    第一步:自定义注解,urlKey表示一个key,用来获取对应的baseUrl,需要自定本地提前存好对应的urlKey和baseurl对应起来

    @Documented
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    /**
     * 自定义Retrofit域名注解
     */
    public @interface UrlToDomainName {
        String urlKey();
    }
    

    第二步:给自己IService中的请求增加对应的UrlToDomainName注解

    public interface IService{
        @UrlToDomainName(url=DomainUrlConstantUtil.TYPE_CDN_HOME_URL)
        @GET
        Observable<ResponseResult<PageBean>> getMenus(@Url String url);
    }
    

    第三步:自定义拦截器DomainInterceptor,解析替换

    /**
     * @Author : jiyajie
     * @Time : On 2022/1/5 17:45
     * @Description : DomainInterceptor
     * 自定义域名拦截器 用于动态更换baseUrl
     */
    public class DomainInterceptor implements Interceptor {
        private DomainParserInterceptor mDomainParser;
    
        public DomainInterceptor(DomainParserInterceptor domainParser) {
            mDomainParser = domainParser;
        }
    
        @NonNull
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            return chain.proceed(processRequest(chain.request()));
        }
    
        /**
         * 处理请求,切换 BaseUrl
         * @param request
         * @return
         */
        private Request processRequest(Request request){
            //如果支持动态配置 BaseUrl
            Invocation invocation = request.tag(Invocation.class);
                if(invocation != null){
                    /*
                    在RequestFactory的create方法下 返回值为当前请求的Request,通过源码可见每一个Request都会关联一个Invocation,
                    Invocation中构造方法中有两个参数一个为method另一个为argumentList请求参数,因为我们定义的注解会在运行时存在,所以
                    可以通过method获取对应的注解对象
                     */
                    UrlToDomainName domainName = invocation.method().getAnnotation(UrlToDomainName.class);
                    if(domainName != null){
                        String value = domainName.url();
                        String domain = GrayAndCdnUrlTools.getGrayOrCdnDomainName(value);
                        HttpUrl domainUrl = HttpUrl.parse(domain);
                        HttpUrl url = request.url();
                        Logger.i("DomainInterceptor inner domainName="+value+" url="+url);
                        if (domainUrl != null) {
                            HttpUrl httpUrl = mDomainParser.parserDomain(domainUrl,request.url());
                            Logger.i("DomainInterceptor inner httpUrl="+httpUrl);
                            //如果不为空,则切换 BaseUrl
                            if(httpUrl != null){
                                return request.newBuilder()
                                        .url(httpUrl)
                                        .build();
                            }
                        }
                    }
                }
    
                HttpUrl baseUrl = HttpUrl.parse(DomainUrlConstantUtil.TYPE_CDN_HOME_DOMAIN);
                if(baseUrl != null){
                    HttpUrl httpUrl = mDomainParser.parserDomain(baseUrl,request.url());
                    Logger.i("DomainInterceptor outtor httpUrl="+httpUrl);
                    //如果不为空,则切换 BaseUrl
                    if(httpUrl != null){
                        return request.newBuilder()
                                .url(httpUrl)
                                .build();
                    }
                }
    
            return request;
    
        }
    
    }
    
    

    工具类DomainParserInterceptor,ParserDomainInterceptor

    public interface ParserDomainInterceptor {
        HttpUrl parserDomain(@NonNull HttpUrl domainUrl, @NonNull HttpUrl httpUrl);
    }
    
    public class DomainParserInterceptor implements ParserDomainInterceptor {
    
        private LruCache<String,String> mCacheUrlMap;
    
        public DomainParserInterceptor(int cacheSize) {
            mCacheUrlMap = new LruCache<>(cacheSize);
        }
    
        @Override
        public HttpUrl parserDomain(@NonNull HttpUrl domainUrl, @NonNull HttpUrl httpUrl) {
            HttpUrl.Builder builder = httpUrl.newBuilder();
            //优先从缓存里面取
            String url = mCacheUrlMap.get(getUrlKey(domainUrl,httpUrl));
            if(TextUtils.isEmpty(url)){
                for (int i = 0; i < httpUrl.pathSize(); i++) {
                    builder.removePathSegment(0);
                }
                List<String> strings = domainUrl.encodedPathSegments();
                List<String> strings1 = httpUrl.encodedPathSegments();
                Logger.i("DomainParserInterceptor: domainUrl.encodedPathSegments"+strings);
                Logger.i("DomainParserInterceptor: httpUrl.encodedPathSegments()"+strings1);
    
                List<String> pathSegments = new ArrayList<>();
    //            pathSegments.addAll(domainUrl.encodedPathSegments());
                pathSegments.addAll(httpUrl.encodedPathSegments());
    
                for (String pathSegment : pathSegments) {
                    builder.addEncodedPathSegment(pathSegment);
                }
            }else{
                builder.encodedPath(url);
            }
            /*
              scheme: 协议名称 http/https
              port: uriPort
              host: uriHost
              在Okhttp-->Address.kt中提供了url的构建方式,参考写法如下
             */
            Logger.i("DomainParserInterceptor: domainUrl.host:"+domainUrl.host()+" domainUrl.port():"+domainUrl.port());
            Logger.i("DomainParserInterceptor: httpUrl.host:"+httpUrl.host()+" httpUrl.port():"+httpUrl.port());
            HttpUrl resultUrl = builder.scheme(domainUrl.scheme())
                    .host(domainUrl.host())
                    .port(domainUrl.port())
                    .build();
    
            if(TextUtils.isEmpty(url)){
                //缓存 Url
                mCacheUrlMap.put(getUrlKey(domainUrl,httpUrl),resultUrl.encodedPath());
            }
            return resultUrl;
        }
    
        /**
         * 获取用于缓存 Url 时的 key,
         * @return 返回key
         */
        private String getUrlKey(@NonNull HttpUrl domainUrl, @NonNull HttpUrl currentUrl){
            return String.format("%s_%s",domainUrl.encodedPath(),currentUrl.encodedPath());
        }
    }
    

    关键技术点:
    1.拦截器
    2.java 注解相关知识点
    3.为什么使用Invocation去获取注解(顺带分析整个Retrofit的执行流程)
    本文均以图文结合的形式去分析


    1.拦截器源码执行流程:
    Interceptor(Chain) --> RealInterceptorChain
    参考模拟实现解析形式
    Intercept 执行逻辑
    Chain 抽象方法
    用HttpLoggingInterceptor进行分析:


    image.png

    2.java注解相关知识点


    3458176-c5642804973c8b6e.jpeg

    3.为什么使用invocation
    Retrofit.create()-->validateServiceInterface(service)-->loadServiceMethod(method)
    -->ServiceMethod.parseAnnotations(retrofit,method)
    -->HttpServiceMethod.parseAnnotations(retrofit,method,requesFactory)第三个参数RequestFactory很关键是整个retrofit请求的核心类,用于解析参数,解析注解,
    解析方法,解析header等 ,具体原因在主线流程结束的前一个框内, 通过源码追踪分析具体执行见流程图:


    retrofit执行流程如图.png

    希望能够帮助到看到这里的你,有不当之处希望能多多指点哟

    相关文章

      网友评论

          本文标题:Retrofit之动态更换BaseUrl以及根据Retrofit

          本文链接:https://www.haomeiwen.com/subject/xsbbcrtx.html