package com.phodev.http.tools;
/**
*
* Copyright 2009 Jive Software.
*
* 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.
*/
import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import com.phodev.http.tools.receivers.ParasiticRequestReceiver;
/**
* 网络请求数据封装
*
* @author sky
*
*/
public class ConnectionHelper {
public static final String TAG = "ConnectionHelper";
public static final boolean DEBUG = true;
public static final int CONNECTION_TIMEOUT = 20000;// ms
public static final int CON_TIME_OUT_MS = CONNECTION_TIMEOUT;
public static final int SO_TIME_OUT_MS = CONNECTION_TIMEOUT;
public static final int MAX_CONNECTIONS_PER_HOST = 20;// SDK默认20
public static final int MAX_TOTAL_CONNECTIONS = 10;// 同时允许的连接个数//SDK默认2
public static final int CONNETIONS_MAX_IDLE_TIME = 60 * 1000;//
// 线程池
public static final int MAX_CORE_POOL_SIZE = 2;// 一直保留的线程数
public static final int KEEP_ALIVE_TIME = 60;// s//允许空闲线程时间
final static ThreadPoolExecutor executor = new ThreadPoolExecutor(
MAX_CORE_POOL_SIZE, Integer.MAX_VALUE, KEEP_ALIVE_TIME,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
public static final String DEFAULT_CHARSET_STR = "UTF-8";
public static final Charset DEFAULT_CHARSET = Charset
.forName(DEFAULT_CHARSET_STR);
//
private HttpClient httpClient;
private ConnectionHelper() {
HttpParams httpParams = new BasicHttpParams();
ConnManagerParams.setMaxTotalConnections(httpParams,
MAX_TOTAL_CONNECTIONS);
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(
MAX_CONNECTIONS_PER_HOST);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, connPerRoute);
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setUseExpectContinue(httpParams, false);
SchemeRegistry reg = new SchemeRegistry();
reg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(),
80));
reg.register(new Scheme("https", SSLSocketFactory.getSocketFactory(),
443));
ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(
httpParams, reg);
HttpConnectionParams.setConnectionTimeout(httpParams, CON_TIME_OUT_MS);
HttpConnectionParams.setSoTimeout(httpParams, SO_TIME_OUT_MS);
HttpClientParams.setCookiePolicy(httpParams,
CookiePolicy.BROWSER_COMPATIBILITY);
httpClient = new DefaultHttpClient(connectionManager, httpParams);
}
private static ConnectionHelper instance;
public static synchronized ConnectionHelper obtainInstance() {
if (instance == null) {
instance = new ConnectionHelper();
}
return instance;
}
public enum RequestMethod {
GET, POST,
/**
* multipart form data
*/
POST_WITH_FILE;
}
/**
* 使用指定的Method发送请求
*
* @param url
* @param requestId
* 在onFinish的会返回requestId,标示是哪个一个请求
* @param rr
*/
public long httpGet(String url, int requestId, RequestReceiver rr) {
RequestEntity entity = RequestEntity.obtain();
entity.setUrl(url).setReceiver(rr).setMethod(RequestMethod.GET)
.setRequestId(requestId);
return execute(entity);
}
public long httpPost(String url, int requestId,
List<NameValuePair> postValues, RequestReceiver rr) {
return httpPost(url, requestId, postValues, DEFAULT_CHARSET_STR, rr);
}
/**
* Post方式发送数据
*
* @param url
* @param requestId
* @param postValues
* @param charset
* @param rr
*/
public long httpPost(String url, int requestId,
List<NameValuePair> postValues, String charset, RequestReceiver rr) {
RequestEntity entity = RequestEntity.obtain();
entity.setUrl(url).setReceiver(rr).setPostEntitiy(postValues, charset)
.setMethod(RequestMethod.POST).setRequestId(requestId);
return execute(entity);
}
public long httpPost(String url, int requestId, String queryString,
RequestReceiver rr) {
return httpPost(url, requestId, queryString, DEFAULT_CHARSET_STR, rr);
}
/**
* Post方式发送数据
*
* @param url
* @param requestId
* @param queryString
* @param charset
* @param rr
*/
public long httpPost(String url, int requestId, String queryString,
String charset, RequestReceiver rr) {
RequestEntity entity = RequestEntity.obtain();
entity.setUrl(url);
entity.setReceiver(rr);
entity.setPostEntitiy(queryString, charset);
entity.setMethod(RequestMethod.POST);
entity.setRequestId(requestId);
return execute(entity);
}
public long httpPost(String url, int requestId,
List<NameValuePair> postValues, Map<String, File> files,
RequestReceiver rr) {
return httpPost(url, requestId, postValues, DEFAULT_CHARSET_STR, files,
rr);
}
/**
* POST方式上传文件
*
* @param url
* @param requestId
* @param postValues
* @param files
* @param rr
*/
public long httpPost(String url, int requestId,
List<NameValuePair> postValues, String charset,
Map<String, File> files, RequestReceiver rr) {
RequestEntity entity = RequestEntity.obtain();
entity.setUrl(url);
entity.setReceiver(rr);
entity.setPostEntitiy(postValues, charset, files);
entity.setMethod(RequestMethod.POST_WITH_FILE);
entity.setRequestId(requestId);
return execute(entity);
}
private Map<Long, RequestEntity> mRequestRecords = new HashMap<Long, RequestEntity>();
public long execute(RequestEntity entity) {
ConnectionTask task = obtainConnectionTask(entity);
synchronized (mRequestRecords) {
mRequestRecords.put(entity.getRequestHandler(), entity);
}
entity.setRequestTaskFuture(executor.submit(task));
// executor.execute(obtainConnectionTask(entity));
return entity.getRequestHandler();
}
/**
* 判断是否在运行
*
* @param reqFingerprint
* @return
*/
public boolean isReqeustRunning(long reqFingerprint) {
synchronized (mRequestRecords) {
RequestEntity re = mRequestRecords.get(reqFingerprint);
if (re == null) {
return false;
}
return !re.isCanceled();
}
}
public class RequestEntityNotFoundException extends RuntimeException {
private static final long serialVersionUID = 3742290111879087686L;
private long reqFingerprint;
public RequestEntityNotFoundException(long reqFingerprint) {
this.reqFingerprint = reqFingerprint;
}
@Override
public String toString() {
return "RequestEntityNotFoundException reqFingerprint="
+ reqFingerprint + " reqeust entity not found";
}
}
/**
* 取消请求
*
* @param reqFingerprint
* @return
*/
public boolean cancleRequest(long reqFingerprint)
throws RequestEntityNotFoundException {
RequestEntity request = null;
synchronized (mRequestRecords) {
request = mRequestRecords.get(reqFingerprint);
if (request == null) {
throw new RequestEntityNotFoundException(reqFingerprint);
}
//
synchronized (request) {
if (request.isCanceled() && request.isCancelStateSend()) {
return true;
}
Future<?> future = request.getRequestTaskFuture();
if (future == null) {
return true;
}
request.setCanceled(true);
//
try {
future.cancel(true);
} catch (Exception e) {
e.printStackTrace();
return isReqeustRunning(reqFingerprint);
}
tryNotifyCanceled(request);
}
}
return true;
}
/**
* 连接任务处理
*/
class ConnectionTask implements Runnable {
RequestEntity rEntity;
boolean isInterrupted = false;
public ConnectionTask(RequestEntity entity) {
rEntity = entity;
}
public void setRequestEntity(RequestEntity entity) {
rEntity = entity;
}
@Override
public void run() {
HttpRequestBase httpRequest = null;
int customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
int statusCode = -1;
try {
if (rEntity.getMethod() == RequestMethod.GET) {
httpRequest = new HttpGet(rEntity.getUrl());
} else {
// POST/POST_WITH_FILE
HttpPost httpPost = new HttpPost(rEntity.getUrl());
// 设置请求的数据
httpPost.setEntity(rEntity.getPostEntitiy());
httpRequest = httpPost;
}
HttpConnectionParams.setSoTimeout(httpRequest.getParams(),
CONNECTION_TIMEOUT);
//
HttpResponse response = httpClient.execute(httpRequest);
statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
rEntity.setRawResponse(EntityUtils.toString(
response.getEntity(), rEntity.getCurrentCharset()));
customResultCode = RequestReceiver.RESULT_STATE_OK;
} else {
customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
}
} catch (ConnectTimeoutException e) {
e.printStackTrace();
customResultCode = RequestReceiver.RESULT_STATE_TIME_OUT;
} catch (ClientProtocolException e) {
e.printStackTrace();
customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
} catch (Exception e) {
e.printStackTrace();
customResultCode = RequestReceiver.RESULT_STATE_NETWORK_ERROR;
} finally {
try {
httpRequest.abort();
} catch (Exception e) {
// ignore
}
}
// Debug
if (DEBUG) {
reportRequestEntity(rEntity, statusCode);
}
RequestReceiver rr = rEntity.getRequestReceiver();
if (rr instanceof ParasiticRequestReceiver) {
((ParasiticRequestReceiver) rr).parasiticHandler(
rEntity.getResultCode(), rEntity.getRequestId(),
rEntity.getTag(), rEntity.getRawResponse());
}
synchronized (this) {
if (!isInterrupted) {
synchronized (rEntity) {
if (rEntity.isCanceled()) {
tryNotifyCanceled(rEntity);
} else {
// 发送到主线程
rEntity.setResultCode(customResultCode);
Message msg = httpHandler.obtainMessage();
msg.obj = rEntity;
httpHandler.sendMessage(msg);
}
}
}
}
recycle();
}
public void recycle() {
rEntity = null;
isInterrupted = false;
if (connectionTaskList.size() < 6) {
connectionTaskList.add(this);
}
}
}
/**
* 检查是否是cancle的状态,如果是则分发状态
*
* @param re
*/
private void tryNotifyCanceled(RequestEntity re) {
if (re.isCanceled() && !re.isCancelStateSend()) {
re.setCancelStateSend(true);
// 发送到主线程
Message msg = httpHandler.obtainMessage();
msg.obj = re;
httpHandler.sendMessage(msg);
}
}
private static Handler httpHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 分发结果给UI Thread
if (msg.obj instanceof RequestEntity) {
RequestEntity e = (RequestEntity) msg.obj;
RequestReceiver rr = e.getRequestReceiver();
if (e.isCanceled()) {
rr.onRequestCanceled(e.getRequestId(), e.getTag());
} else {
rr.onResult(e.getResultCode(), e.getRequestId(),
e.getTag(), e.getRawResponse());
}
//
e.recycle();
}
}
};
private final static Vector<ConnectionTask> connectionTaskList = new Vector<ConnectionTask>();
/**
* 获取实例
*
* @param entity
* @return
*/
private ConnectionTask obtainConnectionTask(RequestEntity entity) {
if (connectionTaskList.size() <= 0) {
return new ConnectionTask(entity);
} else {
ConnectionTask task = connectionTaskList.remove(0);
task.setRequestEntity(entity);
return task;
}
}
/**
* 断开HttpClient的连接
*/
public void shutdownConnection() {
try {
httpClient.getConnectionManager().shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
public void tryRelaseConnetions() {
httpClient.getConnectionManager().closeIdleConnections(
CONNETIONS_MAX_IDLE_TIME, TimeUnit.NANOSECONDS);
}
@Override
protected void finalize() throws Throwable {
shutdownConnection();
}
// public interface HttpTask extends Runnable {
// public void setTask(RequestEntity entity);
// }
// just debug
public void reportRequestEntity(RequestEntity re, int netStateCode) {
if (re == null) {
Log.w(TAG, "------>Connection info RequestEntity is:" + re);
Log.d(TAG,
"------>Connection Thread Pool curr size:"
+ executor.getPoolSize());
} else {
Log.d(TAG, "------>Connection info start");
Log.d(TAG,
"------>Connection Thread Pool curr size:"
+ executor.getPoolSize());
Log.d(TAG, "------>Url:" + re.getUrl());
if (netStateCode < 0) {
Log.w(TAG, "------>Connection thorws Exception");
} else {
Log.d(TAG, "------>Connection StatusCode:" + netStateCode
+ " custom ResultCode:" + re.getResultCode());
}
// if (re.getPostEntitiy() == null) {
// Log.d(TAG, "------>PostValue if Exist:null");
// } else {
// String postContent = null;
// HttpEntity entity = re.getPostEntitiy();
// Header header = entity.getContentEncoding();
// if (header != null && header.getElements() != null) {
// postContent = header.getElements().toString();
// }
// Log.d(TAG, "------>PostValue if Exist:" + postContent);
// }
Log.v(TAG, "------>Raw Result:" + re.getRawResponse());
Log.d(TAG, "------>Connection info end");
}
}
}