<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 本性</title>
    <description>The latest articles on DEV Community by 本性 (@lnzz123).</description>
    <link>https://dev.to/lnzz123</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3506037%2Fe9480c12-a93d-48d6-8a53-6072d6508659.png</url>
      <title>DEV Community: 本性</title>
      <link>https://dev.to/lnzz123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lnzz123"/>
    <language>en</language>
    <item>
      <title>ComboLite: Android plugin framework, helping you easily build dynamic applications where "everything is pluggable."</title>
      <dc:creator>本性</dc:creator>
      <pubDate>Tue, 16 Sep 2025 17:26:11 +0000</pubDate>
      <link>https://dev.to/lnzz123/the-0-hook-android-architecture-your-team-has-been-dreaming-of-especially-for-jetpack-compose-17f8</link>
      <guid>https://dev.to/lnzz123/the-0-hook-android-architecture-your-team-has-been-dreaming-of-especially-for-jetpack-compose-17f8</guid>
      <description>&lt;h3&gt;
  
  
  A 10,000-word deep dive into ComboLite: the 100% official API, Jetpack Compose-native framework designed to fix the stability crisis in dynamic Android apps and deliver an unparalleled developer experience.
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Part 1: The Inevitable "Mid-life Crisis" of Modern Android Apps
&lt;/h2&gt;

&lt;p&gt;Every successful Android app has a story of evolving from a small, elegant tool into a feature-packed giant. In the early days, the codebase is clean, the architecture is clear, and every team member knows every corner. But as business expands, new features pile up, and the team grows, a harsh reality sets in: our app is facing an inevitable "mid-life crisis."&lt;/p&gt;

&lt;p&gt;The core of this crisis lies in a familiar yet frustrating pattern: the &lt;strong&gt;Monolithic Application&lt;/strong&gt;. When an app becomes a monolith, developers experience clear symptoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chaotic Codebase Growth&lt;/strong&gt;: The code easily exceeds millions of lines, with module boundaries blurring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponentially Increasing Maintenance Costs&lt;/strong&gt;: Tightly coupled business logic means a small change can trigger a cascade of unexpected side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Painfully Long Build Times&lt;/strong&gt;: Modifying a single line of code requires rebuilding the entire massive project, draining developers' energy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Huge Team Collaboration Friction&lt;/strong&gt;: Teams from different business lines working in the same repository frequently face code conflicts and merging nightmares.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is there an architecture that can free us from the shackles of the monolith, allowing us to regain development agility, much like building with LEGO bricks? The answer is a resounding yes: &lt;strong&gt;Plugin-based Architecture&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A monolithic Android app can be refactored into a lightweight &lt;strong&gt;"Host"&lt;/strong&gt; and a series of independently developed, dynamically loadable &lt;strong&gt;"Plugins."&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Host&lt;/strong&gt;: The foundation of the application. Its responsibilities are strictly limited, usually containing only the app's startup logic, basic common libraries, and, most importantly, the plugin lifecycle management mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin&lt;/strong&gt;: A highly cohesive functional unit that encapsulates a specific business scenario (e.g., a user center, an e-commerce module, a video player). Each plugin has its own independent code, resources, and even lifecycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key is that pluginization is not just about code organization (like Gradle Modules); it's about &lt;strong&gt;runtime dynamism&lt;/strong&gt;. Plugins can be dynamically loaded, launched, updated, and even uninstalled by the host at runtime &lt;em&gt;without&lt;/em&gt; reinstalling the entire app. This brings tangible advantages that can determine the success of your business:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Updates: Breaking Free from the App Store Release Cycle&lt;/strong&gt;
This is the most well-known and commercially valuable capability of pluginization. A critical online bug can take hours, or even days, to fix, from patching and packaging to submitting for app store review and finally reaching all users. With a plugin-based architecture, when a feature plugin has a bug, we only need to replace the faulty plugin package on the server. The host app on the user's device then pulls and dynamically loads the latest plugin at an appropriate time, making online bug fixes happen in "seconds."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;On-Demand Loading: Slimming Down Your App's Initial Install Size&lt;/strong&gt;
A bloated installation package undoubtedly raises the download barrier for new users. Pluginization offers an elegant solution: "on-demand loading." We can keep core, high-frequency features in the host to ensure an extremely lightweight initial package. Secondary or less-used features can be deployed as plugins in the cloud, downloaded and installed only when the user first tries to access them.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Faster Compilation: Significantly Boosting Daily Development Efficiency&lt;/strong&gt;
In a monolithic app, developers' days are consumed by the "compile-wait-recompile" cycle. Pluginization dramatically improves this by providing physical isolation. When a developer focuses on a specific plugin, they only need to compile that module. The host and other business plugins can be included as pre-compiled binary dependencies, reducing incremental build times from minutes to seconds.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Team Decoupling: Enabling True Parallel Development&lt;/strong&gt;
When multiple teams maintain a single monolithic app, architectural boundaries often rely on fragile conventions. Pluginization provides strong, architectural-level constraints, achieving a true "separation of concerns." Each business team can independently manage the entire lifecycle of one or more plugins, from requirements and development to testing and release.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 2: The "Side Effects" of Traditional Pluginization: A History of Black Magic
&lt;/h2&gt;

&lt;p&gt;Once, the field of Android plugin technology flourished with frameworks like DroidPlugin, VirtualAPK, and RePlugin. In that era of explosive mobile internet growth, they creatively endowed Android apps with dynamic capabilities. They were the "dragon-slaying" techniques of their time, solving countless business emergencies.&lt;/p&gt;

