· 本文主要讲解的是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;
- }
- 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();
- }
- 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;
- }
- }
二、下载进度更新
处理思路:
在 AsyncTask 中doInBackground 里有一个方法publishProgress ,通过 AbstractCallback 的里的写入文件的进度,将进度实时更新到 onProgressUpdate 从而将更新进度更新到 主线程的功能,然后对进度进行相应的处理。如更新进度条等。
1. 首先添加一个监听接口:
- public interface IProgressListener {
- void onProgressUpdate(int curPos,int contentLength);
- }
- while ((read = in.read(b)) != -1) {
- // TODO update progress
- fos.write(b, 0, read);
- }
a. 修改 ICallback 接口:
- Object handle(HttpResponse response, IProgressListener mProgressListener);
- @Override
- public Object handle(HttpResponse response, IProgressListener mProgressListener){.........}
- 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;
- }
- }
- public IProgressListener mProgressListener;
- public void setProgressListener(IProgressListener iProgressListener) {
- this.mProgressListener = iProgressListener;
- }
- @Override
- protected void onProgressUpdate(Integer... values) {
- super.onProgressUpdate(values);
- if (request.mProgressListener != null) {
- request.mProgressListener.onProgressUpdate(values[0], values[1]);
- }
- }
- 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");
- }
- }
- @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)) {
- //将服务器返回的数据写入到文件当中
- 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 {
- // 需要返回的是对象,而不是数据流,所以需要去解析服务器返回的数据
- // 对应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();
- }
- }
- 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 就放到下篇文章再讲吧,还有 预处理返回的对象,即将返回回来的数据解析成对象以后,对该对象进行预处理操作,如写入数据库之类的操作,也一起放入下篇文章讲解,因为这块我也还不是啃的很透,需要再磨练磨练,再看看 stay 老师的视频,
一不小心一点半了,今天就写到这里吧,要写好一篇博客真心难,从晚上 9 点开始一边回顾视频,一边整理思路,一边再重新实现一遍,然后一点点写上来。
ps 本来写了一大段抒情性的话的,等写完了又觉得不太好意思,技术博客就单纯一点吧。
特别感谢 stay 老师在这当中的帮助。让我在框架学习这块实打实的迈出了第一步! 他的个人网站:Stay技术分享技术生活 ,有兴趣的可以去看看他的视频,讲解的相当到位。——纯技术推荐...