/**
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reco.frame.tv.http;
import java.io.File;
import java.io.IOException;
import java.net.UnknownHostException;
import reco.frame.tv.core.AsyncTask;
import reco.frame.tv.http.entityhandler.EntityCallBack;
import reco.frame.tv.http.entityhandler.FileEntityHandler;
import reco.frame.tv.http.entityhandler.StringEntityHandler;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.protocol.HttpContext;
import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
public class HttpHandler<T> extends AsyncTask<Object, Object, Object> implements
EntityCallBack {
private final AbstractHttpClient client;
private final Context appContext;
private final HttpContext context;
private final StringEntityHandler mStrEntityHandler = new StringEntityHandler();
private final FileEntityHandler mFileEntityHandler = new FileEntityHandler();
private final AjaxCallBack<T> callback;
private int executionCount = 0;
private String targetUrl = null; // 下载的路径
private boolean isResume = false; // 是否断点续传
private String charset;
public HttpHandler(Context appContext, AbstractHttpClient client,
HttpContext context, AjaxCallBack<T> callback, String charset) {
this.client = client;
this.appContext = appContext;
this.context = context;
this.callback = callback;
this.charset = charset;
}
private void makeRequestWithRetries(HttpUriRequest request)
throws IOException {
// 无此句,某此文件无法正确获得长度
request.setHeader("Accept-Encoding", "identity");
if (isResume && targetUrl != null) {
File downloadFile = new File(targetUrl);
long fileLen = 0;
if (downloadFile.isFile() && downloadFile.exists()) {
fileLen = downloadFile.length();
}
if (fileLen > 0)
request.setHeader("RANGE", "bytes=" + fileLen + "-");
}
boolean retry = true;
IOException cause = null;
HttpRequestRetryHandler retryHandler = client
.getHttpRequestRetryHandler();
while (retry) {
try {
if (!isCancelled()) {
HttpResponse response = client.execute(request, context);
if (!isCancelled()) {
handleResponse(response);
}
}
return;
} catch (UnknownHostException e) {
publishProgress(UPDATE_FAILURE, e, 0,
"unknownHostException--can't resolve host");
return;
} catch (IOException e) {
cause = e;
retry = retryHandler.retryRequest(cause, ++executionCount,
context);
} catch (NullPointerException e) {
// HttpClient 4.0.x 之前的一个bug
// http://code.google.com/p/android/issues/detail?id=5255
cause = new IOException("NPE in HttpClient" + e.getMessage());
retry = retryHandler.retryRequest(cause, ++executionCount,
context);
} catch (Exception e) {
cause = new IOException("Exception" + e.getMessage());
retry = retryHandler.retryRequest(cause, ++executionCount,
context);
}
}
if (cause != null)
throw cause;
else
throw new IOException("error");
}
@Override
protected Object doInBackground(Object... params) {
if (params != null && params.length == 3) {
targetUrl = String.valueOf(params[1]);
isResume = (Boolean) params[2];
}
try {
publishProgress(UPDATE_START);
makeRequestWithRetries((HttpUriRequest) params[0]);
} catch (IOException e) {
publishProgress(UPDATE_FAILURE, e, 0, e.getMessage());
}
return null;
}
private final static int UPDATE_START = 1;
private final static int UPDATE_LOADING = 2;
private final static int UPDATE_FAILURE = 3;
private final static int UPDATE_SUCCESS = 4;
@SuppressWarnings("unchecked")
@Override
protected void onProgressUpdate(Object... values) {
int update = Integer.valueOf(String.valueOf(values[0]));
switch (update) {
case UPDATE_START:
if (callback != null)
callback.onStart();
break;
case UPDATE_LOADING:
if (callback != null)
callback.onLoading(Long.valueOf(String.valueOf(values[1])),
Long.valueOf(String.valueOf(values[2])));
break;
case UPDATE_FAILURE:
if (callback != null)
callback.onFailure((Throwable) values[1], (Integer) values[2],
(String) values[3]);
break;
case UPDATE_SUCCESS:
if (callback != null)
callback.onSuccess((T) values[1]);
break;
default:
break;
}
super.onProgressUpdate(values);
}
public boolean isStop() {
return mFileEntityHandler.isStop();
}
/**
* @param stop
* 停止下载任务
*/
public void stop() {
mFileEntityHandler.setStop(true);
}
private void handleResponse(HttpResponse response) {
StatusLine status = response.getStatusLine();
if (status.getStatusCode() >= 300) {
String errorMsg = "response status error code:"
+ status.getStatusCode();
if (status.getStatusCode() == 416 && isResume) {
errorMsg += " \n maybe you have download complete.";
}
publishProgress(
UPDATE_FAILURE,
new HttpResponseException(status.getStatusCode(), status
.getReasonPhrase()), status.getStatusCode(),
errorMsg);
} else {
try {
HttpEntity entity = response.getEntity();
Object responseBody = null;
if (entity != null) {
time = SystemClock.uptimeMillis();
if (targetUrl != null) {
responseBody = mFileEntityHandler.handleEntity(
appContext, entity, this, targetUrl, isResume);
} else {
responseBody = mStrEntityHandler.handleEntity(entity,
this, charset);
}
}
publishProgress(UPDATE_SUCCESS, responseBody);
} catch (IOException e) {
publishProgress(UPDATE_FAILURE, e, 0, e.getMessage());
}
}
}
private long time;
@Override
public void callBack(long count, long current, boolean mustNoticeUI) {
if (callback != null && callback.isProgress()) {
if (mustNoticeUI) {
publishProgress(UPDATE_LOADING, count, current);
} else {
long thisTime = SystemClock.uptimeMillis();
if (thisTime - time >= callback.getRate()) {
time = thisTime;
publishProgress(UPDATE_LOADING, count, current);
}
}
}
}
}