Внезапно затваряне на Java приложение: как да го избегнем?
В много случаи се надявате, че потребителят ще затвори приложението по начин, който е приемлив за вас. Например, в първия случай можете да му предоставите компонент JButton, след щракване върху който се извършват необходимите финални операции и приложението директно излиза. Като алтернатива можете да окачите манипулатор на събития на прозорец, който обработва събитието windowClosing. Tomcat, от друга страна, използва специален пакетен файл, който може да бъде изпълнен, когато приложението излезе правилно. Въпреки това е добре известно, че потребителите не затварят приложенията правилно толкова често. Те могат да правят каквото искат с приложенията. В допълнение, потребителят може просто да затвори конзолата или да прекрати сесията си с операционната система, оставяйки вашето приложение отворено. В Java виртуалната машина излиза в два случая: първо, когато приложението се излезе по нормален начин, т.е. методът System.exit е бил извикан или когато е оставена последната нишка, която не е демон. Второ, когато потребителят внезапно прекрати виртуалната машина, например чрез натискане на клавишната комбинация Ctrl + C или излизане от системата, без първо да затвори работещото Java приложение.
За щастие, VM следва следната двуфазова последователност, преди да се разтовари: 1. Виртуалната машина изпълнява всички регистрирани капани за изключване, ако има зададени такива. Кукичките за изключване са нишки, които са регистрирани в класа Runtime. Всички тези капани ще бъдат стартирани и ще работят паралелно, докато не завършат работата си. 2. Виртуалната машина извиква всички дефинирани операции за финализиране (ако има подходящи). В тази статия ще разгледаме първата точка, защото тя позволява на програмиста да натовари Java Virtual Machine с необходимите операции за завършване на приложението. Капаните за изключване са просто екземпляри на класове, които наследяват от класа Thread. За да създадете такъв капан, трябва да изпълните следната последователност от действия: 1. Опишете клас, който наследява класа Thread. 2. Приложете метода run на този нов клас. Този метод съдържа кода, който ще бъде изпълнен за изключване на виртуалната машина, независимо дали приложението е прекратено нормално или не. 3. Свържете класа на куката за изключване с вашето приложение. 4. Регистрирайте кука с помощта на метода addShutdownHook на текущия екземпляр на класа Runtime. Както може би сте забелязали, не е необходимо да стартирате закачащата нишка, която току-що създадохте, тъй като бихте стартирали друг клас, който наследява Thread. Задачата за стартиране на тази нишка е оставена на виртуалната машина, която, след като се приближи до изпълнението на своята последователност на изключване, ще стартира всички регистрирани нишки на кука.
Кодът в листинг 1 въвежда прост клас Shutdown HookDemo и подклас Thread, ShutdownHook. Имайте предвид, че методът run на класа ShutdownHook просто отпечатва низа Shutting на конзолата. Разбира се, можете да вмъкнете абсолютно всеки код, който трябва да изпълните по време на завършването на вашето приложение. След стартиране на публичния клас се извиква методът start. Методът start създава кука за изключване и я регистрира с текущия екземпляр на класа Runtime. ShutdownHook shutdownHook = нов ShutdownHook(); Runtime.getRuntime().addShutdownHook(shutdownHook); След това програмата изчаква потребителят да натисне Enter. System.in.read(); Когато потребителят натисне Enter, потребителят излизапрограми. Въпреки това, преди да излезе, виртуалната машина изпълнява регистрираната кука за изключване, която от своя страна отпечатва реда „Изключване“.
Списък 1
Създайте временен файл при стартиране
За следващия пример ще вземем просто приложение Swing, чийто основен клас е MySwingApp. Това приложение създава временен файл при стартиране. Когато се затвори, файлът трябва да бъде изтрит. Кодът за това приложение е показан в списък 2.
Списък 2
При стартиране това приложение извиква метода за инициализация. Този метод от своя страна създава временен файл в текущата директория, наречен temp.txt.
Когато потребителят затвори това приложение, тогава временният файл трябва да бъде изтрит. В този случай можем само да се надяваме, че потребителят натиска бутона Exit и когато бъде натиснат, ще бъде извикан методът за изключване, който изтрива временния файл. Въпреки това, временният файл няма да бъде изтрит, ако потребителят използва системния бутон X на прозореца на приложението, за да излезе от програмата или по друг начин.
Използване на капана за изключване
Списък 3 показва вариант на същото приложение, което решава този проблем. Новата версия на приложението модифицира кода в списък 2 чрез настройка на кука за изключване. Класът кука се дефинира като вътрешен клас на основния клас на приложение. Така той получава достъп до всички полета и методи на основния клас. В листинг 3 методът run на класа кука просто извиква метода за изключване на основния клас. Това гарантира, че се извиква, когато приложението се прекрати.
Списък 3
Обърнете внимание на метода за инициализация. Първото нещо, което прави, е да създаде екземпляр на вътрешния клас MyShut-downHook, който наследява класарезба. Кука за изключване на MyShutdownHook = нов MyShutdownHook(); Сега, когато имаме екземпляр на класа MyShutdownHook, ние го регистрираме в Runtime, използвайки метода addShut downHook: Runtime.getRuntime().add ShutdownHook(shutdownHook); Останалата част от метода за инициализация е точно същата като метода в листинг 2. Тази част създава временен файл и отпечатва реда Creating temporary file.
Сега опитайте да стартирате това малко приложение Swing. Уверете се, че временният файл е изтрит във всеки случай, без значение как затваряте приложението. Куките за изключване, които разгледахме в тази статия, всъщност са единственото правилно решение на проблема с изпълнението на някакъв код при прекратяване на приложение. И тъй като не можете да сте сигурни как потребителят ще затвори вашето приложение този път, използването им ще направи живота ви много по-лесен, като гарантира, че зададените от вас правила се спазват.