標籤:而且 let exit 設計 ram broadcast 生命週期 getc ica
在前面的幾篇文章,分析的都是Juce庫裡面Core模組的記憶體部分,除了骨灰級C++愛好者之外,貌似大家對這些都不是非常感興趣。相信大家更想知道Juce是怎麼用於產品開發,而對於它的構成不是非常感興趣。天天寫一些記憶體、指標、線程之類的文章。Skilla也厭倦了。這次來分析一下Juce的上層應用程式架構。
以下上一段Demo裡的程式碼片段
class JuceDemoApplication : public JUCEApplication{public: JuceDemoApplication() {} //============================================================================== void initialise (const String& commandLine) override { if (invokeChildProcessDemo (commandLine)) return; Desktop::getInstance().setOrientationsEnabled (Desktop::allOrientations); // Do your application‘s initialisation code here.. mainWindow = new MainAppWindow(); } void shutdown() override { // Do your application‘s shutdown code here.. mainWindow = nullptr; } //============================================================================== void systemRequestedQuit() override { // This gets called when the OS wants our app to quit. You may want to // ask the user to save documents, close windows, etc here, but in this // case we‘ll just call quit(), which tells the message loop to stop and // allows the app to (asynchronously) exit. quit(); } //============================================================================== const String getApplicationName() override { return "JuceDemo"; } const String getApplicationVersion() override { return ProjectInfo::versionString; } bool moreThanOneInstanceAllowed() override { return true; } void anotherInstanceStarted (const String& /*commandLine*/) override { }private: ScopedPointer<MainAppWindow> mainWindow;};//==============================================================================// This macro generates the main() routine that starts the app.START_JUCE_APPLICATION(JuceDemoApplication)
假設你看過Juce裡面的Demo的話,一定對這個類很熟悉了,沒錯。這個就是JuceDemo裡的應用程式類。當你第一次見到它時,首先應該想到的是什嗎?這是我們應該能聯想到,學完Win32時初學MFC時的情境,一個很蛋疼的問題是:這玩意怎麼執行,它的WinMain函數在哪裡?第一次找MFC的WinMain函數時的感覺。相信大家都體驗過,不停的按F11,好像穿越了幾個世紀一樣,見到的都是一個一個的陌生”面孔“,上個廁所的功夫回來就能忘得一乾二淨,分析原始碼的感覺讓人感到彷徨,然而卻很刺激。相比之下Juce的刺激性小多了,對著START_JUCE_APPLICATION按F11一次就看到了WinMain。
#if JUCE_WINDOWS && ! defined (_CONSOLE) #define JUCE_MAIN_FUNCTION int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int) #define JUCE_MAIN_FUNCTION_ARGS #else #define JUCE_MAIN_FUNCTION int main (int argc, char* argv[]) #define JUCE_MAIN_FUNCTION_ARGS argc, (const char**) argv #endif #define START_JUCE_APPLICATION(AppClass) static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } extern "C" JUCE_MAIN_FUNCTION { juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); }#endif
忽略其它平台,單純看Windows部分,START_JUCE_APPLICATION等價於以下的代碼
static juce::JUCEApplicationBase* juce_CreateApplication() { return new JuceDemoApplication(); } extern "C" int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int){juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; return juce::JUCEApplicationBase::main (); }
WinMain函數真正執行的是裡面的靜態成員函數main()
int JUCEApplicationBase::main(){ ScopedJuceInitialiser_GUI libraryInitialiser; jassert (createInstance != nullptr); const ScopedPointer<JUCEApplicationBase> app (createInstance()); jassert (app != nullptr); if (! app->initialiseApp()) return app->getApplicationReturnValue(); JUCE_TRY { // loop until a quit message is received.. MessageManager::getInstance()->runDispatchLoop(); } JUCE_CATCH_EXCEPTION return app->shutdownApp();} ScopedJuceInitialiser_GUI用於建立MessageManager(訊息管理器的)單例,但同一時候也肩負著將它析構的重任,在ScopedPointer的管理下。裡面的局部對象app相同也是自生自滅。app->initialiseApp()用於應用程式的初始化,一般應用程式層的初始化和表單的建立都在這裡實現。
再以下一句代碼是MessageManager::getInstance()->runDispatchLoop();。這句代碼是寫的最短的。然而卻是執行時間最長的,它就是我們最為熟知的訊息迴圈。最後的shutdownApp則是善後處理並調用子類的shutdown成員函數,作為應用程式結束的通知,ShutdownApp返回以後WinMain函數就結束了。繼而MessageManage的單例和app對象被範圍析構掉。由此可見,這樣設計是合情合理的。這一章我們主要分析initialiseApp()看一下應用程式是怎麼初始化的。
bool JUCEApplicationBase::initialiseApp(){ #if JUCE_HANDLE_MULTIPLE_INSTANCES if ((! moreThanOneInstanceAllowed()) && sendCommandLineToPreexistingInstance()) { DBG ("Another instance is running - quitting..."); return false; } #endif // let the app do its setting-up.. initialise (getCommandLineParameters()); stillInitialising = false; if (MessageManager::getInstance()->hasStopMessageBeenSent()) return false; #if JUCE_HANDLE_MULTIPLE_INSTANCES if (multipleInstanceHandler != nullptr) MessageManager::getInstance()->registerBroadcastListener (multipleInstanceHandler); #endif return true;} 最開始的if推斷語句的目的是,推斷應用程式是否同意多個執行個體同一時候進行而且查看是否當前有執行個體正在執行,由於有的軟體是不同意”雙開“或”多開“,這裡能夠依據須要重載moreThanOneInstanceAllowed()方法。 initialise (getCommandLineParameters());的功能是擷取命令列參數,傳給子類並完畢子類的初始化操作。 if (MessageManager::getInstance()->hasStopMessageBeenSent()) return false; 這一句是推斷訊息管理器是否收到了退出訊息。 if (multipleInstanceHandler != nullptr)
MessageManager::getInstance()->registerBroadcastListener (multipleInstanceHandler);這一句是注冊進程通訊的監聽器。
這些操作完畢之後,就是訊息迴圈了。
在ApplicationBase的成員函數main()中我們看到,作者巧妙地利用了棧對象的自己主動析構原理來管理MessageManager和ApplicationBase兩個對象的生命週期,使之初始化時機合理。析構時機亦合理,這點值得借鑒。
Juce原始碼分析(九)應用程式基底類ApplicationBase