简介
本文介绍如何在 COS SDK 中自定义网络层。
功能说明
默认情况下 COS SDK 的网络层是基于 OKHTTP 实现的,也提供了 QUIC SDK 的 QUIC 协议网络实现,现在 COS SDK 也支持更灵活的自定义网络层,方便开发者接管和控制 COS SDK 中网络通信部分。
因此,如果 COS SDK 默认的网络实现不符合您的要求或者您的 APP 本身已经开发了完善的自定义网络库,则可以使用该文档提供的方式在 COS SDK 中自定义网络层。
在自定义网络层中可以更加灵活的进行 DNS 解析优化、网络线路加速、弱网优化以及其他网络优化。
注意:
自定义网络层功能需要对网络编程有一定的了解,如果您不确定如何使用这个功能,我们推荐您使用 COS SDK 的默认网络实现。
方案概述
Android
1. 继承 NetworkClient 实现自定义网络 Client,实现网络请求的管理、配置等。
2. 继承 NetworkProxy 实现网络请求代理,实现具体的网络请求。
3. 通过 CosXmlServiceConfig 的 setCustomizeNetworkClient 方法配置 COS SDK。
iOS
1. 继承 QCloudCustomLoaderTask 实现网络请求任务类。
2. 继承 QCloudCustomSession 实现自定义网络 Session,用于构建自定义任务、进度回调、完成回调等逻辑的处理。
3. 实现 QCloudCustomLoader 代理,用于开启自定义网络和构建自定义 Session 实例。
实现示例
注意:
以下示例仅作为基础的说明性示例,具体实现请结合业务需求进行完善或修改。
Android
Android 以 OKHTTP 和系统自带的 HttpURLConnection 作为示例。
1. 继承 NetworkClient 实现自定义网络 Client。
import com.tencent.qcloud.core.http.HttpLogger;import com.tencent.qcloud.core.http.HttpLoggingInterceptor;import com.tencent.qcloud.core.http.NetworkClient;import com.tencent.qcloud.core.http.NetworkProxy;import com.tencent.qcloud.core.http.QCloudHttpClient;import java.util.concurrent.TimeUnit;import javax.net.ssl.HostnameVerifier;import okhttp3.Dns;import okhttp3.OkHttpClient;/*** 此处实现自定义网络 Client,例如 OkHttpClient 实例*/public class CustomizeOkHttpNetworkClient extends NetworkClient {private static final String TAG = "CustomizeOkHttpNetworkClient";private OkHttpClient okHttpClient;@Overridepublic void init(QCloudHttpClient.Builder b, HostnameVerifier hostnameVerifier,final Dns dns, HttpLogger httpLogger) {super.init(b, hostnameVerifier, dns, httpLogger);HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(httpLogger);logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);OkHttpClient.Builder builder = new OkHttpClient.Builder().connectTimeout(15 * 1000, TimeUnit.MILLISECONDS).readTimeout(30 * 1000, TimeUnit.MILLISECONDS).writeTimeout(30 * 1000, TimeUnit.MILLISECONDS).addInterceptor(logInterceptor);okHttpClient = builder.build();}@Overridepublic NetworkProxy getNetworkProxy() {CustomizeOkHttpNetworkProxy customizeOkHttpNetworkProxy = new CustomizeOkHttpNetworkProxy(okHttpClient);return customizeOkHttpNetworkProxy;}}
说明:
2. 继承 NetworkProxy 实现网络请求代理。
import com.tencent.qcloud.core.http.NetworkProxy;import java.io.IOException;import okhttp3.Call;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;/*** 此处实现一个网络请求,例如 okhttp 的 Call*/public class CustomizeOkHttpNetworkProxy<T> extends NetworkProxy<T> {private static final String TAG = "CustomizeOkHttpNetworkProxy";private Call httpCall;private OkHttpClient okHttpClient;public CustomizeOkHttpNetworkProxy(OkHttpClient okHttpClient){this.okHttpClient = okHttpClient;}@Overridepublic void cancel(){if (httpCall != null) {httpCall.cancel();}}@Overridepublic Response callHttpRequest(Request okHttpRequest) throws IOException {httpCall = okHttpClient.newCall(okHttpRequest);return httpCall.execute();}}
说明:
3. 配置 COS SDK。
CosXmlServiceConfig cosXmlServiceConfig = new CosXmlServiceConfig.Builder(.setRegion(region))// 其他配置不变.setCustomizeNetworkClient(new CustomizeOkHttpNetworkClient()) // 设置上述自定义网络 client.builder();
1. 继承 NetworkClient 实现自定义网络 Client。
import android.util.Log;import com.tencent.qcloud.core.http.HttpLogger;import com.tencent.qcloud.core.http.NetworkClient;import com.tencent.qcloud.core.http.NetworkProxy;import com.tencent.qcloud.core.http.QCloudHttpClient;import java.io.IOException;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;import javax.net.ssl.HostnameVerifier;import okhttp3.Dns;import okhttp3.Request;import okio.BufferedSink;import okio.Okio;/*** 此处实现自定义网络 Client*/public class CustomizeNetworkClient extends NetworkClient {private static final String TAG = "CustomizeNetworkClient";private HttpURLConnectionManager httpURLConnectionManager;@Overridepublic void init(QCloudHttpClient.Builder b, HostnameVerifier hostnameVerifier, Dns dns, HttpLogger httpLogger) {super.init(b, hostnameVerifier, dns, httpLogger);httpURLConnectionManager = new HttpURLConnectionManager();}@Overridepublic NetworkProxy getNetworkProxy() {return new CustomizeNetworkProxy(httpURLConnectionManager);}public static class HttpURLConnectionManager {private static final int CONNECT_TIMEOUT = 5000;private static final int READ_TIMEOUT = 5000;public HttpURLConnection createConnection(Request request) throws IOException {Log.d("NetworkRequest", "Request URL: " + request.url());Log.d("NetworkRequest", "Request method: " + request.method());URL url = new URL(request.url().toString());HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(CONNECT_TIMEOUT);connection.setReadTimeout(READ_TIMEOUT);connection.setRequestMethod(request.method());// Set headersfor (String name : request.headers().names()) {connection.setRequestProperty(name, request.header(name));Log.d("NetworkRequest", "Header: " + name + " = " + request.header(name));}// Set bodyif (request.body() != null) {connection.setDoOutput(true);try (OutputStream outputStream = connection.getOutputStream();BufferedSink bufferedSink = Okio.buffer(Okio.sink(outputStream))) {request.body().writeTo(bufferedSink);}Log.d("NetworkRequest", "Request body set");}return connection;}}}
说明:
2. 继承 NetworkProxy 实现网络请求代理。
import android.text.TextUtils;import android.util.Log;import com.tencent.qcloud.core.http.NetworkProxy;import java.io.IOException;import java.net.HttpURLConnection;import java.util.List;import java.util.Map;import okhttp3.Headers;import okhttp3.MediaType;import okhttp3.Protocol;import okhttp3.Request;import okhttp3.Response;import okhttp3.ResponseBody;import okio.BufferedSource;import okio.Okio;/*** 此处实现一个网络请求*/public class CustomizeNetworkProxy<T> extends NetworkProxy<T> {private static final String TAG = "CustomizeNetworkProxy";private HttpURLConnection connection;private CustomizeNetworkClient.HttpURLConnectionManager httpURLConnectionManager;public CustomizeNetworkProxy(CustomizeNetworkClient.HttpURLConnectionManager httpURLConnectionManager) {this.httpURLConnectionManager = httpURLConnectionManager;}@Overrideprotected void cancel() {if (connection != null) {connection.disconnect();}}@Overridepublic Response callHttpRequest(Request okHttpRequest) throws IOException {try {connection = httpURLConnectionManager.createConnection(okHttpRequest);int responseCode = connection.getResponseCode();Log.d("NetworkRequest", "Response code: " + responseCode);Response response = convertToOkHttpResponse(connection);Log.d("NetworkRequest", "Response headers: " + response.headers());return response;} catch (IOException e) {Log.e("NetworkRequest", "Failed to execute HTTP request", e);throw e;}}@Overrideprotected void disconnect(){if (connection != null) {connection.disconnect();}}private Response convertToOkHttpResponse(HttpURLConnection connection) throws IOException {int code = connection.getResponseCode();String message = connection.getResponseMessage();String contentType = connection.getContentType();int contentLength = connection.getContentLength();// Convert headersHeaders.Builder headersBuilder = new Headers.Builder();Map<String, List<String>> headerFields = connection.getHeaderFields();for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {String name = entry.getKey();if (TextUtils.isEmpty(name)) continue;for (String value : entry.getValue()) {headersBuilder.add(name, value);}}Headers headers = headersBuilder.build();// Convert bodyBufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));ResponseBody body = new ResponseBody() {@Overridepublic MediaType contentType() {if(TextUtils.isEmpty(contentType)){return null;} else {return MediaType.parse(contentType);}}@Overridepublic long contentLength() {return contentLength;}@Overridepublic BufferedSource source() {return source;}};// Build OkHttp ResponseRequest request = new Request.Builder().url(connection.getURL().toString()).build();return new Response.Builder().request(request).protocol(Protocol.HTTP_1_1).code(code).message(message).headers(headers).body(body).build();}}
说明:
3. 配置 COS SDK。
CosXmlServiceConfig cosXmlServiceConfig = new CosXmlServiceConfig.Builder(.setRegion(region))// 其他配置不变.setCustomizeNetworkClient(new CustomizeNetworkClient()) // 设置上述自定义网络 client.builder();
iOS
iOS 以 AFNetworking 作为示例。
1. 继承 QCloudCustomLoaderTask 实现自定义任务类 QCloudAFLoaderTask。
#import <Foundation/Foundation.h>#import "QCloudCustomLoader.h"@interface QCloudAFLoaderTask : QCloudCustomLoaderTask@end
说明:
#import "QCloudAFLoaderTask.h"#import "QCloudAFLoaderSession.h"#import "AFNetworking/AFNetworking.h"@interface QCloudAFLoaderTask ()@property (nonatomic,strong)QCloudCustomSession *customSession;@property (nonatomic,strong)NSMutableURLRequest *httpRequest;@property (nonatomic,strong)NSURL *fromFile;@property (nonatomic,strong)NSURLSessionDataTask *task;@end@implementation QCloudAFLoaderTask// 自定义任务构造方法- (instancetype)initWithHTTPRequest:(NSMutableURLRequest *)httpRequestfromFile:(NSURL *)fromFilesession:(QCloudCustomSession *)session{self = [super init];if (self) {self.fromFile = fromFile;self.httpRequest = httpRequest;self.currentRequest = httpRequest;self.originalRequest = httpRequest;self.customSession = session;}return self;}// 自定义任务启动方法,此方法由业务层实现。由 SDK 负责调用。// 使用自己网络库构建网络请求并发起。// 进度回调、完成回调等逻辑调用自定义 session 对应的方法。传递给 sdk 中。-(void)resume{QCloudWeakSelf(self);if (!self.fromFile) {self.task = [[[QCloudAFLoaderSession session] manager] dataTaskWithRequest:self.httpRequest uploadProgress:^(NSProgress * _Nonnull uploadProgress) {QCloudStrongSelf(self);// 处理上传进度回调。[strongself.customSession customTask:self didSendBodyData:uploadProgress.completedUnitCount totalBytesSent:uploadProgress.totalUnitCount totalBytesExpectedToSend:uploadProgress.totalUnitCount];} downloadProgress:^(NSProgress * _Nonnull downloadProgress) {QCloudStrongSelf(self);// 处理下载进度回调。[strongself.customSession customTask:self didSendBodyData:downloadProgress.completedUnitCount totalBytesSent:downloadProgress.totalUnitCount totalBytesExpectedToSend:downloadProgress.totalUnitCount];} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {QCloudStrongSelf(self);// 处理完成进度回调。[strongself.customSession customTask:strongself didReceiveResponse:response completionHandler:nil];[strongself.customSession customTask:strongself didReceiveData:responseObject];[strongself.customSession customTask:strongself didCompleteWithError:error];}];// 发起请求[self.task resume];}else{// 上传本地文件self.task = [[[QCloudAFLoaderSession session] manager] uploadTaskWithRequest:self.httpRequest fromFile:self.fromFile progress:^(NSProgress * _Nonnull uploadProgress) {QCloudStrongSelf(self);// 处理上传进度回调。[strongself.customSession customTask:strongself didSendBodyData:uploadProgress.completedUnitCount totalBytesSent:uploadProgress.totalUnitCount totalBytesExpectedToSend:uploadProgress.totalUnitCount];} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {QCloudStrongSelf(self);// 处理完成进度回调。[strongself.customSession customTask:strongself didReceiveResponse:response completionHandler:nil];[strongself.customSession customTask:strongself didReceiveData:responseObject];[strongself.customSession customTask:strongself didCompleteWithError:error];}];// 发起请求[self.task resume];}}- (void)cancel{[self.task cancel];}@end
说明:
2. 继承 QCloudCustomSession 实现自定义 Session 类 QCloudAFLoaderSession。
#import <Foundation/Foundation.h>#import "QCloudCore/QCloudCustomLoader.h"#import "AFNetworking/AFNetworking.h"NS_ASSUME_NONNULL_BEGIN@interface QCloudAFLoaderSession : QCloudCustomSession// 自定义session 持有 AFURLSessionManager 实例。@property (nonatomic,strong)AFURLSessionManager *manager;+(QCloudAFLoaderSession *)session;@end
说明:
#import "QCloudAFLoaderSession.h"#import "QCloudAFLoaderTask.h"@implementation QCloudAFLoaderSession// QCloudAFLoaderSession为单例+(QCloudAFLoaderSession *)session{static QCloudAFLoaderSession *session = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{session = [[QCloudAFLoaderSession alloc] init];});return session;}- (instancetype)init{self = [super init];if (self) {// 初始化 AFURLSessionManager;NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];self.manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];self.manager.responseSerializer = [[AFCompoundResponseSerializer alloc]init];}return self;}-(QCloudCustomLoaderTask *)taskWithRequset:(NSMutableURLRequest *)requestfromFile:(NSURL *)fromFile{QCloudAFLoaderTask * task = [[QCloudAFLoaderTask alloc]initWithHTTPRequest:request fromFile:fromFile session:[QCloudAFLoaderSession session]];return task;}@end
说明:
3. 自定义 QCloudCustomLoader 代理实现类实现 QCloudAFLoader 代理 。
#import <Foundation/Foundation.h>#import "QCloudCustomLoader.h"NS_ASSUME_NONNULL_BEGIN@interface QCloudAFLoader : NSObject <QCloudCustomLoader>@end
说明:
#import "QCloudAFLoader.h"#import "QCloudAFLoaderSession.h"@implementation QCloudAFLoader// 持有自定义 session 实例。-(QCloudCustomSession *)session{return [QCloudAFLoaderSession session];}// 是否启用当前网络层-(BOOL)enable:(QCloudHTTPRequest *)httpRequest{return YES;}@end
说明:
4. 配置 COS SDK,将自定义网络加载到 COS SDK 中,并开启。
// 项目启动时,将自定义 loader 初始化并调用 [QCloudLoaderManager manager] 的 addLoader 方法,加载进 SDK 的自定义网络 loader 中// 支持添加多个 自定义 loader,业务层动态切换。[[QCloudLoaderManager manager]addLoader: [[QCloudAFLoader alloc]init]];// 开启自定义网络层开关。当次开关关闭时 所有自定义 loader 不可用,[QCloudLoaderManager manager].enable = YES;