第一次提交
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
package com.fengliyan.http;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.caivideo.http.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
2
http/src/main/AndroidManifest.xml
Normal file
2
http/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.fengliyan.http" />
|
||||
101
http/src/main/java/com/fengliyan/http/httprequest/Config.java
Normal file
101
http/src/main/java/com/fengliyan/http/httprequest/Config.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.fengliyan.http.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
public static final String ENV_PARAMS = "EnvParams";
|
||||
public static final String USER_AGENT = "User-Agent";
|
||||
public static final String CHANNEL = "channel";
|
||||
|
||||
private static String sUserAgent;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public Config(Context context) {
|
||||
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("context cannot be null");
|
||||
}
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通请求的超时事件。如果APP需要修改时,在APP的资源文件中重写数组{@link R.array#xhr_normal_timeoutMs}
|
||||
*
|
||||
* @return 超时时间数组,单位ms,包含三个参数(顺序为connect,read,write)的超时时间
|
||||
*/
|
||||
public int[] normalTimeoutMs() {
|
||||
return mContext.getResources().getIntArray(R.array.xhr_normal_timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载时的请求超时时间。如果APP需要修改时,在APP的资源文件中重写数组{@link R.array#xhr_download_timeoutMs}
|
||||
*
|
||||
* @return 超时时间数组,单位ms,包含三个参数(顺序为connect,read,write)的超时时间
|
||||
*/
|
||||
public int[] downloadTimeoutMs() {
|
||||
return mContext.getResources().getIntArray(R.array.xhr_download_timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传时的请求超时时间。如果APP需要修改时,在APP的资源文件中重写数组{@link R.array#xhr_upload_timeoutMs}
|
||||
*
|
||||
* @return 超时时间数组,单位ms,包含三个参数(顺序为connect,read,write)的超时时间
|
||||
*/
|
||||
public int[] uploadTimeoutMs() {
|
||||
return mContext.getResources().getIntArray(R.array.xhr_upload_timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用SSL校验
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean enableSSLVerification() {
|
||||
String sslVerification = mContext.getString(R.string.xhr_sslVerification);
|
||||
return !"false".equalsIgnoreCase(sslVerification); //除非明确设置false,否则都需要验证SSL有效
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用缓存支持
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean enableCache() {
|
||||
String enableCache = mContext.getString(R.string.xhr_enableCache);
|
||||
return "true".equalsIgnoreCase(enableCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存大小设置,单位为Byte
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int cacheMaxSize() {
|
||||
String cacheMaxSize = mContext.getString(R.string.xhr_cacheMaxSize);
|
||||
return Integer.parseInt(cacheMaxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回APP缓存目录,可能返回null
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public File cacheDir() {
|
||||
File rootCacheDir = mContext.getCacheDir();
|
||||
if (rootCacheDir != null) {
|
||||
return new File(rootCacheDir, "xhrcache");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/23.
|
||||
*/
|
||||
public abstract class HttpCallback<T> extends TType<T> implements ProgressListener {
|
||||
|
||||
/**
|
||||
* 请求失败回调的函数,请求失败包括服务器未返回,或服务器返回的HTTP STATUS CODE标示失败
|
||||
*
|
||||
* @param httpStatusCode
|
||||
* @param message
|
||||
* @param throwable 如果httpStatusCode为-1的话,由该参数标识发生的异常
|
||||
*/
|
||||
public abstract void onFailure(int httpStatusCode, String message, Throwable throwable);
|
||||
|
||||
/**
|
||||
* 请求成功回调的函数,请求成功仅表示服务器正常处理请求了,不代表业务处理成功
|
||||
*
|
||||
* @param httpStatusCode
|
||||
* @param responseObject
|
||||
*/
|
||||
public abstract void onSuccess(int httpStatusCode, T responseObject);
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesCount, long contentLength, boolean done) {
|
||||
}
|
||||
|
||||
//由于泛型的擦试原因,无法在子类中获取T.class,因此需要通过反射机制获取
|
||||
//public Class<T> getTClass() {
|
||||
// return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
//}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/23.
|
||||
*/
|
||||
public class HttpException extends RuntimeException {
|
||||
|
||||
private int mHttpStatusCode;
|
||||
|
||||
public HttpException(String message) {
|
||||
super(message);
|
||||
mHttpStatusCode = -1;
|
||||
}
|
||||
|
||||
public HttpException(int httpStatusCode, String message) {
|
||||
super(message);
|
||||
mHttpStatusCode = httpStatusCode;
|
||||
}
|
||||
|
||||
public HttpException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
mHttpStatusCode = -1;
|
||||
}
|
||||
|
||||
public HttpException(int httpStatusCode, String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
mHttpStatusCode = httpStatusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回HTTP STATUS CODE,-1表示非WEB服务器处理产生的错误
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getHttpStatusCode() {
|
||||
return mHttpStatusCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/23.
|
||||
*/
|
||||
public enum HttpMethod {
|
||||
|
||||
GET,
|
||||
POST
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/23.
|
||||
*/
|
||||
public interface HttpRequest {
|
||||
|
||||
/**
|
||||
* 发起HTTP GET同步请求
|
||||
*
|
||||
* @param tType
|
||||
* @param url
|
||||
* @param parameters 请求的参数,参数将拼接到url中,允许为null
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
* @deprecated 由于使用MAP,传递数组有问题,LinkMap key不能相同。使用{@link #get(TType, String, RequestParameter, String)} 替代
|
||||
*/
|
||||
<T> T get(TType<T> tType, String url, Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 发起HTTP GET同步请求
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
String get(String url, Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 同步请求
|
||||
*
|
||||
* @param ttType
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
* @deprecated 由于使用MAP,传递数组有问题,LinkMap key不能相同。使用{@link #post(TType, String, RequestParameter, String)} 替代
|
||||
*/
|
||||
<T> T post(TType<T> ttType, String url, Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 同步请求
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
String post(String url, Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 发起HTTP异步请求
|
||||
*
|
||||
* @param url
|
||||
* @param parameters 请求的参数,如果是GET请求,则会将参数拼接到url中,允许为null
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @deprecated 由于使用MAP,传递数组有问题, key不能相同。使用{@link #get(String, RequestParameter, HttpCallback)} 替代
|
||||
*/
|
||||
<T> void get(String url, Map<String, String> parameters, HttpCallback<T> httpCallback);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 异步请求
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @deprecated 由于使用MAP,传递数组有问题, key不能相同。使用{@link #post(String, RequestParameter, HttpCallback)} 替代
|
||||
*/
|
||||
<T> void post(String url, Map<String, String> parameters, HttpCallback<T> httpCallback);
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param ttype
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param files
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
*/
|
||||
<T> T upload(TType<T> ttype, String url, Map<String, String> parameters, UploadFile[] files);
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param files
|
||||
* @return
|
||||
*/
|
||||
String upload(String url, Map<String, String> parameters, UploadFile[] files);
|
||||
|
||||
|
||||
/**
|
||||
* 异步文件上传
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param files
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
*/
|
||||
<T> void upload(String url, Map<String, String> parameters, UploadFile[] files,
|
||||
HttpCallback<T> httpCallback);
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param downloadUrl 下载链接
|
||||
* @param fileAbsolutePath 文件保存的绝对路径
|
||||
* @param overwrite 是否覆盖已有文件
|
||||
* @return 已下载文件的全路径,如果文件已存在,且overwrite为false,则返回null
|
||||
*/
|
||||
String download(String downloadUrl, String fileAbsolutePath, boolean overwrite);
|
||||
|
||||
/**
|
||||
* 异步文件下载
|
||||
*
|
||||
* @param downloadUrl
|
||||
* @param fileAbsolutePath
|
||||
* @param overwrite
|
||||
* @param httpCallback
|
||||
*/
|
||||
void download(String downloadUrl, String fileAbsolutePath, boolean overwrite,
|
||||
HttpCallback<String> httpCallback);
|
||||
|
||||
/**
|
||||
* 获取指定链接的输入数据流
|
||||
*
|
||||
* @param url
|
||||
* @param parameters 请求的参数,会将参数拼接到url中,允许为null
|
||||
* @return 输入数据流,注意及时关闭资源
|
||||
*/
|
||||
InputStream getStream(String url, Map<String, String> parameters);
|
||||
|
||||
/**
|
||||
* 异步获取指定链接的输入数据流
|
||||
*
|
||||
* @param url
|
||||
* @param parameters 请求的参数,会将参数拼接到url中,允许为null
|
||||
* @param streamCallback
|
||||
*/
|
||||
void getStream(String url, Map<String, String> parameters, StreamCallback streamCallback);
|
||||
|
||||
/**
|
||||
* 设置是否进行SSL校验,生产环境一般需要校验,默认值取决于配置
|
||||
*
|
||||
* @param enabled
|
||||
*/
|
||||
void setSslVerification(boolean enabled);
|
||||
|
||||
|
||||
/**
|
||||
* 发起HTTP异步请求
|
||||
* 由于原来的使用MAP,传递数组有问题,LinkMap key不能相同。故增加新的接口,保证参数的顺序
|
||||
*
|
||||
* @param url
|
||||
* @param parameters 请求的参数,如果是GET请求,则会将参数拼接到url中,允许为null
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
*/
|
||||
<T> void get(String url, RequestParameter parameters, HttpCallback<T> httpCallback);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 异步请求
|
||||
*
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
*/
|
||||
<T> void post(String url, RequestParameter parameters, HttpCallback<T> httpCallback);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 同步请求
|
||||
*
|
||||
* @param tType
|
||||
* @param url
|
||||
* @param parameters
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
*/
|
||||
<T> T post(TType<T> tType, String url, RequestParameter parameters, String ext);
|
||||
|
||||
/**
|
||||
* 发起HTTP GET同步请求
|
||||
*
|
||||
* @param tType
|
||||
* @param url
|
||||
* @param parameters 请求的参数,参数将拼接到url中,允许为null
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
*/
|
||||
<T> T get(TType<T> tType, String url, RequestParameter parameters, String ext);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 同步请求
|
||||
*
|
||||
* @param tType
|
||||
* @param url
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
* @return
|
||||
*/
|
||||
<T> T post(TType<T> tType, String url, byte[] data);
|
||||
|
||||
/**
|
||||
* 发起HTTP POST 异步请求
|
||||
*
|
||||
* @param url
|
||||
* @param data
|
||||
* @param httpCallback
|
||||
* @param <T> 支持返回实体对像,当指定为String.class时,返回的是字符串
|
||||
*/
|
||||
<T> void post(String url, byte[] data, HttpCallback<T> httpCallback);
|
||||
|
||||
String upload(String url, RequestParameter parameters, UploadFile[] files);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
|
||||
import com.fengliyan.base.base.ContextHolder;
|
||||
import com.fengliyan.http.httprequest.OkHttp.CaiWebSocket;
|
||||
import com.fengliyan.http.httprequest.OkHttp.OkHttpRequestImpl;
|
||||
import com.fengliyan.http.httprequest.cookie.XhrWebkitCookieManager;
|
||||
|
||||
import java.net.CookieHandler;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/23.
|
||||
*/
|
||||
public class HttpRequestFactory {
|
||||
|
||||
private String TAG = "HttpRequestFactory";
|
||||
|
||||
private static Config sConfig;
|
||||
|
||||
static {
|
||||
init(ContextHolder.getApplicationContext());
|
||||
}
|
||||
|
||||
private static void init(Context context) {
|
||||
|
||||
//初始化配置项
|
||||
sConfig = new Config(context);
|
||||
|
||||
//初始化COOKIE管理器
|
||||
/*
|
||||
PersistentCookieStore cookieStore = new PersistentCookieStore(context);
|
||||
CookieManager cookieManager = new CookieManager(cookieStore,
|
||||
CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||
*/
|
||||
XhrWebkitCookieManager cookeManager = new XhrWebkitCookieManager(context);
|
||||
CookieHandler.setDefault(cookeManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个HttpRequest实例
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static HttpRequest create() {
|
||||
|
||||
return new OkHttpRequestImpl(sConfig);
|
||||
}
|
||||
|
||||
public static void createWebSocket(CaiWebSocket.WebSocketListener listener, String url, Activity activity){
|
||||
OkHttpRequestImpl impl = new OkHttpRequestImpl(sConfig);
|
||||
impl.initWebSocket(listener, url, activity);
|
||||
}
|
||||
|
||||
public static void sendWebSocketMessage(String message){
|
||||
OkHttpRequestImpl impl = new OkHttpRequestImpl(sConfig);
|
||||
impl.sendMessage(message);
|
||||
}
|
||||
|
||||
public static void closeWebSocket(){
|
||||
OkHttpRequestImpl impl = new OkHttpRequestImpl(sConfig);
|
||||
impl.closeConnect();
|
||||
}
|
||||
|
||||
public static void reconnectWebSocket(String url){
|
||||
OkHttpRequestImpl impl = new OkHttpRequestImpl(sConfig);
|
||||
impl.reconnectWebSocket(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cookie处理
|
||||
* @return
|
||||
*/
|
||||
public static CookieHandler getCookieHandler() {
|
||||
return CookieHandler.getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除cookie
|
||||
*/
|
||||
public static void clearCookie() {
|
||||
android.webkit.CookieManager.getInstance().removeAllCookie();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import okio.ByteString;
|
||||
|
||||
public class CaiWebSocket extends WebSocketListener {
|
||||
private static CaiWebSocket mInstance;
|
||||
private WebSocket mWebSocket;
|
||||
private WebSocketListener mWebSocketListener;
|
||||
private Activity mActivity;
|
||||
|
||||
public static CaiWebSocket getInstance(){
|
||||
if(null == mInstance){
|
||||
synchronized (CaiWebSocket.class) {
|
||||
if (null == mInstance) {
|
||||
mInstance = new CaiWebSocket();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
private CaiWebSocket(){
|
||||
|
||||
}
|
||||
|
||||
public interface WebSocketListener{
|
||||
void onOpen();
|
||||
void onMessage(String text);
|
||||
void onClosing();
|
||||
void onClosed();
|
||||
void onFailure(String reason);
|
||||
}
|
||||
|
||||
public void setWebSocketListener(WebSocketListener listener, Activity activity){
|
||||
mWebSocketListener = listener;
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
mWebSocket = webSocket;
|
||||
|
||||
if(null != mWebSocketListener){
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWebSocketListener.onOpen();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, final String text) {
|
||||
if(null == mWebSocket){
|
||||
return;
|
||||
}
|
||||
|
||||
if(null != mWebSocketListener){
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWebSocketListener.onMessage(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, ByteString bytes) {
|
||||
if(null == mWebSocket){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosing(WebSocket webSocket, int code, String reason) {
|
||||
if(null == mWebSocket){
|
||||
return;
|
||||
}
|
||||
|
||||
// mWebSocket.close(1000, null);
|
||||
if(null != mWebSocketListener){
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWebSocketListener.onClosing();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||
mWebSocket = null;
|
||||
if(null != mWebSocketListener){
|
||||
mWebSocketListener.onClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, final Throwable t, final Response response) {
|
||||
if(null == mWebSocket){
|
||||
return;
|
||||
}
|
||||
|
||||
if(null != mWebSocketListener){
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWebSocketListener.onFailure(t.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String message){
|
||||
if(null != mWebSocket){
|
||||
mWebSocket.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void closeConnect(){
|
||||
if(null != mWebSocket){
|
||||
mWebSocket.close(1000, null);
|
||||
// mWebSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Square, Inc.
|
||||
*
|
||||
* 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.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Connection;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSink;
|
||||
import okio.BufferedSource;
|
||||
import okio.ForwardingSink;
|
||||
import okio.ForwardingSource;
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
import okio.Source;
|
||||
|
||||
/**
|
||||
* An OkHttp interceptor which logs request and response information. Can be applied as an
|
||||
* {@linkplain OkHttpClient#interceptors() application interceptor} or as a {@linkplain
|
||||
* OkHttpClient#networkInterceptors() network interceptor}. <p> The format of the logs created by
|
||||
* this class should not be considered stable and may change slightly between releases. If you need
|
||||
* a stable logging format, use your own interceptor.
|
||||
*/
|
||||
public final class HttpLoggingInterceptor implements Interceptor {
|
||||
private static final String TAG = "HttpLoggingInterceptor";
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
|
||||
public enum Level {
|
||||
/**
|
||||
* No logs.
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* Logs request and response lines.
|
||||
* <p/>
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1 (3-byte body)
|
||||
* <p/>
|
||||
* <-- 200 OK (22ms, 6-byte body)
|
||||
* }</pre>
|
||||
*/
|
||||
BASIC,
|
||||
/**
|
||||
* Logs request and response lines and their respective headers.
|
||||
* <p/>
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1
|
||||
* Host: example.com
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 3
|
||||
* --> END POST
|
||||
* <p/>
|
||||
* <-- 200 OK (22ms)
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 6
|
||||
* <-- END HTTP
|
||||
* }</pre>
|
||||
*/
|
||||
HEADERS,
|
||||
/**
|
||||
* Logs request and response lines and their respective headers and bodies (if present).
|
||||
* <p/>
|
||||
* <p>Example:
|
||||
* <pre>{@code
|
||||
* --> POST /greeting http/1.1
|
||||
* Host: example.com
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 3
|
||||
* <p/>
|
||||
* Hi?
|
||||
* --> END GET
|
||||
* <p/>
|
||||
* <-- 200 OK (22ms)
|
||||
* Content-Type: plain/text
|
||||
* Content-Length: 6
|
||||
* <p/>
|
||||
* Hello!
|
||||
* <-- END HTTP
|
||||
* }</pre>
|
||||
*/
|
||||
BODY
|
||||
}
|
||||
|
||||
public interface Logger {
|
||||
void log(String message);
|
||||
|
||||
/**
|
||||
* A {@link Logger} defaults output appropriate for the current platform.
|
||||
*/
|
||||
Logger DEFAULT = new Logger() {
|
||||
@Override
|
||||
public void log(String message) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public HttpLoggingInterceptor() {
|
||||
this(Logger.DEFAULT);
|
||||
}
|
||||
|
||||
public HttpLoggingInterceptor(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private final Logger logger;
|
||||
|
||||
private volatile Level level = Level.NONE;
|
||||
|
||||
/**
|
||||
* Change the level at which this interceptor logs.
|
||||
*/
|
||||
public HttpLoggingInterceptor setLevel(Level level) {
|
||||
if (level == null) {
|
||||
throw new NullPointerException("level == null. Use Level.NONE instead.");
|
||||
}
|
||||
this.level = level;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Level getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Level level = this.level;
|
||||
|
||||
Request request = chain.request();
|
||||
if (level == Level.NONE) {
|
||||
return chain.proceed(request);
|
||||
}
|
||||
|
||||
boolean logBody = level == Level.BODY;
|
||||
boolean logHeaders = logBody || level == Level.HEADERS;
|
||||
|
||||
RequestBody requestBody = request.body();
|
||||
boolean hasRequestBody = requestBody != null;
|
||||
|
||||
Connection connection = chain.connection();
|
||||
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
|
||||
String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;
|
||||
if (!logHeaders && hasRequestBody) {
|
||||
requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
|
||||
}
|
||||
logger.log(requestStartMessage);
|
||||
if (logHeaders) {
|
||||
if (hasRequestBody) {
|
||||
// Request body headers are only present when installed as a network interceptor. Force
|
||||
// them to be included (when available) so there values are known.
|
||||
if (requestBody.contentType() != null) {
|
||||
logger.log("Content-Type: " + requestBody.contentType());
|
||||
}
|
||||
if (requestBody.contentLength() != -1) {
|
||||
logger.log("Content-Length: " + requestBody.contentLength());
|
||||
}
|
||||
}
|
||||
|
||||
Headers headers = request.headers();
|
||||
for (int i = 0, count = headers.size(); i < count; i++) {
|
||||
String name = headers.name(i);
|
||||
// Skip headers from the request body as they are explicitly logged above.
|
||||
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
|
||||
logger.log(name + ": " + headers.value(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!logBody || !hasRequestBody) {
|
||||
logger.log("--> END " + request.method());
|
||||
} else if (bodyEncoded(request.headers())) {
|
||||
logger.log("--> END " + request.method() + " (encoded body omitted)");
|
||||
} else {
|
||||
|
||||
/* //modify by yangjinbo 2016/02/15 在打印请求体内容日志时,采用封装原始的请求体,在实际写入的时候打印日志
|
||||
Buffer buffer = new Buffer();
|
||||
requestBody.writeTo(buffer);
|
||||
|
||||
Charset charset = UTF8;
|
||||
MediaType contentType = requestBody.contentType();
|
||||
if (contentType != null) {
|
||||
charset = contentType.charset(UTF8);
|
||||
}
|
||||
|
||||
logger.log("");
|
||||
logger.log(buffer.readString(charset));
|
||||
|
||||
logger.log("--> END " + request.method()
|
||||
+ " (" + requestBody.contentLength() + "-byte body)");
|
||||
*/
|
||||
|
||||
request = request.newBuilder()
|
||||
.method(request.method(), new LogRequestBody(requestBody, logger))
|
||||
.build();
|
||||
logger.log("");
|
||||
logger.log("--> END " + request.method()
|
||||
+ ";; request body is :");
|
||||
}
|
||||
}
|
||||
|
||||
long startNs = System.nanoTime();
|
||||
Response response = chain.proceed(request);
|
||||
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
|
||||
|
||||
|
||||
ResponseBody responseBody = response.body();
|
||||
long contentLength = responseBody.contentLength();
|
||||
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
|
||||
logger.log("<-- " + response.code() + ' ' + response.message() + ' '
|
||||
+ response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "
|
||||
+ bodySize + " body" : "") + ')');
|
||||
|
||||
if (logHeaders) {
|
||||
Headers headers = response.headers();
|
||||
for (int i = 0, count = headers.size(); i < count; i++) {
|
||||
logger.log(headers.name(i) + ": " + headers.value(i));
|
||||
}
|
||||
|
||||
if (!logBody) {
|
||||
logger.log("<-- END HTTP");
|
||||
} else if (bodyEncoded(response.headers())) {
|
||||
logger.log("<-- END HTTP (encoded body omitted)");
|
||||
} else if (bodyImage(response.headers())) {
|
||||
logger.log("<-- END HTTP (body is image, omitted)");
|
||||
} else {
|
||||
/* // modify by yangjinbo 2016/02/15 在打印响应体内容时,采用封装原始响应,在实际读取的时候打印日志
|
||||
BufferedSource source = responseBody.source();
|
||||
source.request(Long.MAX_VALUE); // Buffer the entire body.
|
||||
Buffer buffer = source.buffer();
|
||||
|
||||
Charset charset = UTF8;
|
||||
MediaType contentType = responseBody.contentType();
|
||||
if (contentType != null) {
|
||||
try {
|
||||
charset = contentType.charset(UTF8);
|
||||
} catch (UnsupportedCharsetException e) {
|
||||
logger.log("");
|
||||
logger.log("Couldn't decode the response body; charset is likely malformed.");
|
||||
logger.log("<-- END HTTP");
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentLength != 0) {
|
||||
logger.log("");
|
||||
logger.log(buffer.clone().readString(charset));
|
||||
}
|
||||
|
||||
logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
|
||||
*/
|
||||
|
||||
logger.log("<-- END HTTP ;; response body is :");
|
||||
return response.newBuilder().body(new LogResponseBody(response.body(),
|
||||
logger)).build();
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private boolean bodyEncoded(Headers headers) {
|
||||
String contentEncoding = headers.get("Content-Encoding");
|
||||
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
|
||||
}
|
||||
|
||||
private boolean bodyImage(Headers headers) {
|
||||
String contentType = headers.get("Content-Type");
|
||||
return contentType != null && contentType.toLowerCase().startsWith("image/");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 打印请求体内容
|
||||
*/
|
||||
private static class LogRequestBody extends RequestBody {
|
||||
|
||||
//实际的待包装请求体
|
||||
private final RequestBody mRequestBody;
|
||||
|
||||
private final Logger mLogger;
|
||||
|
||||
/**
|
||||
* 构造函数,赋值
|
||||
*
|
||||
* @param requestBody 待包装的请求体
|
||||
*/
|
||||
public LogRequestBody(RequestBody requestBody, Logger logger) {
|
||||
mRequestBody = requestBody;
|
||||
mLogger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentType
|
||||
*
|
||||
* @return MediaType
|
||||
*/
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mRequestBody.contentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentLength
|
||||
*
|
||||
* @return contentLength
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return mRequestBody.contentLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写进行写入
|
||||
*
|
||||
* @param sink BufferedSink
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
//包装
|
||||
BufferedSink bufferedSink = Okio.buffer(sink(sink));
|
||||
//写入
|
||||
mRequestBody.writeTo(bufferedSink);
|
||||
//必须调用flush,否则最后一部分数据可能不会被写入
|
||||
bufferedSink.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入,打印内容
|
||||
*
|
||||
* @param sink Sink
|
||||
* @return Sink
|
||||
*/
|
||||
private Sink sink(Sink sink) {
|
||||
|
||||
return new ForwardingSink(sink) {
|
||||
@Override
|
||||
public void write(Buffer source, long byteCount) throws IOException {
|
||||
mLogger.log(source.snapshot((int) byteCount).utf8());
|
||||
super.write(source, byteCount);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印响应体内容
|
||||
*/
|
||||
private static class LogResponseBody extends ResponseBody {
|
||||
//实际的待包装响应体
|
||||
private final ResponseBody mResponseBody;
|
||||
|
||||
private final Logger mLogger;
|
||||
|
||||
/**
|
||||
* 构造函数,赋值
|
||||
*
|
||||
* @param responseBody 待包装的响应体
|
||||
*/
|
||||
public LogResponseBody(ResponseBody responseBody, Logger logger) {
|
||||
mResponseBody = responseBody;
|
||||
mLogger = logger;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentType
|
||||
*
|
||||
* @return MediaType
|
||||
*/
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mResponseBody.contentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentLength
|
||||
*
|
||||
* @return contentLength
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return mResponseBody.contentLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写进行包装source
|
||||
*
|
||||
* @return BufferedSource
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public BufferedSource source() {
|
||||
return Okio.buffer(source(mResponseBody.source()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取,打印响应内容
|
||||
*
|
||||
* @param source Source
|
||||
* @return Source
|
||||
*/
|
||||
private Source source(Source source) {
|
||||
|
||||
return new ForwardingSource(source) {
|
||||
private Buffer mBuffer;
|
||||
|
||||
@Override
|
||||
public long read(Buffer sink, long byteCount) throws IOException {
|
||||
if (mBuffer == null) {
|
||||
mBuffer = new Buffer();
|
||||
}
|
||||
long bytesRead = super.read(mBuffer, byteCount);
|
||||
if (bytesRead != -1) {
|
||||
mLogger.log(mBuffer.snapshot((int) bytesRead).utf8());
|
||||
sink.write(mBuffer, bytesRead);
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.fengliyan.http.BuildConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class LoggerInterceptor implements Interceptor {
|
||||
|
||||
private static String TAG = "HTTP-------";
|
||||
|
||||
|
||||
@Override
|
||||
public Response intercept(@NonNull Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.e(TAG, String.format("发送请求:%s on %s%n%s%n%s",
|
||||
request.url(), chain.connection(), request.headers(), request.body()));
|
||||
}
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
import com.fengliyan.http.httprequest.ProgressListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
* Reference: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0904/3416.html
|
||||
*/
|
||||
public class ProgressHelper {
|
||||
/**
|
||||
* 包装OkHttpClient,用于下载文件的回调
|
||||
*
|
||||
* @param client 待包装的OkHttpClient
|
||||
* @param progressListener 进度回调接口
|
||||
* @return 包装后的OkHttpClient,使用clone方法返回
|
||||
*/
|
||||
public static OkHttpClient addProgressResponseListener(OkHttpClient client, final ProgressListener
|
||||
progressListener) {
|
||||
//克隆
|
||||
OkHttpClient clone = client.newBuilder().addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
//拦截
|
||||
Response originalResponse = chain.proceed(chain.request());
|
||||
//包装响应体并返回
|
||||
return originalResponse.newBuilder().body(new ProgressResponseBody(originalResponse.body(),
|
||||
progressListener)).build();
|
||||
}
|
||||
}).build();
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装请求体用于上传文件的回调
|
||||
*
|
||||
* @param requestBody 请求体RequestBody
|
||||
* @param progressRequestListener 进度回调接口
|
||||
* @return 包装后的进度回调请求体
|
||||
*/
|
||||
public static RequestBody addProgressRequestListener(RequestBody requestBody, ProgressListener
|
||||
progressRequestListener) {
|
||||
//包装请求体
|
||||
return new ProgressRequestBody(requestBody, progressRequestListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
import com.fengliyan.http.httprequest.ProgressListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSink;
|
||||
import okio.ForwardingSink;
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
* Reference: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0904/3416.html
|
||||
*/
|
||||
public class ProgressRequestBody extends RequestBody {
|
||||
|
||||
//实际的待包装请求体
|
||||
private final RequestBody mRequestBody;
|
||||
//进度回调接口
|
||||
private final ProgressListener mProgressListener;
|
||||
//包装完成的BufferedSink
|
||||
private BufferedSink mBufferedSink;
|
||||
|
||||
/**
|
||||
* 构造函数,赋值
|
||||
*
|
||||
* @param requestBody 待包装的请求体
|
||||
* @param progressListener 回调接口
|
||||
*/
|
||||
public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
|
||||
mRequestBody = requestBody;
|
||||
mProgressListener = progressListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentType
|
||||
*
|
||||
* @return MediaType
|
||||
*/
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mRequestBody.contentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentLength
|
||||
*
|
||||
* @return contentLength
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return mRequestBody.contentLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写进行写入
|
||||
*
|
||||
* @param sink BufferedSink
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
if (mBufferedSink == null) {
|
||||
//包装
|
||||
mBufferedSink = Okio.buffer(sink(sink));
|
||||
}
|
||||
//写入
|
||||
mRequestBody.writeTo(mBufferedSink);
|
||||
//必须调用flush,否则最后一部分数据可能不会被写入
|
||||
mBufferedSink.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入,回调进度接口
|
||||
*
|
||||
* @param sink Sink
|
||||
* @return Sink
|
||||
*/
|
||||
private Sink sink(Sink sink) {
|
||||
|
||||
return new ForwardingSink(sink) {
|
||||
//当前写入字节数
|
||||
long bytesWritten = 0L;
|
||||
//总字节长度,避免多次调用contentLength()方法
|
||||
long contentLength = 0L;
|
||||
|
||||
@Override
|
||||
public void write(Buffer source, long byteCount) throws IOException {
|
||||
try {
|
||||
super.write(source, byteCount);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
if (contentLength == 0) {
|
||||
//获得contentLength的值,后续不再调用
|
||||
contentLength = contentLength();
|
||||
}
|
||||
//增加当前写入的字节数
|
||||
bytesWritten += byteCount;
|
||||
//回调
|
||||
mProgressListener.onProgress(bytesWritten, contentLength, bytesWritten == contentLength);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
mBufferedSink = null;
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.fengliyan.http.httprequest.OkHttp;
|
||||
|
||||
import com.fengliyan.http.httprequest.ProgressListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.ResponseBody;
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
import okio.ForwardingSource;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
* Reference: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0904/3416.html
|
||||
*/
|
||||
public class ProgressResponseBody extends ResponseBody {
|
||||
|
||||
//实际的待包装响应体
|
||||
private final ResponseBody mResponseBody;
|
||||
|
||||
//进度回调接口
|
||||
private final ProgressListener mProgressListener;
|
||||
|
||||
//包装完成的BufferedSource
|
||||
private BufferedSource mBufferedSource;
|
||||
|
||||
/**
|
||||
* 构造函数,赋值
|
||||
*
|
||||
* @param responseBody 待包装的响应体
|
||||
* @param progressListener 回调接口
|
||||
*/
|
||||
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
|
||||
mResponseBody = responseBody;
|
||||
mProgressListener = progressListener;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentType
|
||||
*
|
||||
* @return MediaType
|
||||
*/
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mResponseBody.contentType();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写调用实际的响应体的contentLength
|
||||
*
|
||||
* @return contentLength
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return mResponseBody.contentLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写进行包装source
|
||||
*
|
||||
* @return BufferedSource
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
@Override
|
||||
public BufferedSource source(){
|
||||
if (mBufferedSource == null) {
|
||||
//包装
|
||||
mBufferedSource = Okio.buffer(source(mResponseBody.source()));
|
||||
}
|
||||
return mBufferedSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取,回调进度接口
|
||||
*
|
||||
* @param source Source
|
||||
* @return Source
|
||||
*/
|
||||
private Source source(Source source) {
|
||||
|
||||
return new ForwardingSource(source) {
|
||||
//当前读取字节数
|
||||
long totalBytesRead = 0L;
|
||||
|
||||
@Override
|
||||
public long read(Buffer sink, long byteCount) throws IOException {
|
||||
long bytesRead;
|
||||
try {
|
||||
bytesRead = super.read(sink, byteCount);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
//增加当前读取的字节数,如果读取完成了bytesRead会返回-1
|
||||
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
|
||||
//回调,如果contentLength()不知道长度,会返回-1
|
||||
mProgressListener.onProgress(totalBytesRead, mResponseBody.contentLength(), bytesRead == -1);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
public void close() throws IOException {
|
||||
mBufferedSource = null;
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
*/
|
||||
public interface ProgressListener {
|
||||
|
||||
/**
|
||||
* 上传或下载的进度反馈
|
||||
*
|
||||
* @param bytesCount 已上传或下载的字节数
|
||||
* @param contentLength 需要上传或下载的总字节数,下载的长度获取不到时可能返回-1
|
||||
* @param done 是否已经成功完成,注意异常情况下,该值不会返回true
|
||||
*/
|
||||
void onProgress(long bytesCount, long contentLength, boolean done);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by yangjinbo on 2016/3/28.
|
||||
*/
|
||||
public class RequestParameter extends ArrayList<String> {
|
||||
|
||||
public void addParameter(String key, String value) {
|
||||
add(key);
|
||||
add(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
*/
|
||||
public interface StreamCallback extends ProgressListener {
|
||||
|
||||
/**
|
||||
* 请求失败回调的函数,请求失败包括服务器未返回,或服务器返回的HTTP STATUS CODE标示失败
|
||||
*
|
||||
* @param httpStatusCode
|
||||
* @param message
|
||||
* @param throwable 如果httpStatusCode为-1的话,由该参数标识发生的异常
|
||||
*/
|
||||
void onFailure(int httpStatusCode, String message, Throwable throwable);
|
||||
|
||||
/**
|
||||
* 请求成功回调的函数,请求成功仅表示服务器正常处理请求了,不代表业务处理成功
|
||||
*
|
||||
* @param httpStatusCode
|
||||
* @param inputStream 输入流,用完注意关闭资源
|
||||
*/
|
||||
void onSuccess(int httpStatusCode, InputStream inputStream);
|
||||
}
|
||||
19
http/src/main/java/com/fengliyan/http/httprequest/TType.java
Normal file
19
http/src/main/java/com/fengliyan/http/httprequest/TType.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Created by siva on 16/1/15.
|
||||
*/
|
||||
public abstract class TType<T> {
|
||||
|
||||
/**
|
||||
* //由于泛型的擦试原因,无法在子类中获取T.class,因此需要通过反射机制获取
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public final Type getTType() {
|
||||
return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/24.
|
||||
*/
|
||||
public class UploadFile {
|
||||
|
||||
//提交表格时,文件对应的名称,可以与文件名不同或为null,用于后台区分
|
||||
private String mName;
|
||||
|
||||
//表格文件
|
||||
private File mFile;
|
||||
|
||||
public UploadFile(String name, File file) {
|
||||
|
||||
if (file == null) {
|
||||
throw new IllegalArgumentException("file cannot be null");
|
||||
}
|
||||
|
||||
mName = name;
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
public UploadFile(String formName, String filePath) {
|
||||
|
||||
if (filePath == null) {
|
||||
throw new IllegalArgumentException("filePath cannot be null");
|
||||
}
|
||||
|
||||
mName = formName;
|
||||
File file = new File(filePath);
|
||||
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new IllegalArgumentException("file not found: " + filePath);
|
||||
}
|
||||
|
||||
mFile = file;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return mFile;
|
||||
}
|
||||
}
|
||||
90
http/src/main/java/com/fengliyan/http/httprequest/Utils.java
Normal file
90
http/src/main/java/com/fengliyan/http/httprequest/Utils.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package com.fengliyan.http.httprequest;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.core.content.PermissionChecker;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Created by siva on 15/12/25.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
/**
|
||||
* 关闭资源而忽略异常
|
||||
*
|
||||
* @param closeable
|
||||
*/
|
||||
public static void CloseQuietly(Closeable closeable) {
|
||||
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON字符串转化为实体类,或者字符串
|
||||
*
|
||||
* @param json
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
/*
|
||||
public static <T> T fromJson(String json, Class<T> clazz) {
|
||||
|
||||
if (clazz == null) {
|
||||
throw new IllegalArgumentException("clazz cannot be null");
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (clazz.equals(String.class)) {
|
||||
return (T) json;
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
return gson.fromJson(json, clazz);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 将JSON字符串转化为实体类,或者字符串
|
||||
*
|
||||
* @param json
|
||||
* @param type
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T fromJson(String json, Type type) {
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("type cannot be null");
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type.equals(String.class)) {
|
||||
return (T) json;
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
return gson.fromJson(json, type);
|
||||
}
|
||||
|
||||
public static boolean checkPermission(Context context, String permName) {
|
||||
return PermissionChecker.checkSelfPermission(context, permName)
|
||||
== PermissionChecker.PERMISSION_GRANTED;
|
||||
// return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.fengliyan.http.httprequest.cookie;
|
||||
|
||||
import android.content.Context;
|
||||
import android.webkit.CookieSyncManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.net.CookieStore;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 兼容XHR与WebView的CookieManager,使得在WebView里的Cookie与XHR里的Cookie相通。
|
||||
* Created by siva on 16/1/2.
|
||||
*/
|
||||
public class XhrWebkitCookieManager extends CookieManager {
|
||||
|
||||
private static final String TAG = XhrWebkitCookieManager.class.getSimpleName();
|
||||
private android.webkit.CookieManager mWebkitCookieManager;
|
||||
private CookiePutInListener mListener;
|
||||
|
||||
public interface CookiePutInListener{
|
||||
void onCookiePutIn(URI uri);
|
||||
}
|
||||
|
||||
public void setOnCookiePutInListener(CookiePutInListener listener){
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public XhrWebkitCookieManager(Context context) {
|
||||
super(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER);
|
||||
|
||||
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context);
|
||||
mWebkitCookieManager = android.webkit.CookieManager.getInstance();
|
||||
mWebkitCookieManager.setAcceptCookie(true);
|
||||
mWebkitCookieManager.removeAllCookie();
|
||||
cookieSyncManager.sync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders)
|
||||
throws IOException {
|
||||
|
||||
|
||||
// make sure our args are valid
|
||||
if ((uri == null) || (requestHeaders == null))
|
||||
throw new IllegalArgumentException("Argument is null");
|
||||
|
||||
// save our url once
|
||||
String url = uri.toString();
|
||||
|
||||
// prepare our response
|
||||
Map<String, List<String>> ret = new HashMap<String, List<String>>();
|
||||
|
||||
// get the cookie
|
||||
String cookie = mWebkitCookieManager.getCookie(url);
|
||||
|
||||
|
||||
// return it
|
||||
if (cookie != null) {
|
||||
ret.put("Cookie", Arrays.asList(cookie));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(URI uri, Map<String, List<String>> responseHeaders) throws IOException {
|
||||
|
||||
if ((uri == null) || (responseHeaders == null))
|
||||
return;
|
||||
|
||||
String url = uri.toString();
|
||||
if(null != mListener) {
|
||||
mListener.onCookiePutIn(uri);
|
||||
}
|
||||
|
||||
// go over the headers
|
||||
for (String headerKey : responseHeaders.keySet()) {
|
||||
// ignore headers which aren't cookie related
|
||||
if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey
|
||||
.equalsIgnoreCase("Set-Cookie"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// process each of the headers
|
||||
for (String headerValue : responseHeaders.get(headerKey)) {
|
||||
mWebkitCookieManager.setCookie(url, headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCookiePolicy(CookiePolicy cookiePolicy) {
|
||||
super.setCookiePolicy(cookiePolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CookieStore getCookieStore() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.fengliyan.http.httprequest.plugin;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.fengliyan.http.httprequest.RequestParameter;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by yuxiaoqiu on 2016/9/13.
|
||||
*/
|
||||
public interface IHttpCacheInterface {
|
||||
|
||||
void writeCache(@NonNull String result, @NonNull String url, RequestParameter parameters);
|
||||
|
||||
HashMap<String, String> readCache(@NonNull String url, RequestParameter parameters, Context context);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.fengliyan.http.httprequest.plugin;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.fengliyan.http.httprequest.RequestParameter;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by yuxiaoqiu on 2016/9/13.
|
||||
*/
|
||||
public class MaleHttpCache {
|
||||
|
||||
// private IHttpCacheInterface _httpCache;
|
||||
//
|
||||
// public IHttpCacheInterface HttpCache(){
|
||||
// get
|
||||
// {
|
||||
// return this._usbDrive;
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// this._usbDrive = value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public MuHttpCache() {
|
||||
// }
|
||||
//
|
||||
// public MuHttpCache(IHttpCacheInterface httpCache) {
|
||||
// this.HttpCache = httpCache;
|
||||
// }
|
||||
//
|
||||
// public void writeCache(@NonNull String result, @NonNull String url, RequestParameter parameters) {
|
||||
// this.HttpCache.writeCache(result, url, parameters);
|
||||
// }
|
||||
//
|
||||
// public void readCache(@NonNull String url, RequestParameter parameters, Context context) {
|
||||
// this.HttpCache.readCache(url, parameters, context);
|
||||
// }
|
||||
|
||||
private IHttpCacheInterface _httpCache;
|
||||
public static MaleHttpCache sInstance;
|
||||
|
||||
private boolean mInitialized = false;
|
||||
|
||||
private MaleHttpCache() {
|
||||
}
|
||||
|
||||
public static MaleHttpCache getInstance() {
|
||||
if (null == sInstance) {
|
||||
synchronized (MaleHttpCache.class) {
|
||||
if (null == sInstance) {
|
||||
sInstance = new MaleHttpCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
||||
public void writeCache(@NonNull String result, @NonNull String url, RequestParameter parameters) {
|
||||
if (mInitialized) {
|
||||
_httpCache.writeCache(result, url, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
public HashMap<String, String> readCache(@NonNull String url, RequestParameter parameters, Context context) {
|
||||
if(mInitialized){
|
||||
return _httpCache.readCache(url, parameters, context);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void register(IHttpCacheInterface _httpCache) {
|
||||
this._httpCache = _httpCache;
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
20
http/src/main/res/values/arrays.xml
Normal file
20
http/src/main/res/values/arrays.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer-array name="xhr_normal_timeoutMs">
|
||||
<item name="connect">15000</item>
|
||||
<item name="read">15000</item>
|
||||
<item name="write">15000</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="xhr_download_timeoutMs">
|
||||
<item name="connect">15000</item>
|
||||
<item name="read">30000</item>
|
||||
<item name="write">30000</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="xhr_upload_timeoutMs">
|
||||
<item name="connect">15000</item>
|
||||
<item name="read">120000</item>
|
||||
<item name="write">120000</item>
|
||||
</integer-array>
|
||||
</resources>
|
||||
6
http/src/main/res/values/strings.xml
Normal file
6
http/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<string name="app_name">HttpRequest</string>
|
||||
<string name="xhr_sslVerification">true</string>
|
||||
<string name="xhr_enableCache">true</string>
|
||||
<string name="xhr_cacheMaxSize">5242880</string>
|
||||
</resources>
|
||||
17
http/src/test/java/com/fengliyan/http/ExampleUnitTest.java
Normal file
17
http/src/test/java/com/fengliyan/http/ExampleUnitTest.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.fengliyan.http;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user