Mobile Development 28 min read

Android Plugin Architecture: Class Loading, Resource Injection, Component Communication, and Popular Frameworks

This article explains how Android installs APKs, introduces the concepts of plugins and pluginization, details class‑loader and resource‑injection mechanisms, describes four‑component communication strategies, compares several hooking techniques, and reviews major open‑source plugin frameworks such as Atlas, RePlugin, VirtualApp and Shadow.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Android Plugin Architecture: Class Loading, Resource Injection, Component Communication, and Popular Frameworks

Preface

In Android, applications are packaged as APK files and must be installed before they can be used. The installation process is actually very simple: copy the APK to a system directory and extract the native libraries.

Common installation directories are: /system/app: system app /system/priv-app: privileged system app /data/app: user app

An APK typically contains classes.dex (bytecode), res (resources), lib (native .so files), assets (static assets) and AndroidManifest.xml (manifest).

When Android launches an app it creates a process and uses ClassLoader to load classes.dex into the process.

Plugin‑related Concepts

Plugin: essentially an APK.

Plugin Project: a project that can compile a plugin APK.

Pluginization: the process of splitting a large APK into multiple smaller APKs.

Plugin architecture includes a dedicated PluginClassLoader, PluginAssetManager, and PluginContext.

Advantages and Disadvantages of Pluginization

Advantages

Smaller APK size; download only needed modules.

Independent debugging and decoupling of modules, improving development efficiency.

Dynamic updates of plugins or patches.

Disadvantages

High refactoring cost for mature projects.

Many frameworks cannot guarantee compatibility across all Android versions.

Difference Between Pluginization and Componentization

Componentization : splits an app into modules that are compiled into a single final APK.

Pluginization : splits an app into multiple APKs; only the host APK is published, plugins are delivered on demand.

Problems to Solve When Refactoring to Pluginization

Plugin class loading

Plugin resource loading

Four‑component communication

Dynamic deployment of plugins

1. Plugin Class Loading

Understanding the parent‑delegation principle is essential. Android’s ClassLoader hierarchy follows this principle.

If a class loader receives a load request, it first delegates to its parent; only when the parent cannot load the class does the request go down to the child.

Key class loaders:

PathClassLoader
DexClassLoader
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super((String) null, (File) null, (String) null, (ClassLoader) null);
        throw new RuntimeException("Stub!");
    }
}

The only difference between the two constructors is the optimizedDirectory parameter. PathClassLoader loads only installed APKs, while DexClassLoader can load APKs or dex files from arbitrary paths.

private void loadClass() {
    init();
    try {
        // Load class from optimized dex file
        clazz = pluginClassLoader.loadClass("com.iflytek.test.HelloWorld");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

private void init() {
    extractPlugin();
    pluginPath = File(filesDir.absolutePath, "plugin.apk").absolutePath;
    nativeLibDir = File(filesDir, "pluginlib").absolutePath;
    dexOutPath = File(filesDir, "dexout").absolutePath;
    pluginClassLoader = DexClassLoader(pluginPath, dexOutPath, nativeLibDir, this::class.java.classLoader);
}

private void extractPlugin() {
    var inputStream = assets.open("plugin.apk");
    File(filesDir.absolutePath, "plugin.apk").writeBytes(inputStream.readBytes());
}

After obtaining the plugin ClassLoader, reflection can be used to invoke methods in the plugin.

val loadClass = pluginClassLoader.loadClass(activityName)
loadClass.getMethod("hello", null).invoke(loadClass)

2. Plugin Resource Loading

Resources are packaged into the APK and referenced via the generated R class. To load resources from an uninstalled plugin APK, two key APIs are used:

PackageManager#getPackageArchiveInfo
PackageManager#getResourcesForApplication
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageArchiveInfo(pluginApkPath,
    PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA |
    PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS |
    PackageManager.GET_SIGNATURES);
pi.applicationInfo.sourceDir = pluginApkPath;
pi.applicationInfo.publicSourceDir = pluginApkPath;
Resources injectResources = null;
try {
    injectResources = pm.getResourcesForApplication(pi.applicationInfo);
} catch (PackageManager.NameNotFoundException e) { }

A custom PluginResources class merges host and plugin resources.

public class PluginResources extends Resources {
    private Resources hostResources;
    private Resources injectResources;
    public PluginResources(Resources hostResources, Resources injectResources) {
        super(injectResources.getAssets(), injectResources.getDisplayMetrics(), injectResources.getConfiguration());
        this.hostResources = hostResources;
        this.injectResources = injectResources;
    }
    @Override
    public String getString(int id, Object... formatArgs) throws NotFoundException {
        try {
            return injectResources.getString(id, formatArgs);
        } catch (NotFoundException e) {
            return hostResources.getString(id, formatArgs);
        }
    }
}

3. Four‑Component Communication

Understanding Activity launch processes (root vs. normal) is necessary. The AMS checks the manifest before launching an Activity.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.music.anna.pluginactivity">
    <application>
        <activity android:name=".StubActivity"/>
    </application>
</manifest>

Three common hooking methods are presented:

Method 1: Hook Instrumentation

public class InstrumentationProxy extends Instrumentation {
    private Instrumentation mInstrumentation;
    private PackageManager mPackageManager;
    public InstrumentationProxy(Instrumentation instrumentation, PackageManager pm) {
        mInstrumentation = instrumentation;
        mPackageManager = pm;
    }
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        List<ResolveInfo> infos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (infos == null || infos.size() == 0) {
            intent.putExtra(HookHelper.TARGET_INTENT_NAME, intent.getComponent().getClassName());
            intent.setClassName(who, "com.music.anna.pluginactivity.StubActivity");
        }
        try {
            Method execMethod = Instrumentation.class.getDeclaredMethod("execStartActivity",
                Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
            return (ActivityResult) execMethod.invoke(mInstrumentation, who, contextThread, token,
                target, intent, requestCode, options);
        } catch (Exception e) { e.printStackTrace(); }
        return null;
    }
    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        String intentName = intent.getStringExtra(HookHelper.TARGET_INTENT_NAME);
        if (!TextUtils.isEmpty(intentName)) {
            return super.newActivity(cl, intentName, intent);
        }
        return super.newActivity(cl, className, intent);
    }
}

Method 2: Hook IActivityManager.startActivity and ActivityThread.mH.mCallback

Hook the AMS binder to replace the Intent with a stub, then restore the real Intent in the Handler callback.

public class HCallback implements Handler.Callback {
    public static final int LAUNCH_ACTIVITY = 100;
    Handler mHandler;
    public HCallback(Handler handler) { mHandler = handler; }
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == LAUNCH_ACTIVITY) {
            Object r = msg.obj;
            try {
                Intent intent = (Intent) FieldUtil.getField(r.getClass(), r, "intent");
                Intent target = intent.getParcelableExtra(HookHelper.TARGET_INTENT);
                intent.setComponent(target.getComponent());
            } catch (Exception e) { e.printStackTrace(); }
        }
        mHandler.handleMessage(msg);
        return true;
    }
}

