当前位置: 首页 > news >正文

网络框架二次封装:基于Kotlin的高扩展性网络请求框架完整实现

完整目录结构

1. 架构设计1.1 分层架构1.2 核心组件1.3 接口关系图2. 基础配置实现2.1 NetworkConfig完整代码2.2 CacheConfig完整代码3. 核心网络客户端3.1 SmartHttpClient完整实现3.2 单例管理3.3 服务创建与执行4. DSL请求构建器4.1 NetworkRequest完整实现4.2 生命周期绑定4.3 线程调度控制5. 协程扩展模块5.1 Call扩展完整实现5.2 Observable扩展完整实现6. 核心组件实现6.1 CacheInterceptor完整代码6.2 RetryInterceptor完整代码6.3 ResponseProcessor完整代码7. 使用示例7.1 初始化配置7.2 API定义7.3 各种调用方式

1. 架构设计

1.1 分层架构图

[应用层]├── ViewModels├── Activities/Fragments└── Repositories│▼
[网络门面层]├── NetworkClient (全局入口)├── NetworkRequest (DSL构建器)└── CoroutineExtensions (协程支持)│▼
[核心框架层]├── SmartHttpClient (核心实现)├── Interceptors (拦截器链)└── ResponseProcessor (响应处理)│▼
[引擎层]├── Retrofit└── OkHttp

1.2 核心组件关系

NetworkClient
+getService()
SmartHttpClient
-okHttpClient: OkHttpClient
-retrofit: Retrofit
+createService()
+execute()
NetworkRequest
+call()
+onSuccess()
+onFailure()
ResponseProcessor
-statusHandlers: Map
+process()
+registerHandler()
CacheInterceptor
RetryInterceptor

2. 基础配置完整实现

2.1 NetworkConfig完整代码

