IntelliJ Platform Plugin SDK Help

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 services

  • MyProjectService(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/exposed as API to other plugins does not need to be registered in plugin.xml (see Declaring a Service). Instead, annotate service class with @Service (see Examples). The service instance will be created in scope according to the caller (see Retrieving a Service).

Light Service Restrictions

  • None of these attributes is required: os, client, overrides, id, preload.

  • Service class must be final.

  • Constructor injection of dependency services is not supported.

  • If application-level service is a PersistentStateComponent, roaming must be disabled (roamingType = RoamingType.DISABLED).

Use these inspections to verify these and highlight 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:

@Service public final class MyAppService { public void doSomething(String param) { // ... } }

Project-level light service example:

@Service(Service.Level.PROJECT) public final class MyProjectService { private final Project myProject; MyProjectService(Project project) { myProject = project; } public void doSomething(String param) { String projectName = myProject.getName(); // ... } }

Application-level light service:

@Service class MyAppService { fun doSomething(param: String) { // ... } }

Project-level light service example:

@Service(Service.Level.PROJECT) class MyProjectService(private val project: Project) { fun doSomething(param: String) { val projectName = project.name // ... } }

Declaring a Service

To register a non-Light Service, distinct extension points are provided for each type:

  • com.intellij.applicationService - application-level service

  • com.intellij.projectService - project-level service

  • com.intellij.moduleService - module-level service (not recommended, see Note)

To expose service API, create a separate class for serviceInterface and extend it in corresponding class registered in serviceImplementation. If serviceInterface isn't specified, it's supposed to have the same value as serviceImplementation. Use inspection Plugin DevKit | Plugin descriptor | Plugin.xml extension registration to highlight redundant serviceInterface declarations.

To provide a custom implementation for test/headless environment, specify testServiceImplementation/headlessImplementation additionally.

Example

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:

<extensions defaultExtensionNs="com.intellij"> <!-- Declare the application-level service --> <applicationService serviceInterface="com.example.MyAppService" serviceImplementation="com.example.MyAppServiceImpl"/> <!-- Declare the project-level service --> <projectService serviceInterface="com.example.MyProjectService" serviceImplementation="com.example.MyProjectServiceImpl"/> </extensions>

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.

MyAppService applicationService = ApplicationManager.getApplication().getService(MyAppService.class); MyProjectService projectService = project.getService(MyProjectService.class);

Service implementations can wrap these calls with convenient static getInstance() or getInstance(Project) method:

MyAppService applicationService = MyAppService.getInstance(); MyProjectService projectService = MyProjectService.getInstance(project);
val applicationService = service<MyAppService>() val projectService = project.service<MyProjectService>()

Getting Service Flow

Allowed in any thread.Call on demand only.Never cache the result.Do not call in constructorsunless needed.getServiceReturn nullnoIs Service Declaration FoundyesnoIs Light ServiceyesIs Container Active?yesdisposed or dispose in progresssynchronizedon service classIs Initializing?yesnoThrowPluginException(Cyclic ServiceInitialization)non-cancelableAvoid getting otherservices to reducethe initialization tree.The fewer thedependencies,the faster and morereliable initialization.Create InstanceRegister to be Disposedon Container Dispose(Disposable only)Load Persistent State(PersistentStateComponentonly)noIs Created and Initialized?yesThrowProcessCanceledExceptionnoIs Created and Initialized?yesReturn Instance

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.

Last modified: 13 July 2024