第一次提交

This commit is contained in:
被淹死的鱼
2026-03-11 18:26:29 +08:00
commit cd3b53759e
8532 changed files with 522078 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fengliyan.http" />

View 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包含三个参数顺序为connectreadwrite的超时时间
*/
public int[] normalTimeoutMs() {
return mContext.getResources().getIntArray(R.array.xhr_normal_timeoutMs);
}
/**
* 下载时的请求超时时间。如果APP需要修改时在APP的资源文件中重写数组{@link R.array#xhr_download_timeoutMs}
*
* @return 超时时间数组单位ms包含三个参数顺序为connectreadwrite的超时时间
*/
public int[] downloadTimeoutMs() {
return mContext.getResources().getIntArray(R.array.xhr_download_timeoutMs);
}
/**
* 上传时的请求超时时间。如果APP需要修改时在APP的资源文件中重写数组{@link R.array#xhr_upload_timeoutMs}
*
* @return 超时时间数组单位ms包含三个参数顺序为connectreadwrite的超时时间
*/
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;
}
}

View File

@@ -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];
//}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,10 @@
package com.fengliyan.http.httprequest;
/**
* Created by siva on 15/12/23.
*/
public enum HttpMethod {
GET,
POST
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
};
}
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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();
}
};
}
}

View File

@@ -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();
}
};
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View 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];
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View 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>

View 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>