在我们进行一些下载的工作时, 有一部分代码是重复的, 那么,我们每一次进行这样的工作的时候, 都要重复写这些代码, 这样是非常浪费时间哒. 所以, 我们可以将这部分代码封装起来, 形成一个工具包, 每次用的时候只要导入就可以啦(这里只是针对网络数据, 不包含图片).
工具类NetWorkAsync
为了实现封装, 要把url作为一个属性, 通过构造方法传入值, 并且返回一个结果(Object 类型)
代码如下:
public class NetWorkAsync<T> extends AsyncTask<NetWorkAsync.Callback<T>, Void, Object> {
private Callback<T> callback;
private String url;
private Class<T> t;
public NetWorkAsync(String url, Class<T> t) {
this.url = url;
this.t = t;
}
}
这个类要继承AsyncTask, 并实现里面的几个方法. 在这几个方法里, 我们执行的工作有这么几个:
下载之前的准备工作(可以没有), 下载(必须有), 下载结束之后的判断工作(是否是空)
代码如下:
protected Object doInBackground(Callback<T>... params) {
callback = params[0];
try {
//新建一个网络连接, 并打开该链接
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
//为这个连接设置方法
connection.setRequestMethod("GET");
connection.setDoInput(true);
int code = connection.getResponseCode();
//下载
if (code == 200){
InputStream is = connection.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
String s = bos.toString("UTF-8");
//解析
Gson gson = new Gson();
return gson.fromJson(s, t);
}else {
return new RuntimeException("error");
}
} catch (IOException e) {
return e;
}
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
if (t.isInstance(o)){
//如果下载成功, 就回调onSuccess方法
callback.onSuccess((T) o);
}else if (o instanceof Exception){
//如果下载失败, 就回调onFail方法
callback.onFail((Exception) o);
}
}
无论成功与失败, 都要调用onSuccess/onFail方法, 将信息传送出去. 回调的接口在MainActivity中实现, 达到传递信息的功能
public interface Callback<S>{
void onSuccess(S text);
void onFail(Exception e);
}
我们可以观察到, 很多地方都用到了泛型T, 这就保证了在不同的情形下, 这个类就可以面向不同的类型使用.
添加注解
说到添加注解, 就会想到用动态接口去实现获取信息.
首先要先创建一个得到url的注解 代码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlString {
//得到url的方法
String url();
}
那么, 动态接口与普通接口有什么区别呢? 如果要对一个接口进行实例化, 就一定要实现这个接口, 再实例化. 动态接口就不需要实现这个接口, 可以直接通过一个工厂, 用getInstace方法得到实例 代码如下:
public static<T> T getInstace(Class<T> type) {
Object o = Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, new MyHandler());
return ((T) o);
}
通过观察newProxyInstance方法我们可以发现, 需要有一个Handler参数, 所以, 我们要写一个Handler来实现自己的需求.
在写Handler之前, 我们要知道, 为什么需要这样一个Handler呢? 之前讲到, 动态接口是不需要实现的, 那么, 也就是说, 这个接口里的方法是没有没有实现方法体的, 那么, 我们的需求在哪里实现呢? 在Handler中, 我们会实现一个invoke方法, 在调用到接口中的方法时, 会自动跳转进入invoke方法.
private static class MyHandler implements InvocationHandler{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
UrlString annotation = method.getAnnotation(UrlString.class);
if (annotation != null) {
String url = String.format(Locale.CHINA, annotation.url(), args);
Class<?> returnType = method.getReturnType();
if (returnType.equals(NetWorkAsync.class)){
ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
Type entryType = type.getActualTypeArguments()[0];
return new NetWorkAsync<>(url, (Class<Object>) entryType);
}
}
return null;
}
}
这样, 这个下载的功能已经实现完毕, 只需要在实用的时候, 创建一个接口, 将url作为注解添加上就可以获取并传入NetWorkAsync类, 进行下载.
接口是这个样子哒
public interface TopServer {
@UrlString(url = "http://www.tngou.net/api/top/list?id=%d&page=%d&rows=%d")
NetWorkAsync<Result> getList(int id, int page, int rows);
}