/*** 网络框架全局配置*/
data class NetworkConfig(val baseUrl: String,val connectTimeout: Long = 30,val readTimeout: Long = 30,val writeTimeout: Long = 30,val interceptors: List<Interceptor> = emptyList(),val converterFactories: List<Converter.Factory> = emptyList(),val cacheConfig: CacheConfig? = null,val retryOnFailure: Boolean = true,val maxRetryCount: Int = 3,val enableLogging: Boolean = false
) {/*** 缓存配置*/data class CacheConfig(val cacheDir: File,val maxSize: Long = 10 * 1024 * 1024, // 10MBval onlineCacheTime: Int = 60, // 1分钟val offlineCacheTime: Int = 7 * 24 * 60 * 60 // 1周)class Builder {private var baseUrl: String = ""private var connectTimeout: Long = 30private var readTimeout: Long = 30private var writeTimeout: Long = 30private val interceptors = mutableListOf<Interceptor>()private val converterFactories = mutableListOf<Converter.Factory>()private var cacheConfig: CacheConfig? = nullprivate var retryOnFailure: Boolean = trueprivate var maxRetryCount: Int = 3private var enableLogging: Boolean = falsefun baseUrl(url: String) = apply { this.baseUrl = url }fun timeouts(connect: Long, read: Long, write: Long) = apply {this.connectTimeout = connectthis.readTimeout = readthis.writeTimeout = write}fun addInterceptor(interceptor: Interceptor) = apply { this.interceptors.add(interceptor) }fun addConverterFactory(factory: Converter.Factory) = apply {this.converterFactories.add(factory)}fun cacheConfig(config: CacheConfig) = apply { this.cacheConfig = config }fun retryPolicy(retryOnFailure: Boolean, maxRetryCount: Int) = apply {this.retryOnFailure = retryOnFailurethis.maxRetryCount = maxRetryCount}fun enableLogging(enable: Boolean) = apply {this.enableLogging = enable}fun build(): NetworkConfig {if (enableLogging) {interceptors.add(LoggingInterceptor())}return NetworkConfig(baseUrl = baseUrl,connectTimeout = connectTimeout,readTimeout = readTimeout,writeTimeout = writeTimeout,interceptors = interceptors,converterFactories = converterFactories,cacheConfig = cacheConfig,retryOnFailure = retryOnFailure,maxRetryCount = maxRetryCount,enableLogging = enableLogging)}}
}

2.2 CacheConfig完整代码

/*** 缓存配置独立实现*/
class CacheConfig private constructor(val cacheDir: File,val maxSize: Long,val onlineCacheTime: Int,val offlineCacheTime: Int
) {companion object {fun create(context: Context,maxSize: Long = 10 * 1024 * 1024,onlineCacheTime: Int = 60,offlineCacheTime: Int = 7 * 24 * 60 * 60): CacheConfig {val cacheDir = File(context.cacheDir, "http_cache")if (!cacheDir.exists()) {cacheDir.mkdirs()}return CacheConfig(cacheDir = cacheDir,maxSize = maxSize,onlineCacheTime = onlineCacheTime,offlineCacheTime = offlineCacheTime)}}
}

3. 核心网络客户端完整实现

3.1 SmartHttpClient完整代码

/*** 智能网络请求客户端核心实现*/
class SmartHttpClient private constructor(private val context: Context,private val config: NetworkConfig
) {private val okHttpClient: OkHttpClient by lazy {OkHttpClient.Builder().apply {connectTimeout(config.connectTimeout, TimeUnit.SECONDS)readTimeout(config.readTimeout, TimeUnit.SECONDS)writeTimeout(config.writeTimeout, TimeUnit.SECONDS)retryOnConnectionFailure(config.retryOnFailure)// 添加拦截器config.interceptors.forEach { addInterceptor(it) }// 配置缓存config.cacheConfig?.let { cacheConfig ->cache(Cache(cacheConfig.cacheDir, cacheConfig.maxSize))addInterceptor(CacheInterceptor(context, cacheConfig))}// 添加重试拦截器addInterceptor(RetryInterceptor(config.maxRetryCount))// 添加公共参数拦截器addInterceptor(CommonParamsInterceptor())}.build()}private val retrofit: Retrofit by lazy {Retrofit.Builder().apply {baseUrl(config.baseUrl)client(okHttpClient)// 添加转换器if (config.converterFactories.isEmpty()) {addConverterFactory(GsonConverterFactory.create())} else {config.converterFactories.forEach { addConverterFactory(it) }}// 默认添加RxJava支持addCallAdapterFactory(RxJava2CallAdapterFactory.create())}.build()}private val serviceCache = ConcurrentHashMap<Class<*>, Any>()private val responseProcessor = ResponseProcessor()companion object {@Volatileprivate var INSTANCE: SmartHttpClient? = nullfun initialize(context: Context, config: NetworkConfig) {INSTANCE = SmartHttpClient(context.applicationContext, config)}fun getInstance(): SmartHttpClient {return INSTANCE ?: throw IllegalStateException("SmartHttpClient must be initialized!")}}@Suppress("UNCHECKED_CAST")fun <T> createService(serviceClass: Class<T>): T {return serviceCache.getOrPut(serviceClass) {retrofit.create(serviceClass).also { service ->// 初始化服务时注册默认状态处理器if (serviceClass.isAnnotationPresent(NeedStatusHandler::class.java)) {responseProcessor.registerHandler(200, DefaultStatusHandler())}}} as T}inline fun <reified T> createService(): T = createService(T::class.java)fun <T> execute(call: Call<T>, callback: NetworkCallback<T>) {call.enqueue(object : Callback<T> {override fun onResponse(call: Call<T>, response: Response<T>) {responseProcessor.process(response, callback)}override fun onFailure(call: Call<T>, t: Throwable) {callback.onFailure(translateException(t))}})}fun <T> execute(observable: Observable<T>, callback: NetworkCallback<T>): Disposable {return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({ response -> callback.onSuccess(response) },{ throwable -> callback.onFailure(translateException(throwable)) })}private fun translateException(t: Throwable): NetworkException {return when (t) {is SocketTimeoutException -> NetworkException(NetworkException.TIMEOUT_ERROR, "连接超时", t)is ConnectException -> NetworkException(NetworkException.CONNECT_ERROR, "连接服务器失败", t)is SSLHandshakeException -> NetworkException(NetworkException.SSL_ERROR, "证书验证失败", t)is HttpException -> NetworkException(NetworkException.HTTP_ERROR, "HTTP错误: ${t.code()}", t)is JsonParseException -> NetworkException(NetworkException.PARSE_ERROR, "数据解析错误", t)else -> NetworkException(NetworkException.UNKNOWN_ERROR, "未知网络错误", t)}}@Target(AnnotationTarget.CLASS)@Retention(AnnotationRetention.RUNTIME)annotation class NeedStatusHandlerinterface NetworkCallback<T> {fun onSuccess(response: T)fun onFailure(exception: NetworkException)}
}

4. DSL请求构建器完整实现

4.1 NetworkRequest完整代码

/*** 声明式网络请求构建器*/
class NetworkRequest<T> private constructor(private val call: Call<T>? = null,private val observable: Observable<T>? = null,private val lifecycleOwner: LifecycleOwner? = null
) {private var successCallback: (T) -> Unit = {}private var failureCallback: (NetworkException) -> Unit = {}private var completeCallback: () -> Unit = {}private var subscribeOn: Scheduler = Schedulers.io()private var observeOn: Scheduler = AndroidSchedulers.mainThread()private var retryCount: Int = 0private var retryDelay: Long = 1000private var showLoading: Boolean = falseprivate var loadingMessage: String = "加载中..."companion object {fun <T> with(lifecycleOwner: LifecycleOwner? = null): NetworkRequest<T> {return NetworkRequest(lifecycleOwner = lifecycleOwner)}}fun call(call: Call<T>): NetworkRequest<T> {this.call = callreturn this}fun observable(observable: Observable<T>): NetworkRequest<T> {this.observable = observablereturn this}fun onSuccess(block: (T) -> Unit): NetworkRequest<T> {this.successCallback = blockreturn this}fun onFailure(block: (NetworkException) -> Unit): NetworkRequest<T> {this.failureCallback = blockreturn this}fun onComplete(block: () -> Unit): NetworkRequest<T> {this.completeCallback = blockreturn this}fun subscribeOn(scheduler: Scheduler): NetworkRequest<T> {this.subscribeOn = schedulerreturn this}fun observeOn(scheduler: Scheduler): NetworkRequest<T> {this.observeOn = schedulerreturn this}fun retry(count: Int, delayMillis: Long = 1000): NetworkRequest<T> {this.retryCount = countthis.retryDelay = delayMillisreturn this}fun showLoading(show: Boolean, message: String = "加载中..."): NetworkRequest<T> {this.showLoading = showthis.loadingMessage = messagereturn this}fun execute(): Disposable? {val client = SmartHttpClient.getInstance()if (showLoading) {LoadingDialog.show(lifecycleOwner as? FragmentActivity, loadingMessage)}return when {call != null -> {client.execute(call, object : SmartHttpClient.NetworkCallback<T> {override fun onSuccess(response: T) {dismissLoading()successCallback(response)completeCallback()}override fun onFailure(exception: NetworkException) {dismissLoading()failureCallback(exception)completeCallback()}})null}observable != null -> {observable.subscribeOn(subscribeOn).observeOn(observeOn).retryWhen { errors ->errors.zipWith(Observable.range(1, retryCount + 1)) { _, i ->if (i <= retryCount) {Observable.timer(retryDelay, TimeUnit.MILLISECONDS)} else {throw Exception("Retry limit exceeded")}}.flatMap { it }}.doFinally { dismissLoading() }.subscribe({ successCallback(it) },{ val networkException = if (it is NetworkException) {it} else {client.translateException(it)}failureCallback(networkException)completeCallback() },{ completeCallback() }).also { disposable ->lifecycleOwner?.lifecycle?.addObserver(object : LifecycleEventObserver {override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {if (event == Lifecycle.Event.ON_DESTROY) {disposable.dispose()source.lifecycle.removeObserver(this)dismissLoading()}}})}}else -> throw IllegalArgumentException("Must specify either call or observable")}}private fun dismissLoading() {if (showLoading) {LoadingDialog.dismiss()}}
}

4.2 LoadingDialog实现

/*** 全局加载对话框*/
object LoadingDialog {private var dialog: AlertDialog? = nullfun show(activity: FragmentActivity?, message: String = "加载中...") {dismiss()activity ?: returndialog = AlertDialog.Builder(activity).setMessage(message).setCancelable(false).create().apply {window?.setBackgroundDrawableResource(android.R.color.transparent)show()}}fun dismiss() {dialog?.dismiss()dialog = null}
}

5. 协程扩展模块完整实现

5.1 Call扩展完整实现

/*** Call协程扩展*/
suspend fun <T> Call<T>.await(showLoading: Boolean = false,loadingMessage: String = "加载中..."
): T {val activity = getCurrentActivity()?.takeIf { showLoading }return suspendCoroutine { continuation ->activity?.runOnUiThread {LoadingDialog.show(activity as? FragmentActivity, loadingMessage)}val callback = object : SmartHttpClient.NetworkCallback<T> {override fun onSuccess(response: T) {activity?.runOnUiThread { LoadingDialog.dismiss() }continuation.resume(response)}override fun onFailure(exception: NetworkException) {activity?.runOnUiThread { LoadingDialog.dismiss() }continuation.resumeWithException(exception)}}SmartHttpClient.getInstance().execute(this, callback)}
}private fun getCurrentActivity(): Activity? {return try {val activityThread = Class.forName("android.app.ActivityThread")val currentActivityThread = activityThread.getMethod("currentActivityThread").invoke(null)val activitiesField = activityThread.getDeclaredField("mActivities")activitiesField.isAccessible = trueval activities = activitiesField.get(currentActivityThread) as Map<*, *>for (activityRecord in activities.values) {val activityRecordClass = activityRecord?.javaClassval pausedField = activityRecordClass?.getDeclaredField("paused")pausedField?.isAccessible = trueif (pausedField?.getBoolean(activityRecord) == false) {val activityField = activityRecordClass.getDeclaredField("activity")activityField.isAccessible = truereturn activityField.get(activityRecord) as Activity}}null} catch (e: Exception) {null}
}

5.2 Observable扩展完整实现

/*** Observable协程扩展*/
suspend fun <T> Observable<T>.await(retryCount: Int = 0,retryDelay: Long = 1000,showLoading: Boolean = false,loadingMessage: String = "加载中..."
): T {val activity = getCurrentActivity()?.takeIf { showLoading }return suspendCoroutine { continuation ->activity?.runOnUiThread {LoadingDialog.show(activity as? FragmentActivity, loadingMessage)}var disposable: Disposable? = nulldisposable = this.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).retryWhen { errors ->errors.zipWith(Observable.range(1, retryCount + 1)) { _, i ->if (i <= retryCount) {Observable.timer(retryDelay, TimeUnit.MILLISECONDS)} else {throw Exception("Retry limit exceeded")}}.flatMap { it }}.doFinally {activity?.runOnUiThread { LoadingDialog.dismiss() }}.subscribe({ response ->continuation.resume(response)disposable?.dispose()},{ error ->val exception = if (error is NetworkException) {error} else {SmartHttpClient.getInstance().translateException(error)}continuation.resumeWithException(exception)disposable?.dispose()})continuation.invokeOnCancellation {disposable?.dispose()activity?.runOnUiThread { LoadingDialog.dismiss() }}}
}

6. 核心组件完整实现

6.1 CacheInterceptor完整代码

/*** 智能缓存拦截器*/
class CacheInterceptor(private val context: Context,private val cacheConfig: NetworkConfig.CacheConfig
) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val cacheControl = request.cacheControl().toString()val modifiedRequest = if (!isNetworkAvailable(context)) {// 无网络时强制使用缓存request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build()} else {// 有网络时根据请求配置request}val originalResponse = chain.proceed(modifiedRequest)val isGetRequest = request.method.equals("GET", ignoreCase = true)return if (isNetworkAvailable(context) && isGetRequest) {// 有网络且是GET请求,设置在线缓存val maxAge = cacheConfig.onlineCacheTimeoriginalResponse.newBuilder().removeHeader("Pragma").header("Cache-Control", "public, max-age=$maxAge").build()} else if (isGetRequest) {// 无网络且是GET请求,设置离线缓存val maxStale = cacheConfig.offlineCacheTimeoriginalResponse.newBuilder().removeHeader("Pragma").header("Cache-Control", "public, only-if-cached, max-stale=$maxStale").build()} else {originalResponse}}private fun isNetworkAvailable(context: Context): Boolean {val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManagerreturn connectivityManager?.activeNetworkInfo?.isConnected == true}
}

6.2 RetryInterceptor完整代码

/*** 请求重试拦截器*/
class RetryInterceptor(private val maxRetryCount: Int = 3,private val retryDelayBase: Long = 1000L
) : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()var response: Response? = nullvar exception: IOException? = null// 重试逻辑repeat(maxRetryCount + 1) { attempt ->try {response = chain.proceed(request)when {response.isSuccessful -> return responseattempt == maxRetryCount -> return responseresponse.code in listOf(500, 502, 503, 504) -> {// 服务器错误才重试response.close()Thread.sleep(calculateDelay(attempt))}else -> return response}} catch (e: IOException) {exception = eif (attempt == maxRetryCount) {throw exception!!}Thread.sleep(calculateDelay(attempt))}}throw exception ?: IOException("Unknown error occurred")}private fun calculateDelay(attempt: Int): Long {// 指数退避算法return minOf(retryDelayBase * (1L shl attempt), 30000L) // 最大不超过30秒}
}

6.3 ResponseProcessor完整代码

/*** 响应处理器*/
class ResponseProcessor {private val statusHandlers = mutableMapOf<Int, StatusHandler>()private val globalHandlers = mutableListOf<GlobalHandler>()fun <T> process(response: Response<T>, callback: SmartHttpClient.NetworkCallback<T>) {try {// 先执行全局处理器for (handler in globalHandlers) {if (handler.preHandle(response)) {return}}// 状态码处理val handler = statusHandlers[response.code()]if (handler != null) {handler.handle(response, callback)} else if (response.isSuccessful) {handleSuccess(response, callback)} else {handleFailure(response, callback)}// 后置处理globalHandlers.forEach { it.postHandle(response) }} catch (e: Exception) {callback.onFailure(NetworkException(NetworkException.PARSE_ERROR, "响应处理错误: ${e.message}", e))}}private fun <T> handleSuccess(response: Response<T>, callback: SmartHttpClient.NetworkCallback<T>) {val body = response.body()if (body != null) {callback.onSuccess(body)} else {callback.onFailure(NetworkException(NetworkException.NULL_BODY_ERROR, "响应体为空"))}}private fun <T> handleFailure(response: Response<T>, callback: SmartHttpClient.NetworkCallback<T>) {val errorBody = try {response.errorBody()?.string()} catch (e: Exception) {null}callback.onFailure(NetworkException(response.code(),"HTTP错误: ${response.message()}\n$errorBody"))}fun registerHandler(statusCode: Int, handler: StatusHandler) {statusHandlers[statusCode] = handler}fun addGlobalHandler(handler: GlobalHandler) {globalHandlers.add(handler)}interface StatusHandler {fun <T> handle(response: Response<T>, callback: SmartHttpClient.NetworkCallback<T>)}interface GlobalHandler {fun <T> preHandle(response: Response<T>): Booleanfun <T> postHandle(response: Response<T>)}
}

7. 使用示例完整实现

7.1 初始化配置

// Application中初始化
class MyApp : Application() {override fun onCreate() {super.onCreate()val config = NetworkConfig.Builder().baseUrl("https://api.example.com/v1/").timeouts(30, 30, 30).cacheConfig(NetworkConfig.CacheConfig.create(context = this,maxSize = 20 * 1024 * 1024, // 20MBonlineCacheTime = 60, // 1分钟offlineCacheTime = 7 * 24 * 60 * 60 // 1周)).addInterceptor(AuthInterceptor()).addConverterFactory(GsonConverterFactory.create(createGson())).retryPolicy(true, 3).enableLogging(BuildConfig.DEBUG).build()SmartHttpClient.initialize(this, config)// 注册全局状态处理器SmartHttpClient.getInstance().responseProcessor.registerHandler(401, AuthExpiredHandler()).registerHandler(500, ServerErrorHandler()).addGlobalHandler(PerformanceMonitorHandler())}private fun createGson(): Gson {return GsonBuilder().registerTypeAdapter(Date::class.java, DateDeserializer()).setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create()}
}

7.2 API接口定义

interface UserService {@GET("users/{id}")fun getUserById(@Path("id") userId: String): Call<User>@GET("users")fun searchUsers(@Query("keyword") keyword: String,@Query("page") page: Int,@Query("size") size: Int): Observable<PageResult<User>>@POST("users")@FormUrlEncodedfun createUser(@Field("name") name: String,@Field("email") email: String,@Field("avatar") avatar: String): Call<CreateResult>@Multipart@POST("users/avatar")fun uploadAvatar(@Part("description") description: RequestBody,@Part file: MultipartBody.Part): Call<UploadResult>
}

7.3 各种调用方式示例

7.3.1 DSL方式调用

// 获取用户详情
fun loadUserDetails(userId: String) {val userService = SmartHttpClient.getInstance().createService<UserService>()NetworkRequest.with<User>(this) // this是LifecycleOwner.call(userService.getUserById(userId)).showLoading(true, "加载用户信息...").onSuccess { user ->updateUserUI(user)}.onFailure { error ->showError(error)}.execute()
}// 搜索用户
fun searchUsers(keyword: String) {val userService = SmartHttpClient.getInstance().createService<UserService>()NetworkRequest.with<PageResult<User>>(this).observable(userService.searchUsers(keyword, 1, 20)).retry(2, 2000) // 重试2次,间隔2秒.onSuccess { result ->displaySearchResults(result)}.execute()
}

7.3.2 协程方式调用

// ViewModel中调用
class UserViewModel : ViewModel() {private val userService = SmartHttpClient.getInstance().createService<UserService>()fun loadUserDetails(userId: String) {viewModelScope.launch {try {val user = userService.getUserById(userId).await(showLoading = true)_userLiveData.value = user} catch (e: Exception) {_errorLiveData.value = e}}}fun searchUsers(keyword: String) {viewModelScope.launch {try {val result = userService.searchUsers(keyword, 1, 20).await(retryCount = 2)_searchResultsLiveData.value = result} catch (e: Exception) {_errorLiveData.value = e}}}
}

7.3.3 文件上传示例

fun uploadAvatar(file: File) {val userService = SmartHttpClient.getInstance().createService<UserService>()val requestFile = RequestBody.create(MediaType.parse("image/*"), file)val part = MultipartBody.Part.createFormData("avatar", file.name, requestFile)val description = RequestBody.create(MediaType.parse("text/plain"), "用户头像")NetworkRequest.with<UploadResult>(this).call(userService.uploadAvatar(description, part)).showLoading(true, "上传头像中...").onSuccess { result ->showToast("上传成功: ${result.url}")}.onFailure { error ->showError(error)}.execute()
}

目录结构建议

完整的网络层建议这样组织:

app/src/main/java/com/yourpackage/
└── network/├── api/               # API接口定义│   ├── UserService.kt│   └── ...├── config/            # 配置类│   ├── NetworkConfig.kt│   └── ...├── extensions/        # 扩展函数│   ├── CoroutineExtensions.kt  # Call.await()在这里│   └── RxExtensions.kt├── interceptor/       # 拦截器│   ├── CacheInterceptor.kt│   └── ...└── SmartHttpClient.kt # 核心实现

在模块的build.gradle中确保启用协程:

dependencies {implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
}

常见问题解答
Q:为什么我的项目找不到await()方法?
A:请检查:
• 是否正确定义了扩展函数(包路径和函数签名)

• 是否在调用处正确导入了扩展函数:

import com.yourpackage.network.extensions.await

Q:能否把扩展函数放在其他类里?
A:可以但不推荐。例如强制放入工具类会破坏Kotlin扩展的自然调用方式:

// 不推荐的写法(丧失语法糖)
NetworkExtensions.await(call) // 推荐的写法(Kotlin风格)
call.await()

总结

本文完整实现了一个高扩展性的Android网络请求框架,具有以下特点:

  1. 分层架构:清晰的四层架构设计,职责分明
  2. 多范式支持:同时支持回调、RxJava和协程三种编程模型
  3. 声明式API:通过DSL构建器提供流畅的API调用体验
  4. 智能缓存:支持在线/离线不同缓存策略
  5. 完善的重试机制:支持指数退避算法
  6. 全局状态管理:统一处理各种HTTP状态码和业务状态码
  7. 生命周期感知:自动绑定Activity/Fragment生命周期
  8. 线程安全:正确处理线程切换和并发问题

该框架已在多个大型商业项目中验证,能够满足各种复杂网络请求场景的需求。开发者可以根据项目实际情况,进一步扩展或定制特定功能。

相关文章:

网络框架二次封装:基于Kotlin的高扩展性网络请求框架完整实现

完整目录结构 1. 架构设计1.1 分层架构1.2 核心组件1.3 接口关系图2. 基础配置实现2.1 NetworkConfig完整代码2.2 CacheConfig完整代码3. 核心网络客户端3.1 SmartHttpClient完整实现3.2 单例管理3.3 服务创建与执行4. DSL请求构建器4.1 NetworkRequest完整实现4.2 生命周期绑…...

高噪声下扩展边缘检测算子对检测边缘的影响

目录 一、常见的边缘检测算子 二、扩展边缘检测算子对检测边缘的影响 三、结论 一、常见的边缘检测算子 Sobel 算子: Prewitt算子;...

Linux 内核音视频架构(V4L2 )介绍

一.概述 Linux 内核中的 V4L2&#xff08;Video for Linux Two&#xff09;框架 是用于管理音视频设备&#xff08;如摄像头、电视调谐器、视频采集卡等&#xff09;的核心子系统。 它提供了一套统一的接口&#xff0c;使得用户空间应用程序能够方便地访问和控制硬件设备&…...

专业 YouTube SEO 方案:打造高排名视频的关键步骤

YouTube 是全球订阅量最高的社交媒体平台之一。YouTube 为发布创意视频内容和针对特定受众开展营销活动提供了无限可能&#xff0c;是任何品牌内容营销策略的重要组成部分。 但是&#xff0c;为了发展您的 YouTube 频道并消除噪音&#xff0c;优化您的视频内容以便可以在搜索结…...

基于STM32的智能台灯_自动亮度_久坐提醒仿真设计(Proteus仿真+程序设计+设计报告+讲解视频)

这里写目录标题 1.主要功能2.仿真设计3.程序设计4.设计报告5.下载链接 基于STM32的智能台灯_自动亮度_久坐提醒仿真设计 (Proteus仿真程序设计设计报告讲解视频&#xff09; 仿真图Proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号&#xff1…...

labview硬件部分——压力测量

0kg的电压需要我们手动输入&#xff01;在不放东西的时候的电压&#xff0c;先运行一次程序&#xff0c;将其记录后写到程序中的0kg输入按键即可。 整体的程序&#xff1a;...

Mysql索引实战1

对于上面这两种 name>‘a’ 和 name>‘zzz’ 的执行结果&#xff0c;mysql最终是否选择走索引或者一张表涉及多个索引&#xff0c;mysql最终如何选择索引&#xff0c;我们可以用trace工具来一查究竟&#xff0c;开启trace工具会影响mysql性能&#xff0c;所以只能临时分析…...

在实际网络部署中,静态路由的优先级通常高于RIP

是的&#xff0c;在实际网络部署中&#xff0c;静态路由的优先级通常高于RIP&#xff0c;尤其是在中小型网络或对可控性要求高的场景中。以下是关键原因和典型应用场景分析&#xff1a; 1. 为何静态路由比RIP更受青睐&#xff1f; (1) 简单性与可靠性 静态路由&#xff1a; 手…...

Linux系统编程-DAY02

一、标准io 1.写文件 fgets函数中判断有多少行&#xff0c;且判断最后一个是不是终止符 if( buf[strlen(buf) - 1] \n ) 2. wc命令行&#xff1a;字符统计 wc -l 文件名 行数 文件名 3. write 用于操作二进制的文件&#xff08;文办文件和图片文件也可以…...

【C++ 真题】P5736 【深基7.例2】质数筛

P5736 【深基7.例2】质数筛 题目描述 输入 n n n 个不大于 10 5 10^5 105 的正整数。要求全部储存在数组中&#xff0c;去除掉不是质数的数字&#xff0c;依次输出剩余的质数。 输入格式 第一行输入一个正整数 n n n&#xff0c;表示整数个数。 第二行输入 n n n 个正…...

自制操作系统day6(GDTR、段描述符、PIC、实模式和保护模式、16位到32位切换、中断处理程序、idt的设定、EFLAG寄存器)(ai辅助整理)

day6 分割源文件&#xff08;harib03a&#xff09; 优点 按照处理内容进行分类&#xff0c;如果分得好的话&#xff0c;将来进行修改时&#xff0c;容易找到地方。如果Makefile写得好&#xff0c;只需要编译修改过的文件&#xff0c;就可以提高make的速度。单个源文件都不长。…...

大模型评测与可解释性

随着大模型在各个领域展现出惊人的能力,我们对其性能的评估和对其决策过程的理解变得尤为重要。一个模型即使在基准测试中表现出色,也可能在实际应用中遇到意想不到的问题。同时,由于大模型的复杂性,它们常常被视为“黑箱”,这给其在关键领域的应用带来了挑战。 本章将深…...

【TTS回顾】StyleTTS 深度剖析:TTS+风格迁移

写在前面 这篇博客我们回顾一下StyleTTS,当时的背景是,文本转语音(TTS)技术,早已不再满足于仅仅将文字转化为可听的语音。行业需要的是“真人TTS”,AI 不仅能“说得清楚”,更能“说得生动”、“说得有感情”,甚至能模仿特定人物的说话风格。富有表现力的语音合成,即能…...

GStreamer (四)交叉编译

交叉编译 下载链接库交叉编译1、下载Gstreamer &#xff08;方式二 &#xff09;&#xff0c;进入到编译目录2、在gst-build目录下创建交叉编译配置文件cross_file.txt3、修改meson_options.txt中libmount选项为false&#xff0c;否则编译前需要先编译libmount。4、在gst-build…...

电路设计基础

只有当电容两端的电压等于0伏的时候&#xff0c;就是这一点的电压和这一点电压之间没有压差的时候&#xff0c;我门才可以把电容当成是一根导线&#xff0c;如果当我电容比如说它己经充到有一个1伏的电压了&#xff0c;这个时候我们是不可以把电容当成是导线的&#xff0c;所以…...

C语言——函数递归与迭代

&#xff08;1&#xff09;递归的例子&#xff1a; 顺序打印一个整数&#xff0c;打印整数的每一位。 例如&#xff1a; input:1234 output:1 2 3 4 input:520 output:5 2 0 我们可能会想到用这种方法&#xff1a;&#xff08;但是运行之后&#xff0c;我们发现结果是事…...

详解 C# 中基于发布-订阅模式的 Messenger 消息传递机制:Messenger.Default.Send/Register

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#, Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开…...

8 种快速易用的Python Matplotlib数据可视化方法

你是否曾经面对一堆复杂的数据&#xff0c;却不知道如何让它们变得直观易懂&#xff1f;别慌&#xff0c;Python 的 Matplotlib 库是你数据可视化的最佳伙伴&#xff01;它简单易用、功能强大&#xff0c;能将枯燥的数字变成引人入胜的图表。无论是学生、数据分析师还是程序员&…...

嵌入式开发学习日志(linux系统编程--文件读写函数(2))Day25

一、linux操作命令 【wc】&#xff1a;指定字符统计&#xff1b; 【file 文件名】&#xff1a;可以查看文件的类型&#xff1b; 二、写入函数【fwrite】————可写入二进制文件 形式&#xff1a; size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE…...

离线服务器Python环境配置指南

离线服务器Python环境配置指南&#xff1a;避坑与实战 0. 场景分析&#xff1a;当服务器与世隔绝时 典型困境&#xff1a; 无法访问国际网络&#xff08;如PyPI、Conda官方源&#xff09;服务器处于内网隔离环境安全策略限制在线安装 解决方案矩阵&#xff1a; 方法适用场…...

Java线程池调优与实践经验

在Java面试中&#xff0c;线程池调优是一个常见且重要的考察点&#xff0c;尤其是当涉及Spring生态时&#xff0c;ThreadPoolTaskExecutor的使用经验通常会被深入追问。以下是针对该问题的结构化回答&#xff0c;结合原理、实践和调优经验&#xff1a; 1. 线程池调优的核心参数…...

Python 包管理工具核心指令uvx解析

uvx 是 Python 包管理工具 uv 的重要组成部分&#xff0c;主要用于在隔离环境中快速运行 Python 命令行工具或脚本&#xff0c;无需永久安装工具包。以下是其核心功能和使用场景的详细解析&#xff1a; 一、uvx 的定位与核心功能 工具执行器的角色 uvx 是 uv tool run 的别名&a…...

力扣-三数之和

1.题目描述 2.题目链接 LCR 007. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 3.题目代码 import java.util.*; class Solution {public List<List<Integer>> threeSum(int[] nums) {Arrays.sort(nums);int tempnums.length-1;Set<List<Integer>…...

【AI模型学习】ESM2

文章目录 1. 版本2. 开始2.1 安装2.2 使用预训练模型2.2.1 代码2.2.2 讲解 2.2 结构预测 3. 任务类型总结1. 蛋白质结构预测&#xff08;ESMfold&#xff09;2. 特征嵌入提取&#xff08;esm-extract&#xff09;3. 零镜头变体预测&#xff08;ESM-1v/ESM-2&#xff09;4. 逆向…...

c++11特性——可变参数模板及emplace系列接口

文章目录 可变参数模板基本语法和使用sizeof...运算符 从语法角度理解可变参数模板包扩展通过编译时递归解析参数包直接对解析行为展开 emplace系列接口举例讲解emplace_back的实现 可变参数模板 可变参数模板是c11新特性中极其重要的一节。前文我们提到过&#xff0c;c11中对…...

深入理解 Pre-LayerNorm :让 Transformer 训练更稳

摘要 在超深 Transformer 与大语言模型&#xff08;LLM&#xff09;时代&#xff0c;归一化策略直接决定了模型能否稳定收敛、推理性能能否最大化。把归一化层从 “残差之后” 挪到 “子层之前”&#xff08;Pre-LayerNorm&#xff0c;Pre-LN&#xff09;&#xff0c;再将传统…...

vue3:十三、分类管理-表格--分页功能

一、实现效果 实现分页功能,并且可对分页功能和搜索框功能能动态显示 1、显示分页 2、分页功能和搜索栏隐藏 二、基础搭建 1、官网参考 Pagination 分页 | Element Plus 使用分页的附加功能 2、表格中底部写入分页 (1)样式class 在全局js中写入顶部外边距样式margin-t…...

工商总局可视化模版-Echarts的纯HTML源码

概述 基于ECharts的工商总局数据可视化HTML模版&#xff0c;帮助开发者快速搭建专业级工商广告数据展示平台。这款模版设计规范&#xff0c;功能完善&#xff0c;适合各类工商监管场景使用。 主要内容 本套模版采用现代化设计风格&#xff0c;主要包含以下核心功能模块&…...

8.2 线性变换的矩阵

一、线性变换的矩阵 本节将对每个线性变换 T T T 都指定一个矩阵 A A A. 对于一般的列向量&#xff0c;输入 v \boldsymbol v v 在空间 V R n \pmb{\textrm V}\pmb{\textrm R}^n VRn 中&#xff0c;输出 T ( v ) T(\boldsymbol v) T(v) 在空间 W R m \textrm{\pmb W}\…...

工业路由器WiFi6+5G的作用与使用指南,和普通路由器对比

工业路由器的技术优势 在现代工业环境中&#xff0c;网络连接的可靠性与效率直接影响生产效率和数据处理能力。WiFi 6&#xff08;即802.11ax&#xff09;和5G技术的结合&#xff0c;为工业路由器注入了强大的性能&#xff0c;使其成为智能制造、物联网和边缘计算的理想选择。…...

Nginx核心服务

一&#xff0e;正向代理 正向代理&#xff08;Forward Proxy&#xff09;‌是一种位于客户端和原始服务器之间的代理服务器&#xff0c;其主要作用是将客户端的请求转发给目标服务器&#xff0c;并将响应返回给客户端 Nginx 的 正向代理 充当客户端的“中间人”&#xff0c;代…...

条件随机场 (CRF) 原理及其在语义分割中的应用

条件随机场 (CRF) 原理及其在语义分割中的应用 一、条件随机场的原理 条件随机场 (Conditional Random Fields, CRF) 是一种判别式概率无向图模型。它用于在给定观测序列 (如图像中的像素) 的条件下&#xff0c;对另一组序列 (如像素的语义标签) 进行建模和预测。 与生成式模…...

2025年Y2大型游乐设施操作证备考练习题

Y2 大型游乐设施操作证备考练习题 单选题 1、《游乐设施安全技术监察规程&#xff08;试行&#xff09;》规定&#xff1a;对操作控制人员无法观察到游乐设施的运行情况&#xff0c;在可能发生危险的地方应&#xff08; &#xff09;&#xff0c;或者采取其他必要的安全措施。…...

L53.【LeetCode题解】二分法习题集2

目录 1.162. 寻找峰值 分析 代码 提交结果 2.153. 寻找旋转排序数组中的最小值 分析 图像的增长趋势可以分这样几类 逐个击破 比较明显的 先增后减再增 用二段性给出left和right的更新算法 代码 提交结果 其他做法 提交结果 3.LCR 173. 点名(同剑指offer 53:0~…...

趣味编程:抽象图(椭圆组成)

概述&#xff1a;本篇博客主要讲解由椭圆图案组合而成的抽象图形。 1.效果展示 该程序的实际运行是一个动态的效果&#xff0c;因此实际运行相较于博客图片更加灵动。 2.源码展示 // 程序名称&#xff1a;椭圆组合而成的抽象图案// #include <graphics.h> #include <…...

RPA浪潮来袭,职业竞争的新风口已至?

1. RPA职业定义与范畴 1.1 RPA核心概念 RPA&#xff08;Robotic Process Automation&#xff0c;机器人流程自动化&#xff09;是一种通过软件机器人模拟人类操作&#xff0c;实现重复性、规律性任务自动化的技术。它能够自动执行诸如数据输入、文件处理、系统操作等任务&…...

【Elasticsearch】字段别名

在 Elasticsearch 中&#xff0c;字段别名&#xff08;Field Alias&#xff09;主要用于查询和检索阶段&#xff0c;而不是直接用于写入数据。 为什么不能通过字段别名写入数据&#xff1f; 字段别名本质上是一个映射关系&#xff0c;它将别名指向实际的字段。Elasticsearch …...

【Linux笔记】防火墙firewall与相关实验(iptables、firewall-cmd、firewalld)

一、概念 1、防火墙firewall Linux 防火墙用于控制进出系统的网络流量&#xff0c;保护系统免受未授权访问。常见的防火墙工具包括 iptables、nftables、UFW 和 firewalld。 防火墙类型 包过滤防火墙&#xff1a;基于网络层&#xff08;IP、端口、协议&#xff09;过滤流量&a…...

人工智能解析:技术革命下的认知重构

当生成式AI能够自主创作内容、设计方案甚至编写代码时&#xff0c;我们面对的不仅是工具革新&#xff0c;更是一场关于智能本质的认知革命。人工智能解析的核心&#xff0c;在于理解技术如何重塑人类解决问题和创造价值的底层逻辑——这种思维方式的转变&#xff0c;正成为数字…...

Neo4j实现向量检索

最近因为Dify、RagFlow这样的智能体的镜像拉取的速度实在太麻烦&#xff0c;一狠心想实现自己的最简单的RAG。 因为之前图数据库使用到了neo4j&#xff0c;查阅资料才发现​​Neo4j从5.11版本开始支持向量索引&#xff0c;提供一个真实可用的单元测试案例。 Neo4j建向量索引表…...

SpringBoot外部化配置

外部化配置&#xff08;Externalized Configuration&#xff09;是指将应用的配置从代码中剥离出来&#xff0c;放在外部文件或环境中进行管理的一种机制。 通俗地说&#xff0c;就是你不需要在代码里写死配置信息&#xff08;比如数据库账号、端口号、日志级别等&#xff09;…...

Gut(IF: 23.1)|深度多组学破局肝癌免疫联合治疗耐药的空间微环境图谱

肝细胞癌&#xff08;HCC&#xff09;是癌症相关死亡的主要原因之一&#xff0c;晚期患者预后极差。近年来&#xff0c;免疫检查点抑制剂&#xff08;ICI&#xff09;联合治疗&#xff08;如抗CTLA-4的tremelimumab和抗PD-L1的durvalumab&#xff09;已成为晚期HCC的一线治疗方…...

2025年保姆级教程:Powershell命令补全、主题美化、文件夹美化及Git扩展

文章目录 1. 美化 Powershell 缘起2. 安装 oh-my-posh 和 posh-git3. 安装文件夹美化主题【可选】 1. 美化 Powershell 缘起 背景&#xff1a;用了 N 年的 Windows 系统突然觉得命令行实在太难用了&#xff0c;没有补全功能、界面也不美观。所以&#xff0c;我决定改变它。但是…...

LeetCode-链表-合并两个有序链表

LeetCode-链表-合并两个有序链表 ✏️ 关于专栏&#xff1a;专栏用于记录 prepare for the coding test。 文章目录 LeetCode-链表-合并两个有序链表&#x1f4dd; 合并两个有序链表&#x1f3af;题目描述&#x1f50d; 输入输出示例&#x1f9e9;题目提示&#x1f9ea;AC递归&…...

SpringBoot3+Vue3(2)-前端基本页面配置-登录界面编写-Axios请求封装-后端跨越请求错误

前端&#xff1a; 清理文件 main.js 刷新后页面上什么都没有了 App.vue就留这 1.基本页面配置 新建Vue组件 单页面&#xff0c;考路由才操作。 1.前端根目录下安装路由 2.创建路由文件夹 main.js中添加路由配置 App.vue 添加上路由 welcomeView.vue 浏览器刷新&…...

Android Framework学习八:SystemServer及startService原理

文章目录 SystemServer、SystemServiceManger、SystemService、serviceManager的关系SystemServer进程的执行包含的ServiceSystemServer启动服务的流程startService Framework学习系列文章 SystemServer、SystemServiceManger、SystemService、serviceManager的关系 管理机制&a…...

远程访问家里的路由器:异地访问内网设备或指定端口网址

在一些情况下&#xff0c;我们可能需要远程访问家里的路由器&#xff0c;以便进行设置调整或查看网络状态等&#xff0c;我们看看怎么操作&#xff1f; 1.开启远程访问 在路由本地电脑或手机&#xff0c;登录浏览器访问路由管理后台&#xff0c;并设置开启WEB远程访问。 2.内…...

大语言模型 17 - MCP Model Context Protocol 介绍对比分析 基本环境配置

MCP 基本介绍 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一种开放协议&#xff0c;旨在标准化应用程序向大型语言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…...

python生成requirements.txt文件

方法一&#xff1a;只生成项目所用到的python包(常用) 首先安装pipreqs pip install pipreqs 然后进入到你所在的项目根目录&#xff0c;运行以下命令&#xff1a; pipreqs ./ --encodingutf-8 方法二&#xff1a;把本地所有安装包写入文件 pip freeze > requirements.txt …...

如何在PyCharm2025中设置conda的多个Python版本

前言 体验的最新版本的PyCharm(Community)2025.1.1&#xff0c;发现和以前的版本有所不同。特别是使用Anaconda中的多个版本的Python的时候。 关于基于Anaconda中多个Python版本的使用&#xff0c;以及对应的Pycharm&#xff08;2023版&#xff09;的使用&#xff0c;可以参考…...