&lt;p&gt;However, as technology evolves, these once-mighty tools are showing their age and even becoming significant stability risks. This section delves into the root causes of these "side effects" and explains why we urgently need a new, future-proof plugin solution today.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The Original Sin: "Black Magic" Built on Quicksand&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Most traditional plugin frameworks operate by "deceiving" the Android system. Since the four main components (Activity, Service, BroadcastReceiver, ContentProvider) in a plugin are not statically registered in the host's &lt;code&gt;AndroidManifest.xml&lt;/code&gt;, the system is unaware of their existence. To make these "unregistered" components work, frameworks must &lt;strong&gt;hook into the system's key services&lt;/strong&gt; at runtime. This architecture, built on non-public APIs (Internal/Hidden APIs), laid the groundwork for its future instability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The "Sword of Damocles": Hooking the System&lt;/strong&gt;&lt;br&gt;
This is the root of all problems. To bypass the system's static checks, frameworks commonly use similar techniques: runtime tampering of system service behavior through Java reflection and dynamic proxies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooking &lt;code&gt;ActivityManagerService&lt;/code&gt; (AMS)&lt;/strong&gt;: When an app calls &lt;code&gt;startActivity()&lt;/code&gt;, it communicates with the system service AMS. The core operation of traditional frameworks is to replace the system-level proxy object with their own proxy. When a request to start an unregistered plugin Activity passes through this proxy, the framework secretly replaces the &lt;code&gt;Intent&lt;/code&gt; with one that starts a pre-registered, legitimate "placeholder" Activity in the host. After the placeholder starts, the framework creates the real plugin Activity instance within its lifecycle and forwards events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooking &lt;code&gt;PackageManagerService&lt;/code&gt; (PMS)&lt;/strong&gt;: Similarly, to make the system believe a plugin APK has been "installed," frameworks hook PMS to deceive the system's queries for package information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach was effective in earlier Android versions, but &lt;strong&gt;starting with Android 9.0 (Pie), Google began to strictly restrict non-SDK interfaces&lt;/strong&gt;. Any attempt to call these interfaces via reflection now leads to warnings or even app crashes. This means the lifeline of hook-based plugin frameworks is firmly in Google's hands; every major Android update could be a devastating blow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The "Chaotic Inheritance" of &lt;code&gt;ClassLoader&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
To load plugin code and enable class sharing, many frameworks crudely interfere with the system's &lt;code&gt;ClassLoader&lt;/code&gt; hierarchy, for example, by forcibly inserting a plugin's &lt;code&gt;DexPathList&lt;/code&gt; into the host's &lt;code&gt;BaseDexClassLoader&lt;/code&gt; via reflection. This breaks the &lt;code&gt;ClassLoader&lt;/code&gt;'s clear parent-delegation model, easily causing unpredictable class loading conflicts (&lt;code&gt;ClassNotFoundException&lt;/code&gt;, &lt;code&gt;NoClassDefFoundError&lt;/code&gt;) and relying on unstable internal class structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Tricks" of Resource Management&lt;/strong&gt;&lt;br&gt;
How can a plugin access its own resources while also accessing the host's? The traditional solution is typically to create a new &lt;code&gt;AssetManager&lt;/code&gt; instance via reflection, call its hidden &lt;code&gt;addAssetPath&lt;/code&gt; method to add the plugin APK's path, and then create a new &lt;code&gt;Resources&lt;/code&gt; object based on this aggregated &lt;code&gt;AssetManager&lt;/code&gt;. This process is cumbersome and prone to resource ID conflicts, a persistent headache in plugin development.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;The New Challenge: The Paradigm Shift of Jetpack Compose&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Even if we could overcome all the above compatibility issues, a new, more severe challenge has emerged: &lt;strong&gt;Jetpack Compose&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As Google's recommended declarative UI framework, Compose fundamentally changes how Android UIs are built. It no longer relies on parsing XML layout files and instantiating &lt;code&gt;View&lt;/code&gt; objects. &lt;code&gt;@Composable&lt;/code&gt; functions are managed by the Compose Runtime with their own lifecycle and state model.&lt;/p&gt;

&lt;p&gt;This is a fatal blow to traditional plugin frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooking &lt;code&gt;LayoutInflater&lt;/code&gt; is Obsolete&lt;/strong&gt;: The old way of loading plugin Views by hooking &lt;code&gt;LayoutInflater&lt;/code&gt; is useless in the Compose world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activity Lifecycle Hooking is Insufficient&lt;/strong&gt;: Compose's lifecycle management is far more granular than an Activity's. Simply proxying an Activity's &lt;code&gt;onCreate&lt;/code&gt; and &lt;code&gt;onResume&lt;/code&gt; methods is no longer sufficient to manage the state and recomposition of Composable functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Access Has Changed&lt;/strong&gt;: The way resources are accessed in Compose (e.g., &lt;code&gt;painterResource&lt;/code&gt;, &lt;code&gt;stringResource&lt;/code&gt;) differs from the traditional View system, and old resource merging solutions may not be perfectly compatible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;strong&gt;the design philosophy of traditional plugin frameworks is built on the underlying mechanisms of the Android View system. Jetpack Compose is a completely different paradigm.&lt;/strong&gt; A deep chasm exists between them.&lt;/p&gt;

&lt;p&gt;The era of pluginization that relied on hooking the system and walking the tightrope of compatibility is over. It's time to say goodbye to solutions fraught with "side effects." We need solutions that can coexist harmoniously with the modern Android ecosystem, not fight against it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 3: Goodbye, Hooks! Introducing ComboLite
&lt;/h2&gt;

&lt;p&gt;When "uncertainty" becomes a significant technical debt, we must return to first principles: &lt;strong&gt;seeking and building "certainty."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Today, we officially present &lt;strong&gt;&lt;code&gt;ComboLite&lt;/code&gt;&lt;/strong&gt;, a new plugin framework that makes "certainty" its highest design principle. It's not a patch or an improvement on existing solutions but a complete reinvention based on official Android public APIs. Its core promise is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A next-generation Android plugin framework, born for Jetpack Compose, 100% compliant with official APIs, and achieving 0 Hooks &amp;amp; 0 Reflection.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Core Philosophy: Coexisting with the Platform, Not Fighting It&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The architectural philosophy of &lt;code&gt;ComboLite&lt;/code&gt; is a clean break from all the "black magic" of the past. We believe a framework's vitality comes from its harmonious coexistence with the platform ecosystem, not a continuous, fragile confrontation.&lt;/p&gt;

&lt;p&gt;All features are rigorously built on the &lt;strong&gt;&lt;code&gt;ClassLoader&lt;/code&gt; delegation mechanism&lt;/strong&gt; and the &lt;strong&gt;Component Proxy pattern&lt;/strong&gt;, both explicitly recommended in the official Android documentation. This "return to the right path" offers unparalleled long-term value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forward Compatibility&lt;/strong&gt;: By not relying on any non-public APIs (&lt;code&gt;@hide&lt;/code&gt; / &lt;code&gt;@UnsupportedAppUsage&lt;/code&gt;), &lt;code&gt;ComboLite&lt;/code&gt; is naturally compatible with all Android versions from 7.0 (API 24) to future releases, completely eliminating the nightmare of compatibility issues caused by system upgrades.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Behavior&lt;/strong&gt;: Every action of the framework is built on public, stable APIs. Developers can clearly predict its operational logic, making the entire lifecycle controllable and predictable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;A Modern Core for New-Era Android Development&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; not only achieves ultimate stability but also fully embraces modern Android development paradigms in its internal implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A Reactive, Thread-Safe State Management Hub&lt;/strong&gt;&lt;br&gt;
The framework's core is the singleton &lt;code&gt;PluginManager&lt;/code&gt;. We've chosen a reactive architecture based on &lt;code&gt;kotlinx.coroutines.flow.StateFlow&lt;/code&gt; to manage the state of the entire plugin environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in com/combo/core/runtime/PluginManager.kt&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;PluginManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Framework initialization state machine&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_initState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InitState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NOT_INITIALIZED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;initStateFlow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;InitState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_initState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Runtime info for loaded plugins, keyed by PluginId&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_loadedPlugins&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LoadedPluginInfo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="nf"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;loadedPluginsFlow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LoadedPluginInfo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_loadedPlugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;// Instantiated plugin entry classes, keyed by PluginId&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_pluginInstances&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IPluginEntryClass&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="nf"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pluginInstancesFlow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IPluginEntryClass&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pluginInstances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design offers &lt;strong&gt;thread safety&lt;/strong&gt;, &lt;strong&gt;data consistency&lt;/strong&gt;, and &lt;strong&gt;declarative subscriptions&lt;/strong&gt;, making it perfect for building highly responsive management UIs with modern frameworks like Jetpack Compose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. An Async-First Architecture with a Robust Coroutine Scope&lt;/strong&gt;&lt;br&gt;
Plugin installation, updates, and loading are I/O-intensive operations. &lt;code&gt;PluginManager&lt;/code&gt; maintains a dedicated coroutine scope for the framework's background tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in com/combo/core/runtime/PluginManager.kt&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;managerScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoroutineScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;SupervisorJob&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SupervisorJob&lt;/code&gt; is crucial here. It ensures that if one plugin's loading task fails with an exception, it won't cancel the entire &lt;code&gt;managerScope&lt;/code&gt;, thus not affecting other ongoing or subsequent plugin operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Native, Seamless Support for Jetpack Compose&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ComboLite&lt;/code&gt;'s support for Compose is not an afterthought; it's part of its core design.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Entry Point is a &lt;code&gt;@Composable&lt;/code&gt;&lt;/strong&gt;: The UI contract between a plugin and the framework, &lt;code&gt;IPluginEntryClass.Content()&lt;/code&gt;, is itself a &lt;code&gt;@Composable&lt;/code&gt; function, making the plugin's UI definition intuitive and pure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent Merged Resources&lt;/strong&gt;: This is the key to seamless Compose support. &lt;code&gt;PluginResourcesManager&lt;/code&gt; creates a &lt;code&gt;Resources&lt;/code&gt; object that aggregates the resource paths of the host and all loaded plugins. By overriding the &lt;code&gt;getResources()&lt;/code&gt; method in the host's &lt;code&gt;BaseHostActivity&lt;/code&gt;, the entire &lt;code&gt;Activity&lt;/code&gt;'s &lt;code&gt;Context&lt;/code&gt; environment uses this merged &lt;code&gt;Resources&lt;/code&gt; object by default.&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in com/combo/core/component/activity/BaseHostActivity.kt&lt;/span&gt;
&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getResources&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Returns the merged Resources object managed by PluginResourcesManager&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resourcesManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResources&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getResources&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Therefore, when you call &lt;code&gt;stringResource(R.string.some_string)&lt;/code&gt; or &lt;code&gt;painterResource(R.drawable.some_image)&lt;/code&gt; in a plugin's Composable function, Compose's resource resolution mechanism can find it in the same merged &lt;code&gt;Resources&lt;/code&gt; object, regardless of its origin.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Production-Grade Reliability: Smart Fusing and Extensible Exception Handling&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A production-grade framework must face various runtime exceptions head-on. &lt;code&gt;ComboLite&lt;/code&gt; not only provides powerful default protection mechanisms but also empowers developers to customize advanced handling strategies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Default "Smart Fusing" Mechanism&lt;/strong&gt;&lt;br&gt;
An app getting stuck in an infinite crash loop due to a single plugin's defect is a nightmare. &lt;code&gt;ComboLite&lt;/code&gt;'s "fusing" mechanism offers an elegant solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precise Signal&lt;/strong&gt;: When a &lt;code&gt;PluginClassLoader&lt;/code&gt; can't find a class anywhere, it throws a &lt;code&gt;PluginDependencyException&lt;/code&gt;. This custom exception is the &lt;strong&gt;unique, precise signal&lt;/strong&gt; to trigger the fuse, carrying the &lt;code&gt;culpritPluginId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Sentinel &lt;code&gt;PluginCrashHandler&lt;/code&gt;&lt;/strong&gt;: The framework registers itself as the application's &lt;code&gt;Thread.defaultUncaughtExceptionHandler&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accurate Target Identification&lt;/strong&gt;: &lt;code&gt;PluginCrashHandler&lt;/code&gt; recursively traverses the exception chain, specifically looking for &lt;code&gt;PluginDependencyException&lt;/code&gt; instances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistent "Self-Healing"&lt;/strong&gt;: Once the fusing signal is identified, &lt;code&gt;PluginManager.setPluginEnabled(..., false)&lt;/code&gt; modifies the &lt;code&gt;enabled&lt;/code&gt; attribute of the corresponding plugin in &lt;code&gt;plugins.xml&lt;/code&gt;. This means that when the user restarts the app, the faulty plugin will be automatically skipped, allowing the app to start normally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Customizable Crash Handling with &lt;code&gt;IPluginCrashCallback&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
A one-size-fits-all fusing approach doesn't suit all business scenarios. &lt;code&gt;ComboLite&lt;/code&gt; addresses this with the &lt;code&gt;IPluginCrashCallback&lt;/code&gt; interface, allowing developers to completely take over the crash handling logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in com/combo/core/security/crash/IPluginCrashCallback.kt&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IPluginCrashCallback&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onClassCastException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginCrashInfo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onDependencyException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginCrashInfo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onResourceNotFoundException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginCrashInfo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onApiIncompatibleException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginCrashInfo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onOtherPluginException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginCrashInfo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers can implement this interface and register it. When &lt;code&gt;PluginCrashHandler&lt;/code&gt; catches an exception, it will &lt;strong&gt;first call&lt;/strong&gt; the developer-registered callback. Returning &lt;code&gt;true&lt;/code&gt; means the exception is handled, and the framework will not execute the default fusing logic. This transforms &lt;code&gt;ComboLite&lt;/code&gt;'s exception handling from a simple "circuit breaker" into a highly programmable, intelligent "disaster recovery control center."&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Deconstructing the Ingenious Architecture Behind "0 Hooks"
&lt;/h2&gt;

&lt;p&gt;This section will serve as &lt;code&gt;ComboLite&lt;/code&gt;'s in-depth technical white paper, diving directly into the framework's source code to deconstruct the core pillars that support its stable operation.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;High-Level Architecture&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; v2.0 adopts a concise and powerful micro-kernel design. An internal context (&lt;code&gt;PluginFrameworkContext&lt;/code&gt;) holds all the core managers, each with its own responsibilities, which are coordinated and exposed externally by a single commander, &lt;code&gt;PluginManager&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    subgraph "Host App &amp;amp; System"
        HostApp[Host App Code] -- Calls API --&amp;gt; PM(PluginManager)
        AndroidSystem[Android System] -- Interacts with --&amp;gt; HostProxies["Host Proxy Components&amp;lt;br&amp;gt;(HostActivity, HostService...)"]
    end

    subgraph "ComboLite Core Services"
        PM -- Coordinates --&amp;gt; Installer(InstallerManager)
        PM -- Coordinates --&amp;gt; ResManager(PluginResourcesManager)
        PM -- Coordinates --&amp;gt; ProxyM(ProxyManager)
        PM -- Coordinates --&amp;gt; DepManager(DependencyManager)
        PM -- Coordinates --&amp;gt; Lifecycle(PluginLifecycleManager)
        PM -- Coordinates --&amp;gt; Security(Security Managers)
    end

    subgraph "Runtime &amp;amp; Data State"
        OnDiskState["On-Disk State&amp;lt;br&amp;gt;plugins.xml, APKs"]
        InMemoryState["In-Memory State&amp;lt;br&amp;gt;Loaded Plugins, ClassLoaders, Instances"]
        ClassIndex["Global Class Index&amp;lt;br&amp;gt;Map&amp;lt;Class, PluginID&amp;gt;"]
        DepGraph["Dependency Graph&amp;lt;br&amp;gt;(Forward &amp;amp; Reverse)"]
        MergedRes["Merged Resources"]
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;PluginManager&lt;/code&gt; (Main Controller)&lt;/strong&gt;: The framework's supreme commander and sole singleton entry point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;InstallerManager&lt;/code&gt; (Installer)&lt;/strong&gt;: Manages the installation, update, and uninstallation processes, and maintains the &lt;code&gt;plugins.xml&lt;/code&gt; metadata registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;PluginLifecycleManager&lt;/code&gt; (Lifecycle Manager)&lt;/strong&gt;: Responsible for core lifecycle operations like plugin loading, instantiation, launching, and unloading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ResourceManager&lt;/code&gt; (Resource Manager)&lt;/strong&gt;: Provides a unified &lt;code&gt;Resources&lt;/code&gt; object by merging the resources of all loaded plugins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ProxyManager&lt;/code&gt; (Dispatcher)&lt;/strong&gt;: Manages the proxy components on the host side and correctly dispatches system intents to the corresponding plugin components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;DependencyManager&lt;/code&gt; (Dependency Manager)&lt;/strong&gt;: Dynamically analyzes inter-plugin dependencies, constructing a graph that provides data support for "chain restart" and cross-plugin class lookups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Security System&lt;/code&gt;&lt;/strong&gt;: A cluster of managers for signature validation, permission checks, and authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Core Mechanism: Non-Invasive &lt;code&gt;ClassLoader&lt;/code&gt; Delegation &amp;amp; Dynamic Dependency Graph&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is the cornerstone of &lt;code&gt;ComboLite&lt;/code&gt;'s architecture and the technical source of its "smart dependency" feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Building a Global Class Index—Reducing &lt;code&gt;O(n)&lt;/code&gt; Class Search to &lt;code&gt;O(1)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
In traditional solutions, finding a class across plugins is an &lt;code&gt;O(n)&lt;/code&gt; linear search. &lt;code&gt;ComboLite&lt;/code&gt; fundamentally solves this. During plugin installation, the framework uses the powerful &lt;code&gt;org.jf.dexlib2.DexFileFactory&lt;/code&gt; library to efficiently parse the binary structure of DEX files and generate a &lt;code&gt;class_index&lt;/code&gt; file. At load time, this index is loaded into a global &lt;code&gt;ConcurrentHashMap&amp;lt;ClassName, PluginId&amp;gt;&lt;/code&gt;. This preprocessing step trades a one-time cost at load time for &lt;code&gt;O(1)&lt;/code&gt; complexity class location capability throughout the runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: &lt;code&gt;PluginClassLoader&lt;/code&gt;—The Art of "Limited Responsibility" and "Proactive Delegation"&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;PluginClassLoader&lt;/code&gt; instance created for each plugin strictly follows Java's &lt;code&gt;ClassLoader&lt;/code&gt; parent-delegation model. Its &lt;code&gt;findClass(name: String)&lt;/code&gt; method's logic is a form of "&lt;strong&gt;directed, precise horizontal delegation after the standard process fails&lt;/strong&gt;":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in com/combo/core/runtime/loader/PluginClassLoader.kt&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PluginClassLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pluginFinder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IPluginFinder&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DexClassLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Step A: Strictly adhere to parent-delegation&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClassNotFoundException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Step B: Only when the standard process fails, initiate horizontal delegation.&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pluginFinder&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;findClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Step C: If delegation also fails, throw a specific exception for fusing&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;PluginDependencyException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The brilliance of this design is that it doesn't pollute the core responsibility of the &lt;code&gt;ClassLoader&lt;/code&gt;. It outsources the complex problem of "where to find" to the &lt;code&gt;DependencyManager&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: &lt;code&gt;DependencyManager&lt;/code&gt;—The Smart Hub for "Arbitration, Recording, and Loading"&lt;/strong&gt;&lt;br&gt;
When &lt;code&gt;DependencyManager&lt;/code&gt; receives a class lookup delegation, it performs an atomic sequence of operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Query Arbitration&lt;/strong&gt;: It accesses the global &lt;code&gt;classIndex&lt;/code&gt; to find the target class with &lt;code&gt;O(1)&lt;/code&gt; complexity.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Dependency Recording&lt;/strong&gt;: Once it determines that plugin &lt;code&gt;A&lt;/code&gt; needs a class from plugin &lt;code&gt;B&lt;/code&gt;, it immediately updates its internal forward (&lt;code&gt;dependencyGraph&lt;/code&gt;) and reverse (&lt;code&gt;dependentGraph&lt;/code&gt;) dependency graphs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Directed Loading&lt;/strong&gt;: It gets plugin B's &lt;code&gt;PluginClassLoader&lt;/code&gt; and calls an internal method that &lt;strong&gt;does not trigger delegation again&lt;/strong&gt; to load the class.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This closed loop of "&lt;strong&gt;pre-indexing -&amp;gt; standard loading -&amp;gt; delegation on failure -&amp;gt; arbitration &amp;amp; recording -&amp;gt; directed loading&lt;/strong&gt;" is built entirely on Java's &lt;code&gt;ClassLoader&lt;/code&gt; mechanism, achieving zero-hack, high-performance, fully dynamic dependency management.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Core Mechanism: "Chain Restart" Based on the Reverse Dependency Graph&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Hot updates are essentially about replacing modules at runtime, which can easily lead to state inconsistencies. &lt;code&gt;ComboLite&lt;/code&gt;'s "chain restart" provides a deterministic safety guarantee.&lt;/p&gt;

