Статьи

Отсроченная выборка элементов модели с помощью JFace Viewer

Элементы модели, отображаемые в Eclipse JFace Viewers, иногда загружаются довольно долго. По этой IDeferredWorkbenchAdapter предоставляет тип IDeferredWorkbenchAdapter для выборки таких элементов модели в фоновом режиме. К сожалению, этот механизм, кажется, поддерживается только для производных AbstractTreeViewer через DeferredTreeContentManager .

Поэтому я разработал собственный собственный DeferredContentManager … Он позволяет выполнять фоновую загрузку для всех типов StructuredViewer которые позволяют добавлять и удалять элементы модели. И в этом посте я объясняю, как это работает и как его можно использовать.

В связи с необходимостью (повторного) использования фоновой выборки с помощью TableViewer я обнаружил только старую и неразрешенную ошибку платформы по этой теме. Но я сомневаюсь, что предложенное решение этой проблемы о внедрении дополнительного менеджера контента для средств просмотра таблиц было бы в любом случае очень разумным. Поэтому я решил попробовать самодельное универсальное решение, основанное на концепциях доступной конкретной реализации дерева.

Отсроченная загрузка контента с помощью JFace Viewers

Основной принцип работы с элементами модели с длительной загрузкой в ​​JFace Viewers прост. Вместо того, чтобы извлекать содержимое в IContentProvider#getElements(Object) напрямую, извлечение данных делегируется определенному адаптеру, который выполняет его в фоновом задании.

Более того, делегирующая реализация getElements(Object) возвращает заполнитель . Это показывается зрителем, пока происходит загрузка данных. В то же время собранные данные передаются на задание обновления . Последний добавляет элементы в структурированную программу просмотра. UIJob обновления является производным от UIJob поскольку доступ к UIJob SWT разрешен только из кода, выполняемого UIJob пользовательского интерфейса.

Наконец, когда фоновая загрузка завершена, задание очистки удаляет заполнитель.

Отложенную выборку содержимого не следует путать с отложенной загрузкой элементов с использованием флага SWT.VIRTUAL . Хотя оба подхода имеют сходство, виртуальная таблица и деревья обычно полезны для частичной загрузки больших наборов данных по требованию.

Отложенная загрузка полезна для наборов данных разумного размера, которые, тем не менее, могут потребовать много времени для извлечения и, следовательно, могут блокировать поток пользовательского интерфейса. Рассмотрим выборку удаленных данных, например. И если вам интересно, оба подхода, конечно, взаимоисключающие

IDeferredWorkbenchAdapter

С точки зрения разработчика, IDeferredWorkbenchAdapter — это путь. Это расширение IWorkbenchAdapter , которое в целом отвечает за «обеспечение визуального представления и иерархической структуры элементов верстака, позволяя им отображаться в пользовательском интерфейсе без необходимости знать конкретный тип элемента», — как указано в его javadoc .

Расширение объявляет дополнительные методы для поддержки отложенной выборки дочерних элементов данного элемента данных и может быть зарегистрировано фабрикой адаптеров. Рассмотрим простое pojo, которое служит элементом модели, например:

1
2
3
public class ModelElement {
  [...]
}

Чтобы абстрагировать визуальное представление и фоновую загрузку от классов домена, предоставьте соответствующую реализацию адаптера…

1
2
3
4
5
public class ModelElementAdapter
  implements IDeferredWorkbenchAdapter
{
  [...]
}

… И сопоставить оба типа вместе, используя фабрику адаптеров:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class ModelElementAdapterFactory
  implements IAdapterFactory
{
 
  @Override
  public Object getAdapter( Object adaptableObject, Class adapterType ) {
    return new ModelElementAdapter();
  }
 
  @Override
  public Class[] getAdapterList() {
    return new Class[] { ModelElement.class };
  }
}

Для получения дополнительной информации об использовании IAdaptable , IWorkbenchAdapter и IAdaptableFactory вы можете посмотреть, как я могу использовать IAdaptable и IAdapterFactory? , К сожалению, поставщики содержимого и меток IAdaptable по умолчанию ожидают, что элементы модели реализуют IAdaptable . Однако это можно обойти с помощью пользовательских провайдеров .

