Mobile Development 16 min read

How Vivo Game Center Boosted Weak‑Network Performance with QUIC and Cronet

This article details how the Vivo Game Center team tackled slow page loads and resource failures in weak‑network environments by integrating the QUIC‑enabled Cronet library into OkHttp, defining precise network states, using custom interceptors, and conducting A/B tests that reduced failure rates by 40% and cut request latency by 7%.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
How Vivo Game Center Boosted Weak‑Network Performance with QUIC and Cronet

Weak Network Optimization Background

The practice focuses on improving the Game Center app’s performance in weak‑network scenarios where page loading is slow and resources often fail to load. By adopting the QUIC‑enabled Cronet network library, connection establishment and data transmission speeds are increased, resulting in a 40% drop in page‑load failure rate, a 7% reduction in request latency, and noticeable image‑loading speed gains across all network conditions.

How to Define Network State

In mobile applications, network state encompasses connection type, speed, latency, and packet‑loss rate. The Game Center defines several dimensions—Wi‑Fi signal strength, cellular signal, ping to Vivo or Baidu domains, recent request failure rate, upstream/downstream bandwidth, and average request latency—to assign a numeric network‑state value used for analytics and optimization.

Weak network: Severe degradation that visibly harms user experience.

Suspected weak network: Unstable or degrading conditions that have not yet reached the severe level.

Game Center Integration of the QUIC Protocol

QUIC Overview

QUIC (Quick UDP Internet Connections) is a UDP‑based protocol introduced by Google in 2013. It aims to make web and app loading faster and more secure by establishing multiple streams per connection, reducing handshake latency, and automatically adjusting bandwidth to avoid congestion.

Application Scenarios

Lightweight resource transfer: Faster delivery of small files such as images and icons.

Video playback enhancement: Quicker first‑frame rendering and fewer interruptions.

High‑frequency interactive requests: Improved response times for login, payment, and other frequent interactions.

Stability under poor networks: Maintains data flow despite high latency or packet loss.

Large‑scale concurrent access: Better connection handling for many simultaneous users.

Implementation Details

Integrating Cronet with OkHttp

Cronet natively supports QUIC, while OkHttp does not. To keep existing OkHttp customizations, a Cronet interceptor is added to the OkHttp chain. The interceptor performs three main steps:

Convert an OkHttp Request into a Cronet Request.

Send the Cronet request and manage its lifecycle.

Convert the Cronet Response back into an OkHttp Response.

The custom interceptor is placed at the end of the OkHttp interceptor chain so that caching, logging, and authentication run before QUIC handling.

// 1. Create cache directory
val cachePath = File(AppContext.getContext().cacheDir, CRONET_CACHE_PATH)
if (!cachePath.isDirectory) {
    cachePath.mkdirs()
    VLog.d(TAG, "no cronet cache dir, mkdirs")
}

// 2. Build CronetEngine
var builder = CronetEngine.Builder(AppContext.getContext())
try {
    builder = builder
        .setStoragePath(cachePath.absolutePath) // set cache path
        .enableBrotli(false) // Brotli disabled for now
        .enableQuic(true) // enable QUIC
        .enableHttp2(true) // enable HTTP/2
        .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, SIZE_1_MB.toLong())
    // Add QUIC hints for configured hosts
    NetworkManager.getInstance().quicHintHosts?.forEach {
        builder = builder.addQuicHint(it, 443, 443)
    }
    cronetEngine = builder.build()
} catch (e: Throwable) {
    VLog.e(TAG, "init cronet engine fail", e)
    cronetEngine = null
}

// 3. Build OkHttpClient with Cronet integration
val netClientBuilder = defOkhttpClient.newBuilder()
    .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
    .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
    .addInterceptor(CronetInterceptor.Builder(cronetEngine).build())

The CronetInterceptor decides per‑request whether to use Cronet based on a configurable domain whitelist.

public final class CronetInterceptor implements Interceptor {
    private static final String TAG = "CronetInterceptor";
    private final RequestResponseConverter mConverter;

    private CronetInterceptor(RequestResponseConverter converter) {
        this.mConverter = checkNotNull(converter);
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        if (chain.call().isCanceled()) {
            throw new IOException("Request call canceled");
        }
        Request request = chain.request();
        if (OkHttpClientHelper.INSTANCE.isNeedUseCronet(request.url())) {
            VLog.d(TAG, "use Cronet request:" + request.url());
            return proceedWithCronet(chain);
        } else {
            VLog.d(TAG, "don't use Cronet request:" + request.url());
            return proceedDefault(chain);
        }
    }

