Design and Implementation of a Non‑Intrusive Page Network Request Preloading Framework for Android
The article presents a non‑intrusive Android framework that preloads page network requests in parallel with page initialization, describing its three‑part architecture, routing and network interceptors, code implementation, and performance gains that can reduce overall page load time by up to 100 ms.
When a page is opened, its loading process can be abstracted as three sequential steps: page initialization (A), network request (B), and view refresh (C), giving a total time T = A + B + C. Because view refresh time is hard to optimise, the article proposes parallelising page initialization and the network request so that the request can be sent earlier, reducing the overall loading time.
The proposed solution consists of three functional modules: a route interceptor, a network‑request interceptor, and a preload data management centre. These components work together to trigger the network request before the page starts and to deliver the preloaded data to the page without modifying existing business logic.
To enable preloading, a page registers the network requests it needs via its routing URL. The framework then automatically triggers those requests before the page is launched. An example registration is shown below:
@Route(value = "homepage/", desc = "预加载请求")
public static List<HttpCall> getCallList(@Param(value = "preload_bundle", desc = "source_global") Bundle bundle) {
ArrayList<HttpCall> list = new ArrayList<>();
HttpCall firstDataCall = APIService.createService(NetApiService.HostMode.class).getfirstDataCall();
list.add(firstDataCall);
return list;
}The route interceptor extracts the registered requests, adds a custom header ("is_preload" = 1) to mark them as preload requests, and enqueues them. The core logic of the interceptor is:
@Override
public boolean intercept(Context context, RouteRequest request) {
List
httpCallList = (List) Router.create(request.mUri)
.with(PRELOAD_BUNDLE, request.getBundle())
.call();
if (httpCallList == null || httpCallList.isEmpty()) {
return false;
}
for (HttpCall httpCall : httpCallList) {
PreloadManager.addPreloadHeader(httpCall);
httpCall.enqueue(new PreloadManager.HttpCallBackImpl());
}
return false;
}Adding the preload header is performed by reflecting into the OkHttp request object to insert the "is_preload" field:
public static void addPreloadHeader(HttpCall httpCall) {
Request httpRequest = httpCall.request();
Headers headers = httpRequest.headers().newBuilder().add("is_preload", 1).build();
Field headerField = null;
try {
headerField = Request.class.getDeclaredField("headers");
Field modifiersField = Field.class.getDeclaredField("accessFlags");
modifiersField.setAccessible(true);
modifiersField.setInt(headerField, headerField.getModifiers() & ~Modifier.FINAL);
headerField.setAccessible(true);
headerField.set(httpRequest, headers);
modifiersField.setInt(headerField, headerField.getModifiers() | Modifier.FINAL);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}The network‑layer interceptor, built on Retrofit + OkHttp, distinguishes preload requests by the "is_preload" header. For preload requests it returns an empty‑body response after caching the data; for normal requests it either serves cached data (if the request was preloaded) or proceeds with a regular network call.
public class PreloadInterceptor implements Interceptor {
public static final String PRELOAD_HEADER = "isPreload";
public static final String PRELOAD_ON = "1";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (PRELOAD_ON.equals(request.headers().get(PRELOAD_HEADER))) {
return getNewResponse(result); // empty body, data cached elsewhere
} else {
if (PreloadResultManager.getInstance().hasRequest(request.url().url())) {
return PreloadResultManager.getInstance().getResult();
} else {
return chain.proceed(request);
}
}
}
}The preload data management centre stores the data returned by early requests and synchronises it with the page’s normal request. If the data arrives before the page request, it is cached and immediately returned when the page asks; if it arrives later, the page registers a listener that receives the data once available.
Performance evaluation shows that the original load time T = A + B + C becomes T = max(A, B) + C after optimisation, saving min(A, B). In practice the framework reduces page load time by roughly 100 ms on average, with larger gains when network latency dominates. The solution has been extracted into an independent SDK for reuse in other Android applications.
Beike Product & Technology
As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.