&lt;p&gt;When a hot update is triggered for a plugin, the &lt;code&gt;reloadPluginWithDependents&lt;/code&gt; method is invoked:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Query Reverse Dependencies&lt;/strong&gt;: It calls &lt;code&gt;dependencyManager.findDependentsRecursive(pluginId)&lt;/code&gt;. This method performs a &lt;strong&gt;Depth-First Search (DFS)&lt;/strong&gt; on the dynamically built &lt;code&gt;dependentGraph&lt;/code&gt; (reverse dependency graph) to find all plugins that directly or indirectly depend on the updated plugin.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Formulate Execution Plan&lt;/strong&gt;: It merges the search result with the plugin itself to form a complete "restart set."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Strict Reverse Unload and Forward Load&lt;/strong&gt;: This is the core of ensuring state consistency. &lt;code&gt;PluginManager&lt;/code&gt; first unloads all plugins in the "restart set" in &lt;strong&gt;reverse dependency order&lt;/strong&gt;, cleaning up all their runtime resources. It then reloads them in the &lt;strong&gt;original dependency order&lt;/strong&gt;, ensuring the entire dependency chain is updated consistently.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;The v2.0 Security System: Permission &amp;amp; Authorization&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is the most significant architectural upgrade in v2.0, designed to provide enterprise-grade security.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Annotation-based Declaration&lt;/strong&gt;: The framework uses the &lt;code&gt;@RequiresPermission&lt;/code&gt; annotation to declare the required permission level (&lt;code&gt;HOST&lt;/code&gt; or &lt;code&gt;SELF&lt;/code&gt;) for sensitive APIs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Caller Tracking&lt;/strong&gt;: When a plugin calls these APIs, the &lt;code&gt;checkApiCaller&lt;/code&gt; extension function accurately identifies the calling plugin's ID by analyzing the call stack (&lt;code&gt;Thread.currentThread().stackTrace&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Static Permission Check&lt;/strong&gt;: &lt;code&gt;AuthorizationManager&lt;/code&gt; first performs a &lt;strong&gt;static check&lt;/strong&gt; based on strict rules (e.g., does the caller's signature match the host's?).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic User Authorization&lt;/strong&gt;: If the static check fails, &lt;code&gt;AuthorizationManager&lt;/code&gt; forwards the request to an &lt;code&gt;IAuthorizationHandler&lt;/code&gt; implementation, which can launch a UI to display the operation details to the user and request their consent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This closed-loop process of "&lt;strong&gt;Annotation Declaration -&amp;gt; Static Check -&amp;gt; Dynamic Authorization&lt;/strong&gt;" builds a robust and flexible security defense for &lt;code&gt;ComboLite&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 5: From 0 to 1: A Complete Tutorial on Building a "Pluggable" App
&lt;/h2&gt;

