动态接口的实现有很多好处,今天以HTTP协议解析json字符串为例。
基本知识
1、JAVA中我们有时候想要传递一个方法,在C/C++中有方法指针,而在JAVA中,我们可以使用的唯一方法就是接口。所以我们一个接口来传递其中的方法。
2、我们今天是想要从网上获取一个json字符串,然后再用Gson方式去解析得到想要的对象。选择使用动态接口的方式去解决是为了节省代码,不必要在不同的地址去获取json串时还需要去写HTTP协议那一套的连接下载。
2、下载具体步骤
1、首先我们需要建立json串对应的相关对象。在这里,我们需要为每个对象添加注解,因为在fastJson、Gson中不加注解容易混淆。
2、我们是解析这个地址的相关信息。http://www.tngou.net/api/top/list
// TnGou类
package net.xiaohong.proxy;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class TnGou {
@SerializedName("status")
private boolean status;
@SerializedName("total")
private int total;
@SerializedName("tngou")
private List<News> data;
// 为了节省空间,大家需要自行解决getter、setter,嘿嘿
}
// News类
package net.xiaohong.proxy;
import com.google.gson.annotations.SerializedName;
import java.util.Calendar;
/**
* Created HTTP_Work.
* By XIAOHONG .
* On 2016/8/18.
*/
public class News {
@SerializedName("count")
private int count;
@SerializedName("description")
private String description;
@SerializedName("fCount")
private int fCount;
@SerializedName("fromName")
private String fromName;
@SerializedName("fromURL")
private String fromURL;
@SerializedName("id")
private int id;
@SerializedName("img")
private String img;
@SerializedName("keyWords")
private String keyWords;
@SerializedName("rCount")
private int rCount;
// 毫秒值,做转换
@SerializedName("time")
@JsonAdapter(CalendarAdapter.class)
private Calendar time;
@SerializedName("title")
private String title;
@SerializedName("topClass")
private int topClass;
// 为了节省空间,大家需要自行解决getter、setter,嘿嘿
}
类型适配器,在gson解析中如果拿不到想要的类型,可以对其进行类型适配,比如时间
// 时间类型比配。会返回一个时间类
package com.xiaohong.proxy;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.Calendar;
public class CalendarAdapter extends TypeAdapter<Calendar> {
// 写Calendar ->json
@Override
public void write(JsonWriter jsonWriter, Calendar calendar) throws IOException {
if (calendar == null) {
jsonWriter.nullValue();
} else {
jsonWriter.value(calendar.getTimeInMillis());
}
}
// 读,将json->Calendar
@Override
public Calendar read(JsonReader jsonReader) throws IOException {
if (jsonReader.hasNext()) {
long l = jsonReader.nextLong();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(l);
return calendar;
}
return null;
}
}
要实现动态接口,需要把自己想要的方法定义在接口中。
package net.xiaohong.proxy;
public interface HttpInter {
@GET("http://www.tngou.net/api/top/list?page=%d&rows=%d&id=%d")
TnGou getList(int page, int rows, int id);
}
在这里,我使用到了注解,通过注解来获取想要的方式(GET、POST)
package net.xiaohong.proxy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value();
}
往下需要一个工具类去实现动态接口的方式,我们需要将HttpInter 作为参数,通过这个接口拿到这里面的方法,进而,我们再去执行相关的下载操作。
// 动态接口实现类
package net.xiaohong.proxy;
import com.google.gson.Gson;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.HttpURLConnection;
import java.net.URL;
public class Utils {
public static<T> T getInstance(Class<T> type) {
Object o = Proxy.newProxyInstance(type.getClassLoader(),
new Class[]{type},
new MyHandler());
return (T) o;
}
private static class MyHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拿到方法的注解类型,因为在这里我默认就让他使用GET了
GET get = method.getAnnotation(GET.class);
//
if (get != null) {
String url = String.format(get.value(), args);
Class<?> type = method.getReturnType();
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
int code = connection.getResponseCode();
if (code == 200) {
int length;
byte[] buffer = new byte[102400];
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((length = is.read(buffer)) != -1) {
baos.write(buffer, 0, length);
}
Gson gson = new Gson();
return gson.fromJson(baos.toString("UTF-8"), type);
}
}
return null;
}
}
}
// 测试类
package net.xiaohong.proxy;
import java.util.List;
public class HttpTest {
public static void main(String[] args) {
HttpInter instance = Utils.getInstance(HttpInter.class);
TnGou list = instance.getList(1, 5, 0);
// News show = instance.getShow(2);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
List<News> data = list.getData();
for (News news : data) {
System.out.print(format.format(news.getTime().getTime()) + ": ");
System.out.println(news.getTitle());
}
}
}
运行结果:
到这里整个项目就实现了。这样使用动态接口的好处:我们可以在对不同的地址获取不同的json进行解析成对象。集体方法:
1、我们需要在HttpInter接口中添加我们想要的方法:比如我们想要获取这样的数据:
@GET("http://www.tngou.net/api/top/show?id=%d")
News getShow(long id);
}
此时我们需要在News类中添加一个属性:
@SerializedName("message")
private String message;
测试类中:
News show = instance.getShow(2);
System.out.println(show.getMessage());
运行结果: