概述
本文中升级http基于前章中整理的网络通信组件,其实在实际开发中我们网络请求非常麻烦,如果高效有质量通信,非常重要,而这次我要介绍的如果加入动态代理和注解技术。当前版本3.0.github地址:
https://github.com/apple317/HttpClientUtils
目前支持
- 支持动态代理
- 支持注解技术
- 一般的get请求
- 一般的post请求 支持文本、表单、表单文件、单个文件操作 支持
- 基于Http Post的文件上传(类似表单) 支持
- 文件上传进度 支持
- 下载文件进度 支持
- 支持url关闭网络请求 支持
- 支持session的保持 支持
- 支持取消某个请求 支持
- 支持数据异步刷新操作
- 支持取消tag请求、支持url取消请求 支持
- 支持HEAD 支持
- 支持自签名网站https的访问,提供方法设置下证书就行 不支持
- DELETE、PATCH、PUT 支持
- 加入网络配置、请求过程抽离 支持
- 加入gson解析,建议加入插件gsonformat来生成解析对象,传入解析类方法setParse
- 加入url表识来区分请求 支持
动态代理技术
其他对象提供一种代理以控制对这个对象的访问,当我们用http访问请求时,如果每次都去申请同样请求方式、请求方法、请求参数等,这时候就需要一个同一接口来定义统一实现,这样方便维护更好扩展,当然这时候光用动态代理,只能统一接口实现,哪请求传入哪些,如果全手动去代入也不够方便,这时候加入注解注入技术+动态代理技术+注解缓冲,更好达到Retrofit技术。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos();
repos.enqueue(new Callback<List<Repo>>(){
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response){
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t){
}
});
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
上面看到这个create方法:
动态代理返回范型类对象,其实最重要代码首先使用Proxy,他作用就是当你传入接口类,当调用者调用其中方法,Proxy就是会调用4步操作:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))。
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))。
而当调用到invoke方法时候:
proxy:代理类对象
method:调用方法
args:方法传入数值。
注解注入参数:
ServiceMethod serviceMethod = loadServiceMethod(method);此类中全是获取注解的类。
缓冲注解参数:
下面方法看到serviceMethodCache变量,其实就是Map类型,通过method存下所有注解方式数值,这样第二次调用直接调用map中。
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
以上全部就是retrofit技术要点。
升级版本
例子:
看到定义http接口,这样可以统一把http放到一起,默认请求post_FORM,可以不用传,解析类必须传,如果么有解析类,也可以不用传。httpBaseUrl是注解传入接口域名前缀地址。
public interface LoginApiStores {
@HttpMehod(METHOD.POST_FORM)
@HttpParse(BaseBean.class)
@HttpBaseUrl("https://api.hushaoping.com/v2/")
void getHomePageData(@FieldParam BaseParams baseParams);
}
/**
* 首页菜单
*/
public void getHomepageQuickEntryList(HttpCallback httpClick) {
AppBaseParams appBaseParams = new AppBaseParams();
appBaseParams.put("app_version_id", "234");
appBaseParams.put("app_market_id", "23");
appBaseParams.put("app_platform_id", "1");
appBaseParams.put("user_id", "");
BaseHttpClient.newBuilder().urlIdentifier("getHomePageData").build().execute(LoginApiStores.class,
new HttpCallback() {
@Override
public void success(String content, BaseHttpClient client, Object parse) {
BaseBean bean=(BaseBean)parse;
Log.e("HU","content======content==="+bean.getRetCode());
}
@Override
public void error(Throwable error, BaseHttpClient client) {
}
}
).getHomePageData(appBaseParams.signParams());
}
Api介绍
HttpMethod:传入请求方式post/get等.
HttpParse:传入请求解析类.
HttpBaseUrl:传入请求url地址。
urlIdentifier:请求返回唯一标识.
源码
/**
* 注解方式
* 反射
*
* @param service
* @param click
* @param <T>
* @return
*/
public <T> T execute(final Class<T> service, final BaseCallback click) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (args.length > 0) {
BaseParams params = (BaseParams) args[0];
loadServiceMethod(click, method, params);
}
return proxy;
}
});
}
/**
* @param click
* @param method
* @param baseParams
*/
public void loadServiceMethod(BaseCallback click, Method method, BaseParams baseParams) {
BaseHttpClient baseHttpClient;
synchronized (serviceMethodCache) {
baseHttpClient = serviceMethodCache.get(method);
if (baseHttpClient == null) {
for (Annotation annotation : method.getAnnotations()) {
if (annotation instanceof HttpMehod) {
this.method = ((HttpMehod) annotation).value();
} else if (annotation instanceof HttpParse) {
this.parse = ((HttpParse) annotation).value();
} else if (annotation instanceof HttpBaseUrl) {
this.url = ((HttpBaseUrl) annotation).value()+method.getName();
}
}
baseHttpClient = new Builder()
.url(this.url).method(this.method).setParse(this.parse)
.build();
serviceMethodCache.put(method, baseHttpClient);
} else {
this.method = baseHttpClient.getMethod();
this.parse = baseHttpClient.getParse();
this.url = baseHttpClient.getUrl();
baseHttpClient = this;
}
if (this.url.equals("")) {
baseHttpClient.url = getConfiguration().getBaseUrl() + method.getName();
}
baseHttpClient.mParams = baseParams;
getHttpImpl().execute(baseHttpClient, click);
}
}