Обновлять принтеры в Java во время работы приложения
Как видно из заголовка, я бы хотел обновить принтеры, зарегистрированные в настройках компьютера, когда запущено мое Java-приложение. Обычно я могу использовать PrinterJob.lookupPrintServices()
чтобы получить принтеры. Однако они обновляются только при перезапуске приложения. Я прочитал кое-что о том, что lookupPrintServices() должно быть сделано в новом потоке, чтобы получить принтеры. Это, однако, не сработало, список принтеров остался прежним. Следующая ссылка показывает, что эта проблема должна быть исправлена в Java 5.0, я что-то не так делаю?
Любая помощь высоко ценится!
РЕДАКТИРОВАТЬ Добавлено MWE.
public class MTPrinterTest extends Thread {
public static void main(String[] args) {
MTPrinterTest t1 = new MTPrinterTest();
t1.start();
try {
System.in.read();
} catch (Exception e){}
MTPrinterTest t2 = new MTPrinterTest();
t2.start();
}
public void run() {
PrinterJob printerJob;
PrintService[] printServices;
printerJob = PrinterJob.getPrinterJob();
printServices = printerJob.lookupPrintServices();
System.out.println("Number of servies found: " + printServices.length);
for (int i =0; i< printServices.length; i++)
System.out.println("--> Available Printer " + i + ": " + printServices[i]);
printerJob.printDialog();
}
}
5 ответов
Нет необходимости перезапускать приложение, чтобы обновить список служб печати.
Здесь я нашел решение:
/**
* Printer list does not necessarily refresh if you change the list of
* printers within the O/S; you can run this to refresh if necessary.
*/
public static void refreshSystemPrinterList() {
Class<?>[] classes = PrintServiceLookup.class.getDeclaredClasses();
for (int i = 0; i < classes.length; i++) {
if ("javax.print.PrintServiceLookup$Services".equals(classes[i].getName())) {
sun.awt.AppContext.getAppContext().remove(classes[i]);
break;
}
}
}
В основном, статический класс PrintServiceLookup.Services
ведет список услуг печати. Итак, если вы удалите этот класс из AppContext
, ты заставляешь PrintServiceLookup
создать новый экземпляр снова. Таким образом, список услуг печати обновляется.
Я сталкивался с одной и той же проблемой до и после нескольких тестов, кажется, что список принтеров снимается в начале приложения Java и не может быть обновлен после этого с помощью Java lookupPrintServices()
,
Что я сделал, чтобы решить эту проблему, так это вызвать API Winspool напрямую с помощью JNA. Если вы собираетесь это сделать, API Winspool хорошо документировано Microsoft: документация по Winspool API.
Кроме того, в этом вопросе я описал часть своего решения проблемы, с которой столкнулся несколько месяцев назад. Это может помочь вам понять JNA и Winspool API.
На основании этого решения связи , предложенной tresf проблемной ссылки и в предыдущем ответе IvanRF, я создал новый исходный код , который является слиянием обоего кодов.
Целью этого кода помимо решения проблемы является устранение предупреждения:
AppContext - это внутренний проприетарный API, который может быть удален в будущем выпуске.
public static boolean refreshSystemPrinters() {
try {
for (final Thread thread : Thread.getAllStackTraces().keySet()) {
if (thread.getName().equals("PrinterListener")) {
thread.interrupt();
}
}
final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
final Object appContext = appContextClass.getMethod("getAppContext").invoke(null);
appContextClass.getMethod("put", Object.class, Object.class).invoke(appContext, javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);
javax.print.PrintServiceLookup.lookupPrintServices(null, null);
return true;
} catch (final Exception e) {
e.printStackTrace();
}
return false;
}
Этот код с использованием отражения Java:
final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
final Object appContext = appContextClass.getMethod("getAppContext").invoke(null);
appContextClass.getMethod("put", Object.class, Object.class).invoke(appContext, javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);
эквивалентно этому:
sun.awt.AppContext.getAppContext().put(javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);
Быстрый обходной путь для систем на основе CUPS:
System.setProperty("sun.java2d.print.polling", "false");
Предупреждение, это имеет побочный эффект постепенного замедления вызовов (например, 80ms
) чтобы PrintServiceLookup.lookupPrintServices(...)
,
В частности:
Свойство sun.java2d.print.polling
установлен в true
:
<1ms
звонитьPrintServiceLookup.lookupPrintServices(...)
Свойствоsun.java2d.print.polling
установлен вfalse
:
80ms
звонитьPrintServiceLookup.lookupPrintServices(...)
Хотя 80 мс - это очень короткий промежуток времени для большинства пользовательских интерфейсов, в сценарии с высокой нагрузкой это может повлиять на производительность.
Кроме того, это80ms
постепенно увеличивается со временем. Например, 100000 звонковPrintServiceLookup.lookupPrintServices(...)
постепенно увеличивает задержку от80ms
в1,000ms
, Длительные программы могут испытывать заметную задержку.
Тем не менее, эта задержка по-прежнему предпочтительнее, чем AppContext
Обходной путь, особенно в системах CUPS (Linux, Mac). AppContext
в других решениях возникают проблемы с JVM-потоками, которые в конечном итоге приводят к2,000ms
задержки, а иногда и до 200,000ms
задержки (ссылка: https://github.com/qzind/tray/issues/479)
Подробный отчет об ошибке: https://github.com/AdoptOpenJDK/openjdk-build/issues/1212
Я не уверен, что вы ожидали. Вот вывод для моего ноутбука с Windows Vista под управлением Java 7.
Number of servies found: 3
--> Available Printer 0: Win32 Printer : PamFax
--> Available Printer 1: Win32 Printer : Microsoft XPS Document Writer
--> Available Printer 2: Win32 Printer : CutePDF Writer
x
Number of servies found: 3
--> Available Printer 0: Win32 Printer : PamFax
--> Available Printer 1: Win32 Printer : Microsoft XPS Document Writer
--> Available Printer 2: Win32 Printer : CutePDF Writer
Услуги принтера не меняются каждые 5 минут, даже в общей сети.
Обновлено на основе комментария:
Ваше Java-приложение должно быть остановлено и запущено, чтобы получить обновленный список принтеров.
Я думаю, я не понимаю вашу среду. В местах, где я работал, все принтеры были определены в сети задолго до того, как мы написали какой-либо код. Изменения принтера были сделаны редко (я имею в виду один раз в год).