开发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); } }
额外注意事项
- 直接从
AnActionEvent获取项目,避免遍历所有打开项目的不可靠操作 - 无需额外
invokeLater,ProgressManager.run会自动处理线程调度 - 若
loadAndInit包含耗时操作,仅将Psi访问部分放在读操作中,避免长时间占用读锁影响IDE响应
内容的提问来源于stack exchange,提问作者Gabriel Eriksson
相关产品推荐
相关产品推荐

