Running a background task from an annotator
Hi everyone,
I have a proble with an annotator which is used to type check the current file. The first time the annotator is used, it retrieves a global type checker which can then be reused for later usage. The problem is, instantiating this type checker can take some time, depending on the project size. IIUC, the IDE waits for this annotator to finish because the UI is frozen during the type checker's instantiation (on a real project it can take up to 25s). To circumvent this, I'd like the type checker to be instantiated in a background thread (with a progress indicating something is loading to the user), return immediatly from the annotator, and restart the annotator once the type checker is ready.
Here's my current attempt (extracted from a projectService extension):
private TypeChecker typeChecker; private boolean isBuildingTypeChecker = false;
public TypeChecker getTypeChecker() { if (typeChecker == null && !isBuildingTypeChecker) { isBuildingTypeChecker = true; // TODO really thread-safe? Runnable instantiateTypeCheckerTask = new Runnable() { @Override public void run() { typeChecker = createTypeChecker(); isBuildingTypeChecker = false; DaemonCodeAnalyzer.getInstance(project).restart(); } }; RunnableBackgroundableWrapper taskWrapper = new RunnableBackgroundableWrapper(project, "Preparing Ceylon project", instantiateTypeCheckerTask); ProgressIndicator progressIndicator = new BackgroundableProcessIndicator(taskWrapper); progressIndicator.setIndeterminate(true); progressIndicator.setText("Preparing Ceylon project..."); // We still get a (harmless?) error :( ProgressManager.getInstance().runProcessWithProgressAsynchronously(taskWrapper, progressIndicator); } return typeChecker; }
Calling runProcessWithProgressAsynchronously() displays an error telling me that I should call it from the EDT ("Access is allowed from event dispatch thread only."). The debugger also tells me that I'm in a different thread. While I'm aware this message is harmless, I'd still like to avoid it.
1. How come the UI freezes if I don't run the code in a background thread, whereas the annotator is not run from the EDT/UI thread?
2. Is there another API that I missed which would allow me to run a background task, with progress, that must not be run from the EDT?
Thanks for your help
请先登录再写评论。
Here is the general idea of how to run a backroundable task
ProgressManager.getInstance().run(new Backgroundable(project, "Description") {
@Override
public void run(@NotNull final ProgressIndicator progressIndicator) {
// put your code here
// to access Project use myProject field
}
});
Thanks for the hint, but I still get the same (hamrless?) error, although it does not stop my task:
I guess there's no workaround to this?
try to enclose the code that access the UI into WriteAction, check this workaround
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
// Access UI here
}
});
}
});
Thanks Mahmoud, it seems to be working with the following code:
WriteAction is required to guard modifications (of PSI or document text or VFS or ...).
For mere UI refresh invokeLater suits better.