Следующий тестовый эскиз проверяет, что адаптация элемента работает должным образом:

01
02
03
04
05
06
07
08
09
10
11
@Test
public void testAdapterRegistration() {
  IAdapterManager manager = Platform.getAdapterManager();
  ModelElementAdapterFactory factory = new ModelElementAdapterFactory();
 
  manager.registerAdapters( factory, ModelElement.class );
  Object actual = manager.getAdapter( new ModelElement(), ModelElement.class );
 
  assertThat( actual )
    .isInstanceOf( ModelElementAdapter.class );
}

Теперь пришло время реализовать функцию извлечения данных из ModelElementAdapter . Это делается в методе fetchDeferredChildren :

01
02
03
04
05
06
07
08
09
10
@Override
public void fetchDeferredChildren(
  Object parent, IElementCollector collector, IProgressMonitor monitor )
{
  collector.add( loadData( parent ), monitor );
}
 
private Object[] loadData( Object parent ) {
  return [...]
}

Загрузка данных, loadData() много времени, очевидно, обрабатывается методом loadData() . Добавление элементов данных в IElementCollector запускает задание обновления, упомянутое выше. Как вы можете видеть, выборка данных может быть разделена на несколько этапов, и о прогрессе можно сообщить через данный IProgressMonitor .

DeferredContentManager

Последнее, что нужно сделать, — это связать механизм, описанный в этом посте, с экземпляром средства просмотра, используемым для отображения элементов модели. Для этой цели DeferredContentManager может адаптировать произвольные средства просмотра и делегировать извлечение элемента к соответствующей реализации IDeferredWorkbenchAdapter .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class ModelElementContentProvider
  implements IStructuredContentProvider
{
 
  DeferredContentManager manager;
 
  @Override
  public void inputChanged(
    Viewer viewer, Object oldInput, Object newInput )
  {
    TableViewerAdapter adapter
      = new TableViewerAdapter( ( TableViewer )viewer );
    manager = new DeferredContentManager( adapter );
  }
   
  @Override
  public Object[] getElements( Object inputElement )  {
    return manager.getChildren( inputElement );
  }
 
  [...]
}

Пользовательский IStructuredContentProvider используется для адаптации зрителя в его методе inputChanged . Реализация getElements делегирует диспетчеру контента, который, в свою очередь, делегирует загрузку элемента в адаптер элемента модели, используя DeferredContentManager#getChildren .

В то время как выборка продолжается, элемент заполнителя возвращается, чтобы показать метку «Ожидание …» во вьюере. Это ситуация, показанная на титульном изображении слева. На правой стороне поиск был завершен, и заполнитель был удален.

StructuredViewerAdapter

Из этого примера становится ясно, как DeferredContentManager может поддерживать разные типы средств просмотра. Средство просмотра адаптируется менеджером контента с использованием подходящего производного от StructuredViewerAdapter . В настоящее время доступны только адаптеры по умолчанию для просмотра абстрактных деревьев и таблиц.

Однако написать адаптеры для других типов структурированных вьюеров просто. Следующий фрагмент демонстрирует, например, реализацию ListViewer :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class ListViewerAdapter
  extends StructuredViewerAdapter
{
 
  public ListViewerAdapter( AbstractListViewer listViewer ) {
    super( listViewer );
  }
 
  @Override
  public void remove( Object element ) {
    viewer.remove( element );
  }
 
  @Override
  public void addElements( Object parent, Object[] children ) {
    viewer.add( children );
  }
}

Использование этого и замена средства просмотра таблиц средством просмотра списка в этом примере приведет к следующему результату:

Список-зритель-адаптер

Здорово! Не так ли?

Вывод

В этом посте было представлено введение в DeferredContentManager и показано, как он позволяет выполнять фоновую загрузку элементов модели с помощью различных средств просмотра JFace. И если — после всех убедительных объяснений использования, приведенных выше, — вам может быть интересно, где его взять, вы найдете его в репозитории Xiled P2. Менеджер контента является частью функции com.codeaffine.eclipse.ui :

Если вы хотите взглянуть на код или подать проблему, вы также можете взглянуть на проект Xiled GitHub: