You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发IntelliJ插件使用Modal ProgressIndicator触发Psi对象读取权限错误

IntelliJ插件使用Modal ProgressIndicator访问Psi对象触发读权限错误

问题描述

开发IntelliJ插件时,使用Modal ProgressIndicator展示进度,不使用该组件时代码运行正常,但使用后访问Psi对象(如PsiElement、PsiAnnotation)时触发读权限错误。

无Modal ProgressIndicator的工作代码

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
  if (new CreationWindowWrapper(true).showAndGet()) {
    loadAndInit();
  }
}

使用Modal ProgressIndicator的出错代码

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
  if (new CreationWindowWrapper(true).showAndGet()) {
    var project = ProjectManager.getInstance().getOpenProjects()[0];
    var modalTask = new Modal(project, "Copying Case", true) {
      public void run(@NotNull() final ProgressIndicator indicator) {
        loadAndInit(indicator);
      }
    };
    ApplicationManager.getApplication().invokeLater(() -> ProgressManager.getInstance().run(modalTask));
  }
}

错误堆栈信息

java.lang.Throwable: Read access is allowed from inside read-action (or EDT) only (see com.intellij.openapi.application.Application.runReadAction())
    at com.intellij.openapi.diagnostic.Logger.error(Logger.java:200)
    at com.intellij.openapi.application.impl.ApplicationImpl.assertReadAccessAllowed(ApplicationImpl.java:980)
    at com.intellij.psi.impl.source.tree.TreeElement.assertReadAccessAllowed(TreeElement.java:424)
    at com.intellij.psi.impl.source.tree.SharedImplUtil.findFileElement(SharedImplUtil.java:79)
    at com.intellij.psi.impl.source.tree.SharedImplUtil.getContainingFile(SharedImplUtil.java:54)
    at com.intellij.psi.impl.source.tree.CompositePsiElement.getContainingFile(CompositePsiElement.java:90)
    at com.intellij.psi.impl.ResolveScopeManager.getElementResolveScope(ResolveScopeManager.java:34)
    at com.intellij.psi.impl.source.tree.CompositePsiElement.getResolveScope(CompositePsiElement.java:241)
    at com.intellij.psi.impl.file.PsiPackageImpl.processDeclarations(PsiPackageImpl.java:297)
    at com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processPackageDeclarations(PsiJavaFileBaseImpl.java:451)
    at com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processOnDemandTarget(PsiJavaFileBaseImpl.java:461)
    at com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processImplicitImports(PsiJavaFileBaseImpl.java:428)
    at com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processOnDemandPackages(PsiJavaFileBaseImpl.java:381)
    at com.intellij.psi.impl.source.PsiJavaFileBaseImpl.processDeclarations(PsiJavaFileBaseImpl.java:301)
    at com.intellij.psi.scope.util.PsiScopesUtil.treeWalkUp(PsiScopesUtil.java:61)
    at com.intellij.psi.scope.util.PsiScopesUtil.treeWalkUp(PsiScopesUtil.java:40)
    at com.intellij.psi.scope.util.PsiScopesUtil.resolveAndWalk(PsiScopesUtil.java:207)
    at com.intellij.psi.scope.util.PsiScopesUtil.resolveAndWalk(PsiScopesUtil.java:136)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl.resolve(PsiJavaCodeReferenceElementImpl.java:495)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl.access$100(PsiJavaCodeReferenceElementImpl.java:44)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl$OurGenericsResolver.resolve(PsiJavaCodeReferenceElementImpl.java:381)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl$OurGenericsResolver.resolve(PsiJavaCodeReferenceElementImpl.java:374)
    at com.intellij.psi.impl.source.resolve.ResolveCache.lambda$resolveWithCaching$2(ResolveCache.java:184)
    at com.intellij.openapi.util.Computable.get(Computable.java:18)
    at com.intellij.psi.impl.source.resolve.ResolveCache.lambda$loggingResolver$4(ResolveCache.java:260)
    at com.intellij.openapi.util.RecursionManager$1.computePreventingRecursion(RecursionManager.java:114)
    at com.intellij.openapi.util.RecursionGuard.doPreventingRecursion(RecursionGuard.java:44)
    at com.intellij.openapi.util.RecursionManager.doPreventingRecursion(RecursionManager.java:68)
    at com.intellij.psi.impl.source.resolve.ResolveCache.resolve(ResolveCache.java:237)
    at com.intellij.psi.impl.source.resolve.ResolveCache.resolveWithCaching(ResolveCache.java:184)
    at com.intellij.psi.impl.PsiImplUtil.multiResolveImpl(PsiImplUtil.java:789)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl.getCanonicalText(PsiJavaCodeReferenceElementImpl.java:296)
    at com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl.getCanonicalText(PsiJavaCodeReferenceElementImpl.java:280)
    at com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl.getQualifiedName(PsiAnnotationImpl.java:77)
    at com.intellij.psi.impl.PsiImplUtil.findAnnotation(PsiImplUtil.java:296)
    at com.intellij.psi.impl.source.PsiModifierListImpl.findAnnotation(PsiModifierListImpl.java:311)
    at com.intellij.psi.PsiJvmConversionHelper.getListAnnotation(PsiJvmConversionHelper.java:53)
    at com.intellij.psi.PsiModifierListOwner.getAnnotation(PsiModifierListOwner.java:41)
    at com.intellij.psi.PsiJvmModifiersOwner.getAnnotation(PsiJvmModifiersOwner.java:27)
    at se.tvv.insertionextractor.extractors.ValueExtractor.getElementValue(ValueExtractor.java:82)
    at se.tvv.insertionextractor.converter.FieldMapper.createSimpleColumnField(FieldMapper.java:71)
    at se.tvv.insertionextractor.converter.FieldMapper.mapField(FieldMapper.java:44)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:442)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at se.tvv.insertionextractor.converter.FieldMapper.getClassFields(FieldMapper.java:35)
    at se.tvv.insertionextractor.extractors.Extractor.initiateCopyOfObject(Extractor.java:36)
    at se.tvv.insertionextractor.extractors.Extractor.init(Extractor.java:30)
    at se.tvv.insertionextractor.MainAction.loadAndInit(MainAction.java:63)
    at se.tvv.insertionextractor.MainAction$1.run(MainAction.java:50)

已尝试的无效方案

  • 使用SwingUtils.invokeLater调用任务:
    SwingUtils.invokeLater(ProgressManager.getInstance().run(modalTask))
    
  • 使用invokeLaterOnWriteThread调用任务:
    ApplicationManager.getApplication().invokeLaterOnWriteThread(ProgressManager.getInstance().run(modalTask))
    

问题原因与解决方法

原因

Modal ProgressIndicator的任务运行在后台线程,而Psi对象的访问必须在**读操作(Read Action)**或EDT线程中执行,直接在任务run方法中访问Psi会违反IntelliJ平台线程安全规则。

修复代码

将访问Psi对象的逻辑包裹在Application.runReadAction中,确保读操作在安全上下文执行:

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
  if (new CreationWindowWrapper(true).showAndGet()) {
    var project = e.getProject();
    if (project == null) return;
    
    var modalTask = new Modal(project, "Copying Case", true) {
      public void run(@NotNull final ProgressIndicator indicator) {
        ApplicationManager.getApplication().runReadAction(() -> {
          loadAndInit(indicator);
        });
      }
    };
    ProgressManager.getInstance().run(modalTask);
  }
}

额外注意事项

  1. 直接从AnActionEvent获取项目,避免遍历所有打开项目的不可靠操作
  2. 无需额外invokeLater,ProgressManager.run会自动处理线程调度
  3. 若loadAndInit包含耗时操作,仅将Psi访问部分放在读操作中,避免长时间占用读锁影响IDE响应

内容的提问来源于stack exchange,提问作者Gabriel Eriksson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 12:32:04