拼多多前端面试,手写简易版 Retrofit框架,大厂面试经验分享

private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();

public T create(final Class service) {

return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},

new InvocationHandler() {

@Override

public Object invoke(Object object, Method method, Object[] args) throws Throwable {

ServiceMethod serviceMethod = loadServiceMethod(method);

return serviceMethod.invoke(args);

}

});

}

/**

  • DLC 方式获取 ServiceMethod,如果没有解析过就解析该方法

  • @param method 动态代理执行的接口方法

  • @return 解析后的方法对象

*/

private ServiceMethod loadServiceMethod(Method method) {

// 先不加锁,避免性能损失

ServiceMethod serviceMethod = serviceMethodCache.get(method);

if (serviceMethod != null) return serviceMethod;

// 避免多线程下重复解析

synchronized (serviceMethodCache) {

serviceMethod = serviceMethodCache.get(method);

if (serviceMethod == null) {

serviceMethod = new ServiceMethod.Builder(this, method).build();

serviceMethodCache.put(method, serviceMethod);

}

}

return serviceMethod;

}

在 ServiceMethod 的 Builder 中解析方法和参数注解:

public class ServiceMethod {

private final String httpMethod;

private final Call.Factory callFactory;

private final HttpUrl baseUrl;

private final String relativeUrl;

private final ParameterHandler[] parameterHandlers;

private FormBody.Builder formBuilder;

private FormBody formBody;

private HttpUrl.Builder urlBuilder;

public ServiceMethod(Builder builder) {

baseUrl = builder.retrofit.baseUrl;

callFactory = builder.retrofit.callFactory;

httpMethod = builder.httpMethod;

relativeUrl = builder.relativeUrl;

parameterHandlers = builder.parameterHandlers;

boolean hasBody = builder.hasBody;

// 如果有请求体,创建一个 OKHttp 请求体对象

if (hasBody) {

formBuilder = new FormBody.Builder();

}

}

static class Builder {

private final SimpleRetrofit retrofit;

private final Method method;

private final Annotation[] annotations;

// 方法上有 n 个参数,每个参数又有 m 个注解,用一个 nxm 的数组保存

private final Annotation[][] parameterAnnotations;

String httpMethod;

boolean hasBody;

String relativeUrl;

ParameterHandler[] parameterHandlers;

public Builder(SimpleRetrofit retrofit, Method method) {

this.retrofit = retrofit;

this.method = method;

annotations = method.getAnnotations();

parameterAnnotations = method.getParameterAnnotations();

}

public ServiceMethod build() {

// 解析方法上的注解

for (Annotation annotation : annotations) {

if (annotation instanceof GET) {

parseHttpMethodAndPath(“GET”, ((GET) annotation).value(), false);

} else if (annotation instanceof POST) {

parseHttpMethodAndPath(“POST”, ((POST) annotation).value(), true);

}

}

// 解析方法参数上的所有注解,把注解值存入 ParameterHandler[] 中

int length = parameterAnnotations.length; // 方法上的参数个数

parameterHandlers = new ParameterHandler[length];

for (int i = 0; i < length; i++) {

// 一个参数上的所有注解

Annotation[] annotations = parameterAnnotations[i];

parameterHandlers[i] = parseParameter(annotations);

}

return new ServiceMethod(this);

}

private ParameterHandler parseParameter(Annotation[] annotations) {

// 根据注解类型创建对应的 ParameterHandler

ParameterHandler result = null;

for (Annotation annotation : annotations) {

ParameterHandler annotationAction = parseParameterAction(annotation, annotations);

// 如果当前检查的注解并不是我们能处理的,就继续遍历下一个

if (annotationAction == null) {

continue;

}

// 如果 result 不为 null 说明之前遍历时已经找到了 SimpleRetrofit 能处理的注解

// 不允许一个参数上被多个 SimpleRetrofit 的参数注解标注,抛异常

if (result != null) {

throw new IllegalArgumentException(“Multiple Retrofit annotations found, only one allowed.”);

}

result = annotationAction;

}

// 遍历完都没找到说明这个参数没有被 SimpleRetrofit 注解标注,不应该被检查

if (result == null) {

throw new IllegalArgumentException(“No Retrofit annotation found.”);

}

return result;

}

private ParameterHandler parseParameterAction(Annotation annotation, Annotation[] annotations) {

if (annotation instanceof Query) {

String key = ((Query) annotation).value();

return new ParameterHandler.QueryParameterHandler(key);

} else if (annotation instanceof Field) {

String key = ((Field) annotation).value();

return new ParameterHandler.FieldParameterHandler(key);

}

return null;

}

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {

// 规定一个方法上只能有一个 httpMethod 注解,否则抛出异常

if (this.httpMethod != null) {

String message = String.format(“Only one HTTP method is allowed. Found: %s and %s.”,

this.httpMethod, httpMethod);

throw new IllegalArgumentException(message);

}

this.httpMethod = httpMethod;

this.hasBody = hasBody;

if (value == null) {

return;

}

this.relativeUrl = value;

}

}

}

