自定义网络层

最近更新时间:2025-03-26 10:55:42

我的收藏

简介

本文介绍如何在 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 作为示例。
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;

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

@Override
public NetworkProxy getNetworkProxy() {
CustomizeOkHttpNetworkProxy customizeOkHttpNetworkProxy = new CustomizeOkHttpNetworkProxy(okHttpClient);
return customizeOkHttpNetworkProxy;
}
}
说明:
更多完整示例,请前往 GitHub 查看。
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;
}

@Override
public void cancel(){
if (httpCall != null) {
httpCall.cancel();
}
}

@Override
public Response callHttpRequest(Request okHttpRequest) throws IOException {
httpCall = okHttpClient.newCall(okHttpRequest);
return httpCall.execute();
}
}
说明:
更多完整示例,请前往 GitHub 查看。
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;
@Override
public void init(QCloudHttpClient.Builder b, HostnameVerifier hostnameVerifier, Dns dns, HttpLogger httpLogger) {
super.init(b, hostnameVerifier, dns, httpLogger);
httpURLConnectionManager = new HttpURLConnectionManager();
}

@Override
public 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 headers
for (String name : request.headers().names()) {
connection.setRequestProperty(name, request.header(name));
Log.d("NetworkRequest", "Header: " + name + " = " + request.header(name));
}

// Set body
if (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;
}
}
}
说明:
更多完整示例,请前往 GitHub 查看。
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;
}

@Override
protected void cancel() {
if (connection != null) {
connection.disconnect();
}
}

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

@Override
protected 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 headers
Headers.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 body
BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));
ResponseBody body = new ResponseBody() {
@Override
public MediaType contentType() {
if(TextUtils.isEmpty(contentType)){
return null;
} else {
return MediaType.parse(contentType);
}
}

@Override
public long contentLength() {
return contentLength;
}

@Override
public BufferedSource source() {
return source;
}
};

// Build OkHttp Response
Request 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();
}
}
说明:
更多完整示例,请前往 GitHub 查看。
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
说明:
更多完整示例,请前往 GitHub
#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 *)httpRequest
fromFile:(NSURL *)fromFile
session:(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
说明:
更多完整示例,请前往 GitHub
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
说明:
更多完整示例,请前往 GitHub
#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 *)request
fromFile:(NSURL *)fromFile{
QCloudAFLoaderTask * task = [[QCloudAFLoaderTask alloc]initWithHTTPRequest:request fromFile:fromFile session:[QCloudAFLoaderSession session]];
return task;
}
@end
说明:
更多完整示例,请前往 GitHub
3. 自定义 QCloudCustomLoader 代理实现类实现 QCloudAFLoader 代理 。
#import <Foundation/Foundation.h>
#import "QCloudCustomLoader.h"
NS_ASSUME_NONNULL_BEGIN

@interface QCloudAFLoader : NSObject <QCloudCustomLoader>

@end
说明:
更多完整示例,请前往 GitHub
#import "QCloudAFLoader.h"
#import "QCloudAFLoaderSession.h"
@implementation QCloudAFLoader
// 持有自定义 session 实例。
-(QCloudCustomSession *)session{
return [QCloudAFLoaderSession session];
}
// 是否启用当前网络层
-(BOOL)enable:(QCloudHTTPRequest *)httpRequest{
return YES;
}
@end

说明:
更多完整示例,请前往 GitHub
4. 配置 COS SDK,将自定义网络加载到 COS SDK 中,并开启。
// 项目启动时,将自定义 loader 初始化并调用 [QCloudLoaderManager manager] 的 addLoader 方法,加载进 SDK 的自定义网络 loader 中
// 支持添加多个 自定义 loader,业务层动态切换。
[[QCloudLoaderManager manager]addLoader: [[QCloudAFLoader alloc]init]];
// 开启自定义网络层开关。当次开关关闭时 所有自定义 loader 不可用,
[QCloudLoaderManager manager].enable = YES;