Services
A service is a plugin component loaded on demand when your plugin calls the getService()
method of corresponding ComponentManager
instance (see Types). The IntelliJ Platform ensures that only one instance of a service is loaded even though it is called several times. Services are used to encapsulate logic operating on a set of related classes or to provide some reusable functionality that can be used across the plugin project. Conceptually, they don't differ from the service classes in other languages or frameworks.
A service must have an implementation class used for service instantiation. A service may also have an interface class used to obtain the service instance and provide the service's API.
A service needing a shutdown hook/cleanup routine can implement Disposable
and perform necessary work in dispose()
(see Automatically Disposed Objects).
Types
The IntelliJ Platform offers three types of services: application-level services (global singleton), project-level services, and module-level services. For the latter two, a separate instance of the service is created for each instance of its corresponding scope, see Project Model Introduction.
Constructor
To improve startup performance, avoid any heavy initializations in the constructor.
Project/Module-level service constructors can have a Project
/Module
argument.
Kotlin Coroutines
When using Kotlin Coroutines, a distinct service scope can be injected as parameter.
The Application Service and Project Service scopes are bound to an application and project service lifetimes accordingly. They are children of the Intersection Scopes, which means that they are canceled when the application/project is closed or a plugin is unloaded.
The service scope is provided to services via constructor injection. The following constructor signatures are supported:
MyService(CoroutineScope)
for application and project servicesMyProjectService(Project, CoroutineScope)
for project services
Each service instance receives its own scope instance. The injected scopes' contexts contain Dispatchers.Default
and CoroutineName(serviceClass)
.
See Launching Coroutine From Service Scope for full samples.
Light Services
A service not going to be overridden or exposed as API to other plugins does not need to be registered in plugin.xml (see Declaring a Service). Instead, annotate the service class with @Service
(see Examples). The service instance will be created in the scope according to the caller (see Retrieving a Service).
Light Service Restrictions
None of these attributes/restrictions (available for registration of non-light services) is allowed:
id
,os
,client
,overrides
,configurationSchemaKey
/preload
(Internal API).There is no separate headless/test implementation required.
Service class must be
final
.Constructor injection of dependency services is not supported.
If an application-level service is a PersistentStateComponent, roaming must be disabled (
roamingType = RoamingType.DISABLED
).
Use these inspections to verify above restrictions and highlight non-light services that can be converted (2023.3):
Plugin DevKit | Code | Light service must be final
Plugin DevKit | Code | Mismatch between light service level and its constructor
Plugin DevKit | Code | A service can be converted to a light one and corresponding Plugin DevKit | Plugin descriptor | A service can be converted to a light one for plugin.xml
Examples
Application-level light service:
Project-level light service example:
Application-level light service:
Project-level light service example:
Declaring a Service
To register a non-Light Service, distinct extension points are provided for each type:
com.intellij.applicationService
– application-level servicecom.intellij.projectService
– project-level servicecom.intellij.moduleService
– module-level service (not recommended, see Note)
The service implementation is specified in the required serviceImplementation
attribute.
Service API
To expose a service's API, create a separate class for serviceInterface
and extend it in the corresponding class registered in serviceImplementation
. If serviceInterface
isn't specified, it is supposed to have the same value as serviceImplementation
. Use inspection Plugin DevKit | Plugin descriptor | Plugin.xml extension registration to highlight redundant serviceInterface
declarations.
Additional Attributes
A service can be restricted to a certain OS via the os
attribute.
To provide a custom implementation for test or headless environment, specify testServiceImplementation
or headlessImplementation
respectively.
Examples
Application-level service:
Interface:
public interface MyAppService { void doSomething(String param); }Implementation:
final class MyAppServiceImpl implements MyAppService { @Override public void doSomething(String param) { // ... } }
Project-level service:
Interface:
public interface MyProjectService { void doSomething(String param); }Implementation:
final class MyProjectServiceImpl implements MyProjectService { private final Project myProject; MyProjectServiceImpl(Project project) { myProject = project; } public void doSomething(String param) { String projectName = myProject.getName(); // ... } }
Application-level service:
Interface:
interface MyAppService { fun doSomething(param: String) }Implementation:
internal class MyAppServiceImpl : MyAppService { override fun doSomething(param: String) { // ... } }
Project-level service:
Interface:
interface MyProjectService { fun doSomething(param: String) }Implementation:
internal class MyProjectServiceImpl(private val project: Project) : MyProjectService { fun doSomething(param: String) { val projectName = project.name // ... } }
Registration in plugin.xml:
Retrieving a Service
Getting a service doesn't need a read action and can be performed from any thread. If a service is requested from several threads, it will be initialized in the first thread, and other threads will be blocked until it is fully initialized.
Service implementations can wrap these calls with convenient static getInstance()
or getInstance(Project)
method:
Getting Service Flow
Sample Plugin
To clarify how to use services, consider the maxOpenProjects sample plugin available in the code samples.
This plugin has an application service counting the number of currently opened projects in the IDE. If this number exceeds the maximum number of simultaneously opened projects allowed by the plugin (3), it displays an information message.
See Code Samples on how to set up and run the plugin.