手写简易版 Retrofit框架

// 如果 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);

}

});

}

/**

  • 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;

}

}

2.2、ServiceMethod


package com.test.retrofittest2.retro;

import java.lang.annotation.Annotation;

import java.lang.reflect.Method;

import okhttp3.Call;

import okhttp3.FormBody;

import okhttp3.HttpUrl;

import okhttp3.Request;

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;

}

}

// 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);

}

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.3、ParameterHandler


package com.test.retrofittest2.retro;

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);

}

}

}

2.4、Api


package com.test.retrofittest2;

import com.test.retrofittest2.retro.Field;

import com.test.retrofittest2.retro.GET;

import com.test.retrofittest2.retro.POST;

import com.test.retrofittest2.retro.Query;

public interface Api {

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

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

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

okhttp3.Call postWeather(@Field(“city”) String city, @Field(“key”) String key);

}

2.5、MainActivity


package com.test.retrofittest2;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import com.test.retrofittest2.retro.SimpleRetrofit;

import java.io.IOException;

import okhttp3.Call;

import okhttp3.Callback;

import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

private static final String TAG = “MainActivity”;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

findViewById(R.id.btn_get).setOnClickListener(view -> getRequest());

findViewById(R.id.btn_post).setOnClickListener(view -> postRequest());

}

private void postRequest() {

SimpleRetrofit retrofit = new SimpleRetrofit.Builder().baseUrl(“https://restapi.amap.com”).build();

Api api = retrofit.create(Api.class);

Call getCall = api.postWeather(“北京”, “13cb58f5884f9749287abbead9c658f2”);

getCall.enqueue(new Callback() {

@Override

public void onFailure(Call call, IOException e) {

}

@Override

public void onResponse(Call call, Response response) throws IOException {

Log.i(TAG, "onResponse: " + response.body().string());

}

});

}

private void getRequest() {

SimpleRetrofit retrofit = new SimpleRetrofit.Builder().baseUrl(“https://restapi.amap.com”).build();

Api api = retrofit.create(Api.class);

Call getCall = api.getWeather(“北京”, “13cb58f5884f9749287abbead9c658f2”);

getCall.enqueue(new Callback() {

@Override

public void onFailure(Call call, IOException e) {

}

@Override

public void onResponse(Call call, Response response) throws IOException {

Log.i(TAG, "onResponse: " + response.body().string());

}

});

}

}

2.6、布局文件


<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout

xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

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

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

-0PfxAAbx-1713619447739)]

[外链图片转存中…(img-4jLk84fX-1713619447741)]

[外链图片转存中…(img-xKZJdMGl-1713619447742)]

[外链图片转存中…(img-nLy7P3Vr-1713619447743)]

[外链图片转存中…(img-nOTqVnET-1713619447744)]

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-32U96UZS-1713619447746)]

【算法合集】

[外链图片转存中…(img-xD1ksPKj-1713619447746)]

【延伸Android必备知识点】

[外链图片转存中…(img-mqQjoMHD-1713619447747)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值