Android学习之——自己搭建Http框架(2)——框架扩展
· 本文主要解说的是Json指定转化成对象返回。下载进度更新,随时取消Request请求
一、Json指定转化成对象返回
上篇文章主要讲基础的框架搭建起来了,这次须要做一些些的扩展,这里Json转化用到了google的Gson。
上篇文章,我们直接返回了String的字符串,那么假设是请求返回回来的是Json格式的,我们是否能在数据返回的时候将数据转化成须要的对象呢。答案当然是能够的。
我们能够在UI线程中创建Callback的时候将预处理的对象放入进去,还是直接代码描写叙述比較清楚:
1. 首先我们须要传递 实体类 的class 进去,该方法我们能够在抽象类 AbstractCallback 中定义:
public AbstractCallback<T> setReturnClass(Class<T> clz) {
this.mReturnClass = clz;
return this;
}
public AbstractCallback<T> setReturnType(Type type) {
this.mReturnType = type;
return this;
}
2. 使用上述方法。在ui线程代码中,当使用的的时候调用方法例如以下, 注以下代码中的 new TypeToken<Entity>(){}.getType()
为GSON获取Type的方法:
private void requestJson() {
Request request = new Request(UrlHelper.test_json_url, RequestMethod.GET);//UrlHelper.test_json_url是一个json地址
request.setCallback(new JsonCallback<Entity>() { // Entity 为 json 要转化的实体类 ,能够是 ArrayList<Entity>的形式等
@Override
public void onFilure(Exception result) {
}
@Override
public void onSuccess(Entity result) {
mTestResultLabel.setText(result.weatherinfo + "----");
}
}.setReturnType(new TypeToken<Entity>(){}.getType()));//.setReturnClass(Entity.class));
request.execute();
}
3. JsonCallback的实现方式例如以下所看到的:当中,我们须要将继承的类AbstractCallback改成AbstractCallback<T> 以及相应的接口也改成ICallback<T>。这里就不详细列出改动泛型的代码了
public abstract class JsonCallback<T> extends AbstractCallback<T> {
public static Gson gson = new Gson();
@Override
protected T bindData(String content) {
Log.i("bindData", content);
if (TextUtil.isValidate(path)) {
content = IOUtilities.readFromFile(path);
}
if (mReturnClass != null) {
return gson.fromJson(content, mReturnClass);
} else if (mReturnType != null) {
return gson.fromJson(content, mReturnType);
}
return null;
}
}
至此,转化成Json的对象已经处理完毕。可能描写叙述的不是太清楚。事实上基本的步骤就是,跟上篇文章实现StringCallback.java一样,在UI线程的request.setcallback中new一个匿名内部类将ICallback以及他的抽象类。抽象子类实现泛型。使之能够传递须要的实体类的 cass,或者 type 进去。事实上这个Json的转化并非很重要。在我阅读以及使用 android-async-http 框架的时候,直接的做法是,直接将HTTP返回的值预处理成JSON然后再onSuccess的时候进行JSON的解析,效率也并不会有太大的影响。
二、下载进度更新
处理思路:
在 AsyncTask 中doInBackground 里有一个方法publishProgress ,通过 AbstractCallback 的里的写入文件的进度,将进度实时更新到 onProgressUpdate 从而将更新进度更新到 主线程的功能,然后对进度进行对应的处理。如更新进度条等。
1. 首先加入一个监听接口:
public interface IProgressListener {
void onProgressUpdate(int curPos,int contentLength);
}
2. 我们能够通过监听 写入到文件的循环中来进行监听,在AbstractCallback.java 中handle方法的写入文件的代码例如以下:
while ((read = in.read(b)) != -1) {
// TODO update progress
fos.write(b, 0, read);
}
为了节省篇幅,详细的代码能够去上一篇文章阅读。在这里,我们能够通过当前写入的进度和总长度进行比較写入监听的接口方法中,详细代码例如以下:
a. 改动 ICallback 接口:
Object handle(HttpResponse response, IProgressListener mProgressListener);
b. 当然了同一时候要改动AbstractCallback 中的实现类
@Override
public Object handle(HttpResponse response, IProgressListener mProgressListener){.........}
c. 改动AbstractCallback 中的handle方法中写入文件的那段代码即上文代码的 //TODO update progress 段,详细代码例如以下:
byte[] b = new byte[IO_BUFFER_SIZE];
int read;
long curPos = 0;
long length = entity.getContentLength();
while ((read = in.read(b)) != -1) {
checkIfCanceled();
if (mProgressListener != null) {
curPos += read;
//将当前进度和总进度返回到详细的实现层。
//我们在 RequestTask 的 doInBackground 中去实现 IProgressLinstener 接口中的的该方法,将值传到onProgressUpdate中
mProgressListener.onProgressUpdate((int) (curPos / 1024), (int) (length / 1024));
}
fos.write(b, 0, read);
}
3. 在RequestTask.java 中的doInBackground 中我们来实现上述内容:
@Override
protected Object doInBackground(Object... params) {
try {
HttpResponse response = HttpClientUtil.excute(request);
//response 解析代码放到相应的类中,相应handle中的bindData方法
Log.i("doInBackground", response.toString());
if (request.mProgressListener != null) {
return request.callback.handle(response, new IProgressListener() {
@Override
public void onProgressUpdate(int curPos, int contentLength) {
//这里的參数类型是 AsyncTask<Object, Integer, Object>中的Integer决定的。在onProgressUpdate中能够得到这个值去更新UI主线程
publishProgress(curPos,contentLength);
}
});
}else {
return request.callback.handle(response, null);
}
} catch (Exception e) {
return e;
}
}
4. 写到这里。突然忘记最重要的一点。我们须要在主线程中设置它的监听才干真正实现监听,在Request.java中我们增加例如以下方法,使主线程能够调用:
public IProgressListener mProgressListener;
public void setProgressListener(IProgressListener iProgressListener) {
this.mProgressListener = iProgressListener;
}
5. 继续上面第3点的话题,在RequestTask.java 中我们实现 AsyncTask 的onProgressUpdate 方法, 将在doInBackground 中调用的 publishProgress(..., ...) 方法得到的值在该方法中中传给IProgressListener的onProgressUpdate。不知道我这种描写叙述是否准确。表达不是非常理想。
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (request.mProgressListener != null) {
request.mProgressListener.onProgressUpdate(values[0], values[1]);
}
}
6. 最后。我们在主线程中设置setProgressListener 并实现匿名内部类 IProgressListener ,通过重写 onProgressUpdate 方法得到当前进度值和总进度值,依据该进度值,进行实时的进度条更新等操作,主线程调用方法例如以下:
private void requestString() {
//设置保存路径
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mrfu_http.txt";
Request request = new Request(UrlHelper.test_string_url, RequestMethod.GET);
request.setCallback(new StringCallback() {
@Override
public void onSuccess(String result) {
mTestResultLabel.setText((String)result);
}
@Override
public void onFilure(Exception result) {
result.printStackTrace();
}
}.setPath(path));
request.setProgressListener(new IProgressListener() {
//在这里实现 IProgressListener 的onProgressUpdate 将子线程中得到的进度值获取到。 //这里,我们仅仅是显示到LogCat中,实际我们能够依据须要实现进度条更新等操作
@Override
public void onProgressUpdate(int curPos, int contentLength) {
System.err.println("curPost:"+curPos +",contentLength:" + contentLength);
}
});
request.execute();
}
三、 怎样随时取消 Request 请求
1. 我们须要取消 Request 请求,那么,在代码中,我们在哪些地方能够取消请求呢?我们先来分析框架的基本内容:
a. 在主线程我们运行 request.execute(); 在 Request.java 中开启了一个 RequestTask,它继承自 AsyncTask
b. doInBackground 是异步运行的。在这个子线程中 我们运行 HttpResponse response = HttpClientUtil.excute(request); 代码段 正式调用HTTP的get或者set方法。得到类型为 HttpResponse
的返回值 ,然后运行 AbstractCallback 的 handle 方法
c. 在AbstractCallback 的 public T handle(HttpResponse response, IProgressListener mProgressListener) 方法中我们处理返回回来的 HttpResponse
的内容,假设返回的code是200,则成功,那么我们依据是否设置了下载路径选择是否下载,或者是直接返回数值。
d. 依据主线程设置的 StringCallback 或者JsonCallback 或者其它解析类型,通过调用 bindData(....); 去详细的解析内容,并返回到 UI 线程。
2. 设计思路:
在主线程,我们接到了取消请求的需求,通过 调用 Requset 的 cancel() 方法,去调用 callback 中的 cancel(); 方法,将其AbstractCallback 中的取消标志设置为true,假设为true 我们就在checkIfCanceled()方法中抛出异常,结束该次请求。我们能够将 checkIfCanceled()
方法放在handle(..., ...)刚開始的时候。放在while ((read = in.read(b)) != -1){ fos.write(b, 0, read); } 写入文件的时候 以及返回数据放入不同callback中进行处理的时候。
以下我们会给出详细的实现方法,还有http请求的 get 和 post 的时候。
3. 实现代码:
a. ICallback 接口中定义例如以下方法:
void checkIfCanceled() throws AppException;
void cancel();
b. 主线程调用cancel请求:
public void testCancel(){
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "stay_training_http.txt";
final Request request = new Request("http://h.hiphotos.baidu.com/image/w%3D2048/sign=432fca00369b033b2c88fbda21f636d3/a2cc7cd98d1001e9ae04c30bba0e7bec54e797fe.jpg",RequestMethod.GET);
request.setCallback(new PathCallback() { @Override
public void onSuccess(String result) {
} @Override
public void onFilure(Exception result) {
result.printStackTrace();
}
}.setPath(path));
request.setProgressListener(new IProgressListener() {
@Override
public void onProgressUpdate(int curPos, int contentLength) {
System.err.println("curPost:"+curPos +",contentLength:" + contentLength);
if (curPos > 8) {
request.cancel();//当下载进度为8的时候我们取消了该请求
}
}
});
request.execute();
}
}
c. 在 Request 中实现 cancel() 方法,方法内调用 callback 的 cancel() 方法:
public ICallback callback;
public void cancel(){
if (callback != null) {
this.callback.cancel();
}
}
d. 在 AbstractCallback 中实现ICallback 里定义的 cancel() 的方法,假设主线程调用了cancel方法,我们就将标志设置为 true
protected boolean isCancelled;
@Override
public void cancel() {
isCancelled = true;
}
e. 实现 ICallback 中的 checkIfCanceled() 方法。假设 isCancelled 为 true 则抛出异常,就可以中断请求操作,代码中出现了 AppException 自己定义异常类 这个我们后面再讲
@Override
public void checkIfCanceled() throws AppException {
if (isCancelled) {
throw new AppException(EnumException.CancelException, "request has been cancelled");
}
}
f. 在 handle 开開始放入 checkIfCanceled() 的推断,在将下载的文件写入到文件的时候我们也做推断,还有在进行数据处理的时候也进行推断
@Override
public T handle(HttpResponse response, IProgressListener mProgressListener) throws AppException{
// file, json, xml, image, string
checkIfCanceled();//在这里我们调用检查是否取消请求的方法
int statusCode = -1;
InputStream in = null;
try {
HttpEntity entity = response.getEntity();
statusCode = response.getStatusLine().getStatusCode();
switch (statusCode) {
case HttpStatus.SC_OK:
if (TextUtil.isValidate(path)) {
//将server返回的数据写入到文件其中
FileOutputStream fos = new FileOutputStream(path);
if (entity.getContentEncoding() != null) {
String encoding = entity.getContentEncoding().getValue();
if (encoding != null && "gzip".equalsIgnoreCase(encoding)) {
in = new GZIPInputStream(entity.getContent());
} if (encoding != null && "deflate".equalsIgnoreCase(encoding)) {
in = new InflaterInputStream(entity.getContent());
}
} else {
in = entity.getContent();
}
byte[] b = new byte[IO_BUFFER_SIZE];
int read;
long curPos = 0;
long length = entity.getContentLength();
while ((read = in.read(b)) != -1) {
checkIfCanceled(); //<span style="font-family: Arial, Helvetica, sans-serif;">在这里我们调用检查是否取消请求的方法</span>
if (mProgressListener != null) {
curPos += read;
//将当前进度和总进度返回到详细的实现层,
//我们在 RequestTask 的 doInBackground 中去实现 IProgressLinstener 接口中的的该方法,将值传到onProgressUpdate中
mProgressListener.onProgressUpdate((int) (curPos / 1024), (int) (length / 1024));
}
fos.write(b, 0, read);
}
fos.flush();
fos.close();
in.close();
//写入文件之后,再从文件其中将数据读取出来,直接返回对象
return bindData(path);
} else {
// 须要返回的是对象,而不是数据流,所以须要去解析server返回的数据
// 相应StringCallback 中的return content;
//2. 调用binData
return bindData(EntityUtils.toString(entity));
}
default:
break;
}
return null;
} catch (ParseException e) {
throw new AppException(EnumException.ParseException, e.getMessage());
} catch (IOException e) {
throw new AppException(EnumException.IOException, e.getMessage());
}
}
/**
* 数据放入到不同的Callback中处理,StringCallback 等方法中实现了该方法
* @throws AppException
*/
protected T bindData(String content) throws AppException{
checkIfCanceled();//在这里我们检查是否取消请求的方法
return null;
}
g. 我们在 RequestTask 中重写 onCancelled 推断 是否有做了 task.cancel(true); 的操作,当然,我们并没有实现该操作,那是由于。我们须要无论Request 的请求结果假设。我都须要返回到主线程。假设我这个时候取消掉了 AsyncTask ,那么AsyncTask 就永远不继续运行了,也就无法回调回来了。
@Override
protected void onCancelled() {
super.onCancelled();
if (request.callback != null) {
request.callback.cancel();
}
}
h. HttpClientUtil.java 中的 post 和 get 代码中增加例如以下代码,详细内容请看凝视:
private static HttpResponse get(Request request) throws AppException {
try {
//假设在代码已经运行到这里的时候,AbstractCallback中的isCancelled被置为了 true
//这时我们就要再一次进行检查是否取消。 post方法同理,不再赘述
if (request.callback != null) {
request.callback.checkIfCanceled();
}
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(request.url);
addHeader(get, request.headers);
//返回的结果放到上一层进行处理
HttpResponse response = client.execute(get);
return response;
} catch (ClientProtocolException e) {
throw new AppException(EnumException.ClientProtocolException, e.getMessage());
} catch (IOException e) {
throw new AppException(EnumException.IOException, e.getMessage());
}
}
上文提到的 AppException 就放到下篇文章再讲吧,还有 预处理返回的对象。即将返回回来的数据解析成对象以后,对该对象进行预处理操作,如写入数据库之类的操作。也一起放入下篇文章解说,由于这块我也还不是啃的非常透。须要再磨练磨练
一不小心一点半了。今天就写到这里吧。要写好一篇博客真心难,从晚上 9 点開始一边回想视频,一边整理思路。一边再又一次实现一遍,然后一点点写上来。
ps 本来写了一大段抒情性的话的,等写完了又认为不太好意思,技术博客就单纯一点吧。
特别感谢 stay 老师在这其中的帮助。
让我在框架学习这块实打实的迈出了第一步!
Android学习之——自己搭建Http框架(2)——框架扩展的更多相关文章
- Android学习——windows下搭建Cygwin环境
在上一篇博文<Android学习——windows下搭建NDK_r9环境>中,我们详细的讲解了在windows下进行Android NDK开发环境的配置,我们也讲到了在NDk r7以后,我 ...
- Android学习——windows下搭建NDK_r9环境
1. NDK(Native Development Kit) 1.1 NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP功能开发的工具,通过这个 ...
- Android学习之——自己搭建Http框架(1)
一.前言 近期学习http框架. 眼下写的这个框架临时仅仅适用于学习之用,实际用于项目之中还须要不断的优化. 要从server或者网络获取数据.显示到U ...
- android学习——环境的搭建
1.安装JDK(java开发工具箱) 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html(根据自己需要下载) ...
- Android学习——第一个NDK程序
在前面的学习中,我们已经讲解了关于NDK编程的环境搭建流程,简单的使用我们也通过官网本身自带的例子进行说明了.可是相信大家一定还存在这么的一个疑惑:“如果我要自己利用NDK编写一个Android应用, ...
- Linux学习心得之 Linux下命令行Android开发环境的搭建
作者:枫雪庭 出处:http://www.cnblogs.com/FengXueTing-px/ 欢迎转载 Linux学习心得之 Linux下命令行Android开发环境的搭建 1. 前言2. Jav ...
- 一、Android学习第一天——环境搭建(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 一. Android学习第一天——环境搭建 Android 开发环境的搭建 ...
- Android 学习笔记之Volley开源框架解析(一)
PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍... 1.Http请求... 这里只是简单的说一下Http请求的过程.. ...
- Android开发学习总结(一)——搭建最新版本的Android开发环境
Android开发学习总结(一)——搭建最新版本的Android开发环境(转) 最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Android开发虽然有所了解,但是 ...
随机推荐
- DOMContentLoaded事件<zz>
今天查看百度空间源代码,发现多了个util.js文件,打开看看.里面里面定义了addDOMLoadEvent.这是干什么用的? 仔细查看代码,发现在Mozilla添加了DOMContentLoaded ...
- TNS-12555 / TNS-12560 / TNS-00525 Error listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPR
TNS-12555 / TNS-12560 / TNS-00525 Error listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPR ...
- Java 后台性能优化简要
业务系统性能优化的前提时观察和诊断.观察工具例如以下:前端优化工具:YSlow页面响应时间:Firebug方法对应时间:btraceGC日志分析:JVM 启动參数数据库优化:慢查询系统资源调用:监控 ...
- P1233 木棍加工
P1233 木棍加工 题目描述 一堆木头棍子共有n根,每根棍子的长度和宽度都是已知的.棍子可以被一台机器一个接一个地加工.机器处理一根棍子之前需要准备时间.准备时间是这样定义的: 第一根棍子的准备时间 ...
- weblogic状态监控脚本
echo "======================================welcome============================================ ...
- [jzoj 6080] [GDOI2019模拟2019.3.23] IOer 解题报告 (数学构造)
题目链接: https://jzoj.net/senior/#main/show/6080 题目: 题意: 给定$n,m,u,v$ 设$t_i=ui+v$ 求$\sum_{k_1+k_2+...+k_ ...
- 50个极好的bootstrap 后台框架主题下载
50个极好的bootstrap 后台框架主题下载 http://sudasuta.com/bootstrap-admin-templates.html 越来越多的设计师和前端工程师开始用bootstr ...
- 清北集训Day1T3 LYK loves jumping(期望DP)
题目描述 LYK在玩一个魔法游戏,叫做跳跃魔法. 有n个点,每个点有两个属性hi和ti,表示初始高度,和下降高度.也就是说,它初始时高度为hi,一旦LYK踩在这个点上,由于重力的影响,这个点的高度会下 ...
- 关于getElementsByTagName的遍历顺序
关于getElementsByTagName的遍历顺序是怎么样的呢? getElementsByTagName的遍历顺序是从HTML的页面从上到下遍历还是按照标签的嵌套顺序层层遍历的呢? 来做个小小的 ...
- Kattis - String Matching(kmp)
String Matching Input The input consists of several test cases. Each test case consists of two lines ...