&lt;p&gt;Now, we'll guide you through a complete plugin development cycle from scratch, building a "pluggable" dynamic application with &lt;code&gt;ComboLite&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Laying the Foundation — Project Initialization and Dependency Configuration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, create a new Android project. We highly recommend using &lt;strong&gt;Version Catalog (&lt;code&gt;libs.versions.toml&lt;/code&gt;)&lt;/strong&gt; to manage dependencies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define dependencies in &lt;code&gt;gradle/libs.versions.toml&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;
&lt;span class="py"&gt;combolite&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.0.0"&lt;/span&gt;  &lt;span class="c"&gt;# We strongly recommend using the latest stable version&lt;/span&gt;
&lt;span class="py"&gt;aar2apk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.1.0"&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;
&lt;span class="py"&gt;combolite-core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"io.github.lnzz123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"combolite-core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"combolite"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[plugins]&lt;/span&gt;
&lt;span class="py"&gt;combolite-aar2apk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"io.github.lnzz123.combolite-aar2apk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"aar2apk"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Apply the packaging plugin in the root &lt;code&gt;build.gradle.kts&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in your project's root /build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;combolite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aar2apk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Add the core library to the host &lt;code&gt;:app&lt;/code&gt; module's &lt;code&gt;build.gradle.kts&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in your :app/build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;combolite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Building a Solid Base — Configuring the "Shell" Host&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implement an auto-initializing &lt;code&gt;Application&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;code&gt;ComboLite&lt;/code&gt; offers an extremely simple initialization method. Just have your &lt;code&gt;Application&lt;/code&gt; class inherit from &lt;code&gt;BaseHostApplication&lt;/code&gt;, and the framework will automatically handle all the tedious initialization work for you.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in :app/src/main/java/.../HostApp.kt&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.runtime.app.BaseHostApplication&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainApplication&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BaseHostApplication&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onFrameworkSetup&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// --- Perform all framework-related configurations here ---&lt;/span&gt;
            &lt;span class="c1"&gt;// Example: Set the plugin signature validation strategy&lt;/span&gt;
            &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValidationStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidationStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Insecure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure the proxy &lt;code&gt;Activity&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
To allow plugins to correctly access merged resources, the host's &lt;code&gt;Activity&lt;/code&gt; needs to inherit from &lt;code&gt;BaseHostActivity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in :app/src/main/java/.../MainActivity.kt&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.component.activity.BaseHostActivity&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BaseHostActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Creating the First "Part" — Developing a Simple "Greeting" Plugin&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a new plugin module&lt;/strong&gt;:&lt;br&gt;
In your project, select &lt;code&gt;File &amp;gt; New &amp;gt; New Module...&lt;/code&gt;, then choose &lt;code&gt;Android Library&lt;/code&gt;, and name it &lt;code&gt;hello-plugin&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add dependencies for the plugin&lt;/strong&gt;:&lt;br&gt;
The plugin module's dependency on the core library should be &lt;code&gt;compileOnly&lt;/code&gt;, as these classes will be provided by the host at runtime.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in :hello-plugin/build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;compileOnly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;combolite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implement the plugin entry class &lt;code&gt;IPluginEntryClass&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
This is the core of the plugin. It implements the &lt;code&gt;IPluginEntryClass&lt;/code&gt; interface and is the sole bridge for interaction between the plugin and the framework.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in :hello-plugin/src/main/java/.../HelloPluginEntry.kt&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.helloplugin&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.Text&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.runtime.Composable&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.api.IPluginEntryClass&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.model.PluginContext&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloPluginEntry&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;IPluginEntryClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PluginContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Plugin [${context.pluginInfo.id}] has been loaded."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onUnload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Plugin is being unloaded."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Composable&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello from a dynamically loaded Plugin!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure plugin metadata in the Manifest&lt;/strong&gt;:&lt;br&gt;
The framework learns the plugin's ID, version, and entry class through its &lt;code&gt;AndroidManifest.xml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;manifest&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
    &lt;span class="na"&gt;package=&lt;/span&gt;&lt;span class="s"&gt;"com.example.myplugin"&lt;/span&gt;
    &lt;span class="na"&gt;android:versionCode=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
    &lt;span class="na"&gt;android:versionName=&lt;/span&gt;&lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt; 
        &lt;span class="na"&gt;android:label=&lt;/span&gt;&lt;span class="s"&gt;"My First Plugin"&lt;/span&gt;
        &lt;span class="na"&gt;android:icon=&lt;/span&gt;&lt;span class="s"&gt;"@drawable/plugin_icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt; 
            &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"plugin.entryClass"&lt;/span&gt; 
            &lt;span class="na"&gt;android:value=&lt;/span&gt;&lt;span class="s"&gt;"com.example.helloplugin.HelloPluginEntry"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Packaging &amp;amp; Debugging with &lt;code&gt;aar2apk&lt;/code&gt;'s One-Click Magic&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;aar2apk&lt;/code&gt; Gradle plugin fully automates the complex conversion of an AAR into a functional APK. &lt;code&gt;ComboLite&lt;/code&gt; v2.0 introduces a revolutionary feature: &lt;strong&gt;seamless development-time debugging&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Declare Plugins in the Project Root &lt;code&gt;build.gradle.kts&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
