Mobile Development 17 min read

How RePlugin Manages Processes and Loads Plugins: A Deep Source Code Walkthrough

This article dissects RePlugin's process management and plugin loading mechanisms from a source‑code perspective, detailing the startup sequence, framework configuration, IPC initialization, PMF setup, plugin installation flow, and how plugins are registered and synchronized across processes.

Qizhuo Club
Qizhuo Club
Qizhuo Club
How RePlugin Manages Processes and Loads Plugins: A Deep Source Code Walkthrough

This article analyzes RePlugin's process management mechanism from the source‑code level, focusing on the process startup and plugin loading procedures.

Process Startup

The system first calls RePluginApplication.attachBaseContext, which then invokes RePlugin.App.attachBaseContext.

public static void attachBaseContext(Application app, RePluginConfig config) {
    if (sAttached) {
        if (LogDebug.LOG) {
            LogDebug.d(TAG, "attachBaseContext: Already called");
        }
        return;
    }
    RePluginInternal.init(app);
    sConfig = config;
    sConfig.initDefaults(app);
    IPC.init(app);
    // additional initialization steps omitted for brevity
    sAttached = true;
}

The method performs several key steps:

Initializes the internal framework via RePluginInternal.init(app).

Sets up the framework configuration.

Initializes the IPC process communication class, configuring process name, PID, package name, persistent‑process name, UI‑process flag, and persistent‑process flag.

Initializes the Plugin Manager Framework (PMF) and calls PMF.callAttach().

Key Initialization Methods

RePluginInternal.init() simply stores the application context for later use:

static void init(Application app) {
    sAppContext = app;
}

Framework configuration assigns the provided RePluginConfig to sConfig and calls sConfig.initDefaults(app).

IPC.init() sets up inter‑process communication attributes:

public static void init(Context context) {
    sCurrentProcess = SysUtils.getCurrentProcessName();
    sCurrentPid = Process.myPid();
    sPackageName = context.getApplicationInfo().packageName;
    if (HostConfigHelper.PERSISTENT_ENABLE) {
        String cppn = HostConfigHelper.PERSISTENT_NAME;
        if (!TextUtils.isEmpty(cppn)) {
            sPersistentProcessName = cppn.startsWith(":") ? sPackageName + cppn : cppn;
        }
    } else {
        sPersistentProcessName = sPackageName;
    }
    sIsUIProcess = sCurrentProcess.equals(sPackageName);
    sIsPersistentProcess = sCurrentProcess.equals(sPersistentProcessName);
}

PMF.init() prepares the plugin manager and patches the host class loader:

public static final void init(Application application) {
    setApplicationContext(application);
    PluginManager.init(application);
    sPluginMgr = new PmBase(application);
    sPluginMgr.init();
    Factory.sPluginManager = PMF.getLocal();
    Factory2.sPLProxy = PMF.getInternal();
    PatchClassLoaderUtils.patch(application);
}

PMF.callAttach() loads plugins into the process:

final void callAttach() {
    mClassLoader = PmBase.class.getClassLoader();
    for (Plugin p : mPlugins.values()) {
        p.attach(mContext, mClassLoader, mLocal);
    }
    if (PluginManager.isPluginProcess()) {
        Plugin p = mPlugins.get(mDefaultPluginName);
        if (p != null) {
            boolean rc = p.load(Plugin.LOAD_APP, true);
            if (rc) {
                mDefaultPlugin = p;
                mClient.init(p);
            }
        }
    }
}

Plugin Manager Initialization

PluginManager.init(Context context)

sets the current process UID and plugin‑process index, applying specific rules for UI, persistent, loader, and custom processes.

public void init(Context context) {
    // initialize sUid and sPluginProcessIndex based on process type
    // UI process -> -1, Persistent -> -2, loader0~1 -> 0~1, p1~p3 -> -100~-98
}

PmBase Core Functions

newPluginFound() updates the plugin tables and notifies the process:

final void newPluginFound(PluginInfo info, boolean persistNeedRestart) {
    PluginTable.updatePlugin(info);
    insertNewPlugin(info);
    PluginStatusController.setStatus(info.getName(), info.getVersion(), PluginStatusController.STATUS_OK);
    // broadcast new plugin intent
}

insertNewPlugin() adds a new plugin to the internal map, handling version comparison and persistence:

final void insertNewPlugin(PluginInfo info) {
    if (RePlugin.getConfig().getCallbacks().isPluginBlocked(info)) return;
    Plugin p = mPlugins.get(info.getPackageName());
    if (p != null && p.isInitialized()) return; // already loaded
    mNeedRestart = true;
    Plugin plugin = Plugin.build(info);
    plugin.attach(mContext, mClassLoader, mLocal);
    putPluginObject(info, plugin);
}

putPluginObject() stores the plugin in the map and, if running in the persistent process, updates the plugins_up shared‑preferences cache.

private void putPluginObject(PluginInfo info, Plugin plugin) {
    if (HostConfigHelper.PERSISTENT_ENABLE && IPC.isPersistentProcess()) {
        SharedPreferences sp = Pref.getSharedPreferences("plugins_up");
        sp.edit().putInt(info.getName(), plugin.mInfo.getVersion()).apply();
    }
    mPlugins.put(info.getPackageName(), plugin);
    if (!TextUtils.isEmpty(info.getAlias())) {
        mPlugins.put(info.getAlias(), plugin);
    }
}

Plugin Installation Flow

The installation starts with RePlugin.install(String path), which validates the file and delegates to MP.pluginDownloaded(path).

public static PluginInfo install(String path) {
    // validate file
    return MP.pluginDownloaded(path);
}
MP.pluginDownloaded(String path)

handles lock files for p‑n plugins, calls the host process if needed, and returns a PluginInfo object.

PluginInfo info = PluginProcessMain.getPluginHost().pluginDownloaded(path);
if (info != null) {
    RePlugin.getConfig().getEventCallbacks().onInstallPluginSucceed(info);
}
return info;

In the persistent process, PmHostSvc.pluginDownloaded() determines the plugin type (p‑n or APK) and forwards the request to the appropriate installer.

if (fn.startsWith("p-n-") || fn.startsWith("v-plugin-") || fn.startsWith("plugin-s-") || fn.startsWith("p-m-")) {
    pi = pluginDownloadedForPn(path);
} else {
    pi = mManager.getService().install(path);
}
if (pi != null) {
    syncInstalledPluginInfo2All(pi);
}
return pi;

syncInstalledPluginInfo2All() synchronizes the new plugin info to all processes via a local broadcast.

Intent intent = new Intent(PmBase.ACTION_NEW_PLUGIN);
intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart);
intent.putExtra("obj", (Parcelable) needToSyncPi);
IPC.sendLocalBroadcast2AllSync(mContext, intent);

Process ID Mapping

RePlugin assigns the following IDs to processes:

UI process: -1

Persistent process: -2

loader0~loader1: 0~1

p1~p3: -100~-98

Understanding these IDs is essential for interpreting the plugin‑process index values used throughout the framework.

Overall, the article provides a step‑by‑step walkthrough of how RePlugin initializes its environment, manages process information, installs plugins, and keeps all processes in sync, giving developers a clear view of the inner workings of this Android plugin framework.

RePlugin process startup diagram
RePlugin process startup diagram
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.

mobile developmentAndroidRePlugin
Qizhuo Club
Written by

Qizhuo Club

360 Mobile tech channel sharing practical experience and original insights from 360 Mobile Security and other teams across Android, iOS, big data, AI, and more.

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.