build() 中负责解析方法注解和方法参数注解。解析方法注解主要是为了获取使用哪种 HTTP 方法(GET、POST)、是否有请求体以及相对地址;解析方法参数注解是为了把被 @Field 或 @Query 注解的参数的值,即网络请求的 key 保存在 ParameterHandler[] 中。比如说对于 getWeather():

@GET(“/v3/weather/weatherInfo”)

Call getWeather(@Query(“city”) String city, @Query(“key”) String key);

@Query 注解的值分别为 city、key,那么就把 city 和 key 分别传入 ParameterHandler[] 中保存,而这两个 key 对应的 value 会在调用 ServiceMethod 的 invoke() 方法时传入。

ParameterHandler 的作用是保存网络请求的 key,并把 key-value 回调给 ServiceMethod:

public abstract class ParameterHandler {

abstract void apply(ServiceMethod serviceMethod, String value);

static class QueryParameterHandler extends ParameterHandler {

String key;

public QueryParameterHandler(String key) {

this.key = key;

}

@Override

void apply(ServiceMethod serviceMethod, String value) {

serviceMethod.addQueryParameter(key, value);

}

}

static class FieldParameterHandler extends ParameterHandler {

String key;

public FieldParameterHandler(String key) {

this.key = key;

}

@Override

void apply(ServiceMethod serviceMethod, String value) {

serviceMethod.addFieldParameter(key, value);

}

}

}

回调方法一个是处理 GET 请求的,一个是处理 POST 请求的:

// get 请求,把 key-value 拼接到 url 中

public void addQueryParameter(String key, String value) {

if (urlBuilder == null) {

urlBuilder = baseUrl.newBuilder(relativeUrl);

}

urlBuilder.addQueryParameter(key, value);

}

// post 请求,把 key-value 放到请求体中

public void addFieldParameter(String key, String value) {

formBuilder.add(key, value);

}

最后在 invoke() 中生成 OKHttp 的 Request 对象并调用 CallFactory 的 newCall(Request) 生成 Call:

public Object invoke(Object[] args) {

for (int i = 0; i < parameterHandlers.length; i++) {

ParameterHandler parameterHandler = parameterHandlers[i];

parameterHandler.apply(this, args[i].toString());

}

if (urlBuilder == null) {

urlBuilder = baseUrl.newBuilder(relativeUrl);

}

HttpUrl httpUrl = urlBuilder.build();

if (formBuilder != null) {

formBody = formBuilder.build();

}

Request request = new Request.Builder().url(httpUrl).method(httpMethod, formBody).build();

return callFactory.newCall(request);

}

2、使用

====

注解见上,不重复写了。

2.1、SimpleRetrofit


package com.test.retrofittest2.retro;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

import okhttp3.Call;

import okhttp3.HttpUrl;

import okhttp3.OkHttpClient;

public class SimpleRetrofit {

HttpUrl baseUrl;

Call.Factory callFactory;

private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();

public SimpleRetrofit(Builder builder) {

this.baseUrl = builder.baseUrl;

this.callFactory = builder.callFactory;

}

public static class Builder {

HttpUrl baseUrl;

Call.Factory callFactory;

public Builder baseUrl(String baseUrl) {

this.baseUrl = HttpUrl.parse(baseUrl);

return this;

}

public SimpleRetrofit build() {

// 先做参数校验

if (baseUrl == null) {

throw new IllegalStateException(“Base URL required.”);

}

if (callFactory == null) {

callFactory = new OkHttpClient();

}

return new SimpleRetrofit(this);

}

}

@SuppressWarnings(“unchecked”)

public T create(final Class service) {

return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},

new InvocationHandler() {

@Override

public Object invoke(Object object, Method method, Object[] args) throws Throwable {

ServiceMethod serviceMethod = loadServiceMethod(method);

return serviceMethod.invoke(args);

}

});

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

这里我希望可以帮助到大家提升进阶。

内容包含:Android学习PDF+架构视频+面试文档+源码笔记高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

喜欢本文的话,不妨给我点个小赞、评论区留言或者转发支持一下呗~

img

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

+架构视频+面试文档+源码笔记**,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

喜欢本文的话,不妨给我点个小赞、评论区留言或者转发支持一下呗~

img

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值