This tells the &lt;code&gt;aar2apk&lt;/code&gt; plugin which modules to manage.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in your project's root /build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;aar2apk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":hello-plugin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;signing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... configure your signing information ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enable Integration in the Host App &lt;code&gt;build.gradle.kts&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
This turns on the automatic integration feature for debugging.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// in your :app/build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;packagePlugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;buildType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PackageBuildType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pluginsDir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// The directory where APKs are stored within assets&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this setup, every time you click "Run" or "Debug" in Android Studio, the plugin is automatically compiled, packaged, and embedded into the host's &lt;code&gt;assets&lt;/code&gt; directory, ready for immediate testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Witness the Magic — Dynamically Loading and Running the Plugin&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, let's write the host logic to load and run the plugin from the &lt;code&gt;assets&lt;/code&gt; folder during debug builds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In MainActivity.kt&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.lifecycle.lifecycleScope&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.runtime.PluginManager&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.combo.core.utils.installPluginsFromAssetsForDebug&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlinx.coroutines.launch&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BaseHostActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pluginId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.example.myplugin"&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;pluginEntry&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IPluginEntryClass&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;isLoading&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... render UI based on isLoading and pluginEntry ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lifecycleScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Debug mode: Force reinstall on every launch to ensure the latest code&lt;/span&gt;
                &lt;span class="nf"&gt;installPluginsFromAssetsForDebug&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadEnabledPlugins&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launchPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pluginId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Check for the plugin instance&lt;/span&gt;
            &lt;span class="n"&gt;pluginEntry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PluginManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPluginInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pluginId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run your &lt;code&gt;app&lt;/code&gt; module. The app will launch, and &lt;strong&gt;“Hello from a dynamically loaded Plugin!”&lt;/strong&gt; will magically appear on your screen!&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 6: Mastering ComboLite - Advanced Guides and Best Practices
&lt;/h2&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Packaging Strategies: &lt;code&gt;compileOnly&lt;/code&gt; is the Golden Rule&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;For plugin development, &lt;strong&gt;&lt;code&gt;compileOnly&lt;/code&gt; is the norm, &lt;code&gt;implementation&lt;/code&gt; is the exception&lt;/strong&gt;. All common libraries you expect the host to provide (like &lt;code&gt;comboLite-core&lt;/code&gt;, &lt;code&gt;Kotlin&lt;/code&gt;, &lt;code&gt;AndroidX&lt;/code&gt;, &lt;code&gt;OkHttp&lt;/code&gt;, etc.) should use &lt;code&gt;compileOnly&lt;/code&gt; to keep plugins lightweight and avoid dependency conflicts.&lt;/p&gt;

&lt;p&gt;While the &lt;code&gt;aar2apk&lt;/code&gt; plugin provides capabilities like &lt;code&gt;includeAllDependencies()&lt;/code&gt; to bundle dependencies, this should be considered a &lt;strong&gt;fallback for special cases, not a routine operation&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Full Support for the Four Major Components&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; provides comprehensive support for Android's four major components (Activity, Service, BroadcastReceiver, ContentProvider) through a powerful &lt;strong&gt;proxy pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A crucial concept to understand is the &lt;strong&gt;Proxy Context&lt;/strong&gt;. Plugin &lt;code&gt;Activities&lt;/code&gt; and &lt;code&gt;Services&lt;/code&gt; are not native components but regular objects. You &lt;strong&gt;cannot&lt;/strong&gt; directly call standard &lt;code&gt;Context&lt;/code&gt; methods like &lt;code&gt;this.finish()&lt;/code&gt;. Instead, you must use the &lt;code&gt;proxyActivity&lt;/code&gt; or &lt;code&gt;proxyService&lt;/code&gt; property provided by the base classes (&lt;code&gt;BasePluginActivity&lt;/code&gt;, &lt;code&gt;BasePluginService&lt;/code&gt;) to access all standard &lt;code&gt;Context&lt;/code&gt; functionalities.&lt;/p&gt;

&lt;p&gt;The setup for each component follows a similar pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Host-Side Setup&lt;/strong&gt;: Create an empty proxy component (e.g., &lt;code&gt;HostActivity&lt;/code&gt;), register it in the host's &lt;code&gt;AndroidManifest.xml&lt;/code&gt;, and inform the &lt;code&gt;PluginManager&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Plugin-Side Implementation&lt;/strong&gt;: Inherit from the provided base class (e.g., &lt;code&gt;BasePluginActivity&lt;/code&gt;) and implement your logic, always using the &lt;code&gt;proxy&lt;/code&gt; object for context-related calls.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Final Invocation&lt;/strong&gt;: Use the provided extension functions like &lt;code&gt;context.startPluginActivity(...)&lt;/code&gt; to launch your plugin components.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;A Tour of the Core APIs (&lt;code&gt;PluginManager&lt;/code&gt;)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;PluginManager&lt;/code&gt; is the single public API entry point of the framework, providing comprehensive control. Key functions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Status Query &amp;amp; Listening&lt;/strong&gt;: &lt;code&gt;initStateFlow&lt;/code&gt;, &lt;code&gt;loadedPluginsFlow&lt;/code&gt;, &lt;code&gt;getPluginInstance(pluginId)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Lifecycle Control&lt;/strong&gt;: &lt;code&gt;launchPlugin(pluginId)&lt;/code&gt;, &lt;code&gt;unloadPlugin(pluginId)&lt;/code&gt;, &lt;code&gt;loadEnabledPlugins()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Discovery&lt;/strong&gt;: &lt;code&gt;getInterface(interfaceClass, className)&lt;/code&gt; for ultimate cross-plugin decoupling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Query&lt;/strong&gt;: &lt;code&gt;getPluginDependentsChain(pluginId)&lt;/code&gt; ("Who depends on me?") and &lt;code&gt;getPluginDependenciesChain(pluginId)&lt;/code&gt; ("What do I depend on?").&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 7: The Future is Now: The Game-Changer 2.0 Release
&lt;/h2&gt;

