Mobile Development 12 min read

Android App Package Size Optimization through Built‑in Image Networkization

This article explores Android package size reduction by converting built‑in images to network‑loaded resources, detailing resource compression, code obfuscation, AAB splitting, drawable hooking via reflection or bytecode, download strategies with Fresco, caching, and safe removal of unused assets.

58 Tech
58 Tech
58 Tech
Android App Package Size Optimization through Built‑in Image Networkization

Android app package size has long been a pain point; common reduction techniques include resource compression (WebP, obfuscation), code shrinking, and AAB modularization. However, built‑in image networkization lacks solid references, prompting an investigation into intercepting Drawable loading, rendering, downloading, caching, and removal of embedded images.

Built‑in Image Networkization Analysis – The approach intercepts the Android Resources#getDrawable and Resources#loadDrawable processes, replacing static drawables with a custom Drawable that first shows a placeholder and then loads the image from the network. Four main steps are identified: intercept image loading, render images, download & cache, and delete original assets.

Intercepting Image Loading – Two methods are considered: bytecode modification of Activity#getResources() and reflection to replace the Resources instance. The author chose reflection for simplicity. Hooking can be done by overriding getDrawable (public) and attempting to hook the protected loadDrawable , which proved difficult.

Static to Dynamic Redirection – By implementing LayoutInflater$Factory2 , XML view creation is intercepted, allowing custom base views (e.g., SkinTextView ) to replace the original drawable via Resources#getDrawable . Sample code demonstrates extracting background/src attributes and applying them to the view:

class SkinTextView extends TextView {
    public SkinTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setSkin(this, attrs);
    }
    private static final int[] ATTR_ARRAY = {16842964/*android.R.attr.background*/, android.R.attr.src};
    public static void setSkin(View view, AttributeSet attrs) {
        Resources resources = view.getContext().getResources();
        TypedArray ta = view.getContext().obtainStyledAttributes(attrs, ATTR_ARRAY);
        for (int i = 0; i < ATTR_ARRAY.length; i++) {
            int drawableId = ta.getResourceId(i, 0);
            if (drawableId == 0) continue;
            Drawable drawable = resources.getDrawable(drawableId, view.getContext().getTheme());
            if (ATTR_ARRAY[i] == 16842964) {
                view.setBackground(drawable);
            } else if (ATTR_ARRAY[i] == android.R.attr.src && view instanceof ImageView) {
                ((ImageView) view).setImageDrawable(drawable);
            }
        }
        ta.recycle();
    }
}

Custom views that do not inherit from standard Android widgets pose a challenge; three solutions were explored: bytecode rewriting of inheritance hierarchies, hooking LayoutInflater for custom view creation, and ultimately abandoning custom‑view handling in favor of XML attribute filtering.

Image Download Strategies – Two options were evaluated: packaging all images in an auxiliary APK plugin versus direct network download using Fresco. The latter was chosen for ease of implementation. Fresco’s three‑layer architecture (RootDrawable, Controller, DataSource) is leveraged for downloading, caching, and memory management.

Image Rendering – Drawable callbacks ( Drawable.Callback#invalidateDrawable ) are used to swap placeholder and network images. A LayerDrawable ensures the same Drawable instance is updated, simplifying synchronous placeholder display and asynchronous network updates.

Built‑in Image Deletion – Direct removal of resources causes build‑time errors; instead, a 1‑pixel placeholder replaces unwanted images. Various deletion schemes were compared, and the chosen method replaces matching Flat binary files (PNG/WebP/JPG) with a single‑pixel image after the AAPT merge step, allowing size estimation and server‑side upload.

Conclusion – The built‑in image networkization concept is feasible, with the main difficulty being the interception of Resources#loadDrawable for XML‑defined drawables. While custom view handling remains unresolved, bytecode‑level inheritance rewriting offers a promising direction for future research.

Mobile developmentAndroidApp Size OptimizationDrawable HookingFrescoImage Networkization
58 Tech
Written by

58 Tech

Official tech channel of 58, a platform for tech innovation, sharing, and communication.

0 followers
Reader feedback

How this landed with the community

login 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.