    private Response proceedWithCronet(Chain chain) throws IOException {
        RequestResponseConverter.CronetRequestAndOkHttpResponse requestAndOkHttpResponse =
                mConverter.convert(chain.request(), chain.readTimeoutMillis(), chain.writeTimeoutMillis());
        try {
            requestAndOkHttpResponse.getRequest().start();
            return toInterceptorResponse(requestAndOkHttpResponse.getResponse(), chain.call());
        } catch (Throwable e) {
            VLog.e(TAG, "proceedWithCronet exception:", e);
            throw e;
        }
    }

    private Response proceedDefault(Chain chain) throws IOException {
        try {
            Request request = chain.request();
            VLog.d(TAG, "intercept " + request.method() + ", " + request.tag());
            request = RequestHelper.handleRequest(request);
            Response response = chain.proceed(request);
            int retryNum = 0;
            while ((response == null || !response.isSuccessful()) && retryNum < DEFAULT_RETRY_COUNT) {
                retryNum++;
                if (response != null && response.body() != null) {
                    response.body().close();
                }
                response = chain.proceed(request);
            }
            return response;
        } catch (Throwable e) {
            if (e instanceof IOException) {
                throw e;
            } else {
                throw new IOException(e);
            }
        }
    }

    private Response toInterceptorResponse(Response response, Call call) {
        checkNotNull(response.body());
        return response.newBuilder()
                .body(new CronetInterceptorResponseBody(response.body(), call))
                .build();
    }
}

After receiving the Cronet response, it is transformed back into an OkHttp response so the rest of the app can continue using the familiar OkHttp API.

Testing Configuration

Three aspects are configured for the A/B experiment:

Domain support: Domains that should use QUIC must be listed and verified with the carrier for GQUIC/IQUIC compatibility.

Speed‑limit parameters: Different network‑state thresholds are set (see the image below).

Test tools: A custom bash script simulates latency, bandwidth, and packet‑loss on the device.

#!/bin/bash
# Set latency (ms), bandwidth (kbit/mbit), and packet loss (%).
# Example: 300ms latency, 100kbit bandwidth, 50% loss
# bash NetworkSimulation.sh 300ms 100kbit 50%
# Example: 100ms latency, 1mbit bandwidth, 0% loss
# bash NetworkSimulation.sh 100ms 1mbit 0%

adb root
adb shell setenforce 0
adb shell tc qdisc del dev ifb0 root
adb shell ip link set dev ifb0 down
adb shell tc qdisc del dev wlan0 ingress
adb shell tc qdisc del dev wlan0 root

if [ $# -eq 1 ]; then
    echo "setup cleared"
elif [ $# -eq 3 ]; then
    latency=$1
    bandwidth=$2
    packetloss=$3
    adb shell ip link set dev wlan0 up
    adb shell ip link set dev ifb0 up
    adb shell tc qdisc del dev wlan0 clsact
    adb shell tc qdisc add dev wlan0 handle ffff: ingress
    adb shell tc filter add dev wlan0 parent ffff: protocol all u32 match u32 00 action mirred egress redirect dev ifb0
    # Throttle upload
    adb shell tc qdisc add dev wlan0 root handle 1: htb default11
    adb shell tc class add dev wlan0 parent 1: classid 1:1 htb rate "$bandwidth"
    adb shell tc class add dev wlan0 parent 1:1 classid 1:11 htb rate "$bandwidth"
    adb shell tc qdisc add dev wlan0 parent 1:11 handle 10: netem delay "$latency" loss "$packetloss"
    # Throttle download
    adb shell tc qdisc add dev ifb0 root handle 1: htb default10
    adb shell tc class add dev ifb0 parent 1: classid 1:1 htb rate "$bandwidth"
    adb shell tc class add dev ifb0 parent 1:1 classid 1:10 htb rate "$bandwidth"
else
    echo "Invalid parameters"
fi

Optimization Results

Page‑load failure rate: Decreased by 40% overall.

Request latency: Average page request time reduced by 7%.

Image loading in normal networks: Speed improved by 38%.

Image loading in weak networks: Speed improved by 30%.

Image loading in extremely poor networks: Speed improved by up to 58%.

These results demonstrate that integrating QUIC via Cronet and fine‑grained network‑state detection can substantially enhance user experience even under challenging connectivity conditions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidCronetnetwork optimizationQUICOkHttp
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.