&lt;p&gt;In the 1.0 era of &lt;code&gt;ComboLite&lt;/code&gt;, we accomplished our core mission: to provide a rock-solid, future-proof foundation. However, a truly modern framework cannot stop at being merely "stable and usable."&lt;/p&gt;

&lt;p&gt;Two major roadblocks have plagued plugin developers: a &lt;strong&gt;hellish debugging experience&lt;/strong&gt; and a &lt;strong&gt;fragile security model&lt;/strong&gt;. &lt;strong&gt;&lt;code&gt;ComboLite&lt;/code&gt; 2.0&lt;/strong&gt; is here to fix that, making the crucial leap from a "stable foundation" to an "efficient and secure productivity platform."&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;A Revolution in Developer Experience&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;build-time automatic integration&lt;/strong&gt; (&lt;code&gt;packagePlugins&lt;/code&gt; feature) described in the tutorial is the most exciting feature of &lt;code&gt;ComboLite&lt;/code&gt; 2.0. It completely changes your plugin development workflow, reducing debugging cycles from minutes to seconds.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;An Enterprise-Grade Security System&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; 2.0 introduces a complete enterprise-grade security architecture, built on the three pillars of a &lt;strong&gt;permission system&lt;/strong&gt;, &lt;strong&gt;validation strategies&lt;/strong&gt;, and &lt;strong&gt;crash handling&lt;/strong&gt;. This system is what allows &lt;code&gt;ComboLite&lt;/code&gt; to support an open ecosystem, like a "plugin store," with confidence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pillar 1: The Permission System (The Law)&lt;/strong&gt;: Through the &lt;code&gt;@RequiresPermission&lt;/code&gt; annotation, we've set clear "access rules" for all sensitive APIs within the framework (&lt;code&gt;PermissionLevel.HOST&lt;/code&gt; and &lt;code&gt;PermissionLevel.SELF&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pillar 2: Validation Strategies (The Gatekeeper)&lt;/strong&gt;: &lt;code&gt;ComboLite&lt;/code&gt; 2.0 gives the decision-making power to you. Through &lt;code&gt;PluginManager.setValidationStrategy()&lt;/code&gt;, you can set three different "security check" strategies:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ValidationStrategy.Strict&lt;/code&gt;: Continues the 1.0 strategy; the signature must match the host's.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ValidationStrategy.UserGrant&lt;/code&gt;: If the signature doesn't match, it automatically launches an activity to ask the user for permission.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ValidationStrategy.Insecure&lt;/code&gt;: Disables all validation, for debugging only.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Pillar 3: The Crash Handler (The Fusebox)&lt;/strong&gt;: The &lt;code&gt;PluginCrashHandler&lt;/code&gt; has been fully enhanced in 2.0 with a new crash UI with syntax highlighting and a tiered callback mechanism for fine-grained exception handling.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 8: The Road Ahead
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; is an active open-source project, and development doesn't stop at 2.0. While the core is more stable and feature-rich than ever, we are always looking for ways to improve.&lt;/p&gt;

&lt;p&gt;Our immediate focus is on continuing to refine the framework, fix bugs, and enhance the existing features based on community feedback. One potential area of exploration for embracing an even more open ecosystem is the introduction of a &lt;strong&gt;Whitelist Mode&lt;/strong&gt; for validation. This would allow a host to configure a set of trusted public key fingerprints, enabling the loading of third-party plugins from a pre-approved list without prompting the user every time.&lt;/p&gt;

&lt;p&gt;We believe in community-driven development. If you have ideas, encounter pain points, or have suggestions for the future, your feedback is crucial.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion: It's Time to Build Dynamic Applications in a Modern Way&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ComboLite&lt;/code&gt; provides a "return to standards" option for the Android dynamic landscape. It proves that we can build a powerful, user-friendly, and truly future-proof plugin framework without resorting to any hacking. We firmly believe that a great framework should be like a fine Swiss Army knife: not only powerful and reliable but also a pleasure to use every time.&lt;/p&gt;

&lt;p&gt;We've paved the way for you. Now, we invite you to embark on this journey and experience this unprecedented development bliss.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Call to Action&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project Source Code&lt;/strong&gt;: &lt;strong&gt;&lt;a href="https://github.com/lnzz123/ComboLite" rel="noopener noreferrer"&gt;https://github.com/lnzz123/ComboLite&lt;/a&gt;&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;If you appreciate the design philosophy and engineering practices of &lt;code&gt;ComboLite&lt;/code&gt;, please give us a &lt;strong&gt;Star&lt;/strong&gt;! Your support is our greatest motivation for continuous iteration.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Sample App Download&lt;/strong&gt;: &lt;strong&gt;&lt;a href="https://raw.githubusercontent.com/lnzz123/ComboLite/refs/heads/master/app/release/app-release.apk" rel="noopener noreferrer"&gt;Click here to download the APK directly&lt;/a&gt;&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Install the sample app and experience what a "pluggable everything" application is like.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Communication &amp;amp; Contribution&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Have any questions, suggestions, or found a bug? We look forward to in-depth technical discussions with you in &lt;strong&gt;&lt;a href="https://github.com/lnzz123/ComboLite/issues" rel="noopener noreferrer"&gt;GitHub Issues&lt;/a&gt;&lt;/strong&gt;!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>pluginframework</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
