Этап 1: единый OkHttpClient (переиспользование пула/кэша) — ч.1
Раньше NetworkFactory создавал новый OkHttpClient на КАЖДЫЙ запрос (~32 места): TLS-handshake + новый пул/диспатчер на запрос + утечка ExecutorService. Теперь: один базовый клиент (общий ConnectionPool/Dispatcher + диск-кэш 20МБ), авторизованные варианты через base.newBuilder() (шарят пул/кэш), кэшируются по кредам+таймаутам. Публичный API НЕ менялся — 32 вызова не тронуты. Init диск-кэша в F7MobileApp.onCreate. +3 unit-теста (переиспользование клиента, общий пул). 401→interceptor НЕ делал (меняет семантику исключений — отдельный шаг). Проверено: 15 тестов зелёные + assembleRelease BUILD SUCCESSFUL, APK 53 МБ.
This commit is contained in:
@@ -1,9 +1,37 @@
|
||||
package ru.forbion.f7cloud.core.network
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.File
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Фабрика HTTP-клиентов. Раньше создавала НОВЫЙ OkHttpClient на КАЖДЫЙ запрос (~32 места) →
|
||||
* TLS-handshake и новый пул/диспатчер на запрос, утечка ExecutorService. Теперь:
|
||||
* - один базовый клиент (общий ConnectionPool + Dispatcher + диск-кэш);
|
||||
* - авторизованные варианты — через `base.newBuilder()` (шарят пул/диспатчер/кэш);
|
||||
* - экземпляры кэшируются по кредам+таймаутам → переиспользуются, соединения живут.
|
||||
*
|
||||
* Публичный API НЕ менялся — все вызывающие (`newAuthedClient*`) работают как прежде.
|
||||
* NB: 401→UnauthorizedException пока проверяется в репозиториях вручную; централизация
|
||||
* через interceptor — отдельный шаг (меняет семантику исключений).
|
||||
*/
|
||||
object NetworkFactory {
|
||||
@Volatile
|
||||
private var base: OkHttpClient = OkHttpClient()
|
||||
private val clients = ConcurrentHashMap<String, OkHttpClient>()
|
||||
|
||||
/** Вызывать один раз из Application.onCreate — добавляет диск-кэш (нужен cacheDir). */
|
||||
fun init(context: Context) {
|
||||
val cacheDir = File(context.applicationContext.cacheDir, "http-cache")
|
||||
base = OkHttpClient.Builder()
|
||||
.cache(Cache(cacheDir, 20L * 1024 * 1024)) // 20 МБ
|
||||
.build()
|
||||
clients.clear() // пересобрать производные клиенты уже с кэшем
|
||||
}
|
||||
|
||||
fun newAuthedClient(
|
||||
username: String,
|
||||
appPassword: String,
|
||||
@@ -11,13 +39,16 @@ object NetworkFactory {
|
||||
callTimeoutSeconds: Long = 30,
|
||||
readTimeoutSeconds: Long = 30,
|
||||
): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.callTimeout(callTimeoutSeconds, TimeUnit.SECONDS)
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(readTimeoutSeconds, TimeUnit.SECONDS)
|
||||
.applyUnsafeSslIfNeeded(trustAllCerts)
|
||||
.addInterceptor(BasicAuthInterceptor(username, appPassword))
|
||||
.build()
|
||||
val key = "$username|$appPassword|$trustAllCerts|$callTimeoutSeconds|$readTimeoutSeconds"
|
||||
return clients.getOrPut(key) {
|
||||
base.newBuilder() // общий пул/диспатчер/кэш базового клиента
|
||||
.callTimeout(callTimeoutSeconds, TimeUnit.SECONDS)
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(readTimeoutSeconds, TimeUnit.SECONDS)
|
||||
.applyUnsafeSslIfNeeded(trustAllCerts)
|
||||
.addInterceptor(BasicAuthInterceptor(username, appPassword))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
/** Collabora / richdocuments: cold start and WOPI can be slow on mobile networks. */
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.forbion.f7cloud.core.network
|
||||
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Test
|
||||
|
||||
/** Фиксирует переиспользование клиента и общий пул соединений (было: новый клиент на запрос). */
|
||||
class NetworkFactoryTest {
|
||||
|
||||
@Test fun `одинаковые креды дают ТОТ ЖЕ экземпляр клиента`() {
|
||||
val a = NetworkFactory.newAuthedClient("user", "pass", false)
|
||||
val b = NetworkFactory.newAuthedClient("user", "pass", false)
|
||||
assertSame("клиент должен переиспользоваться, а не создаваться заново", a, b)
|
||||
}
|
||||
|
||||
@Test fun `клиенты шарят общий ConnectionPool и Dispatcher`() {
|
||||
val a = NetworkFactory.newAuthedClient("u1", "p1", false)
|
||||
val b = NetworkFactory.newAuthedClient("u2", "p2", false) // другие креды
|
||||
assertSame(a.connectionPool, b.connectionPool)
|
||||
assertSame(a.dispatcher, b.dispatcher)
|
||||
}
|
||||
|
||||
@Test fun `разные таймауты — разные клиенты, но общий пул`() {
|
||||
val normal = NetworkFactory.newAuthedClient("u", "p", false)
|
||||
val office = NetworkFactory.newAuthedClientForOffice("u", "p", false)
|
||||
assertNotSame(normal, office)
|
||||
assertSame(normal.connectionPool, office.connectionPool)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user