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 老师在这其中的帮助。

让我在框架学习这块实打实的迈出了第一步!



欢迎转载。转载注明出处。谢谢
Mr.傅

原文地址:https://www.cnblogs.com/jzssuanfa/p/7101898.html