Method 3: Hook ClassLoader (RePlugin approach)

RePlugin hooks two class loaders: RePluginClassLoader (extends PathClassLoader) for host classes and PluginDexClassLoader (extends DexClassLoader) for plugin classes.

@Override
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    Class<?> pc = null;
    try {
        pc = super.loadClass(className, resolve);
        if (pc != null) return pc;
    } catch (ClassNotFoundException e) {
        if (PluginDexClassLoaderPatch.need2LoadFromHost(className)) {
            try { return loadClassFromHost(className, resolve); } catch (ClassNotFoundException ignored) {}
        }
    }
    if (RePlugin.getConfig().isUseHostClassIfNotFound()) {
        try { return loadClassFromHost(className, resolve); } catch (ClassNotFoundException ignored) {}
    }
    if (cnfException != null) throw cnfException;
    return null;
}

4. Runtime Container Technique (Proxy Activity)

A host APK can pre‑declare a placeholder ContainerActivity. At runtime it loads the plugin APK, creates a PluginClassLoader and PluginResources, and forwards lifecycle callbacks to the actual plugin Activity.

public class ContainerActivity extends Activity {
    PluginActivity pluginActivity;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        String pluginActivityName = getIntent().getString("pluginActivityName", "");
        pluginActivity = PluginLoader.loadActivity(pluginActivityName, this);
        if (pluginActivity == null) { super.onCreate(savedInstanceState); return; }
        pluginActivity.onCreate();
    }
    @Override
    protected void onResume() { if (pluginActivity == null) { super.onResume(); return; } pluginActivity.onResume(); }
    @Override
    protected void onPause() { if (pluginActivity == null) { super.onPause(); return; } pluginActivity.onPause(); }
    // ... other lifecycle methods
}

Popular Open‑Source Plugin Frameworks

Alibaba Atlas

Atlas provides component isolation, on‑demand bundle loading, remote bundles, and an interpretation‑first execution strategy.

Atlas architecture
Atlas architecture

Qihoo 360 RePlugin

RePlugin is a stable, full‑featured, placeholder‑based plugin solution supporting all four Android components, cross‑process communication, resource isolation, and works from API 9 onward.

asLody VirtualApp

VirtualApp uses a ContentProvider as the host execution environment and hooks the AMS to achieve plugin isolation.

Tencent Shadow

Shadow supports components, fragments, data binding, cross‑process services, custom themes, native library loading, and segmented plugin loading.

Choosing a plugin framework depends on project requirements and the specific features each framework offers.

Conclusion

This article covered the core techniques of Android pluginization: class loading, resource injection, component communication, various hooking methods, runtime container technology, and a comparison of major open‑source frameworks.

Understanding these mechanisms helps developers explore the inner workings of plugin systems used by large companies.

References

Android Pluginization Principle (Part 1) – Activity Pluginization

Atlas Official Documentation

Deep Dive into Android Pluginization

RePlugin Principle Introduction

Analysis and Implementation of Pluginization

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.

AndroidpluginclassloaderframeworksComponentCommunicationResourceInjection
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

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.