Plugin Framework - Principles
Why use plugins
During the implementation of software, it is desired to dynamically add or remove specific functionalities.
Common plugin frameworks
Plugin principles
Plugin management
What is a framework?
Firstly, a framework consists of three parts: users, the framework itself, and extensions. Users are the hosts, the framework itself is the manager, and extensions are the specific plugins.
Lifecycle
To dynamically add or remove functionalities, it is necessary to intercept the host's lifecycle and add calls to the plugin at specific stages. The intercepted lifecycle nodes are the extension points.
Dependency management
In addition to implementing the interception of the host's lifecycle to call the plugin, the framework also needs to manage the dependencies of the plugins, as some plugins depend on each other.
Dependency implementation
- Static: Each plugin declares a dependency tree, and all plugins generate a dependency graph. The plugins are initialized and called in the order of the dependency graph (partially parallel).
- Dynamic: When initializing and calling plugins, dependencies are checked and called dynamically through recursion. It is necessary to pay attention to checking for circular dependencies. The dependency graph can be checked for circular dependencies through post-validation, while the dynamic approach requires other mechanisms to prevent circular dependencies.
Plugin implementation
- Unique identifier for the plugin
- Declaration of plugin dependencies
- Declaration of plugin extension points
- Plugin implementation
- Plugin lifecycle
Different from intercepting the host's lifecycle, plugins also have their own lifecycle, such as plugin creation/destruction, loading/unloading, dependency configuration, etc.
Plugin Framework - Implementation of Android Plugin Framework
Host lifecycle
interface IPluginApplication {
fun onCreate()
fun onTerminate()
fun onLowMemory()
fun onTrimMemory(level: Int)
fun onConfigurationChanged(newConfig: Configuration)
}
Intercepting the host lifecycle
open class PluginApplication : MultiDexApplication(), IPluginApplication {
lateinit var proxy: IPluginApplication
override fun onCreate() {
proxy = PluginApplicationProxy(this)
super.onCreate()
proxy.onCreate()
}
override fun onTerminate() {
super.onTerminate()
proxy.onTerminate()
}
override fun onLowMemory() {
super.onLowMemory()
proxy.onLowMemory()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
proxy.onTerminate()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
proxy.onConfigurationChanged(newConfig)
}
}
class PluginApplicationProxy(val application: Application) : IPluginApplication {
override fun onCreate() {
}
override fun onTerminate() {
}
override fun onLowMemory() {
}
override fun onTrimMemory(level: Int) {
}
override fun onConfigurationChanged(newConfig: Configuration) {
}
}
Plugin management
interface IPlugin {
fun onCreate()
fun onTerminate()
fun onLowMemory()
fun onTrimMemory(level: Int)
fun onConfigurationChanged(newConfig: Configuration)
}
object PluginManager {
private val plugins = mutableListOf<IPlugin>()
fun init(application: Application) {
//find plugin
//generate dependencies graph //next
}
fun add(plugin: IPlugin) {
plugins.add(plugin)
}
fun remove(plugin: IPlugin) {
plugins.remove(plugin)
}
}