Juce源代码分析(九)应用程序基类ApplicationBase

在前面的几篇文章,分析的都是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两个对象的生命周期,使之初始化时机合理。析构时机亦合理,这点值得借鉴。

时间: 2024-10-25 08:26:42

Juce源代码分析(九)应用程序基类ApplicationBase的相关文章

Hadoop源代码分析(*IDs类和*Context类)

我们开始来分析Hadoop MapReduce的内部的运行机制.用户向Hadoop提交Job(作业),作业在JobTracker对象的控制下执行.Job被分解成为Task(任务),分发到集群中,在TaskTracker的控制下运行.Task包括MapTask和ReduceTask,是MapReduce的Map操作和Reduce操作执行的地方.这中任务分布的方法比较类似于HDFS中NameNode和DataNode的分工,NameNode对应的是JobTracker,DataNode对应的是Tas

Hadoop源代码分析

关键字: 分布式云计算 Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http://research.google.com/archive/googlecluster.html Chubby:http://labs.google.com/papers/chubby.html GFS:http://labs.google.com/papers/gfs.html BigTable:http://labs.googl

Cocos2d-X3.0 刨根问底(九)----- 场景切换(TransitionScene)源代码分析

上一章我们分析了Scene与Layer相关类的源代码,对Cocos2d-x的场景有了初步了解,这章我们来分析一下场景变换TransitionScene源代码. 直接看TransitionScene的定义 class CC_DLL TransitionScene : public Scene { public: /** Orientation Type used by some transitions */ enum class Orientation { /// An horizontal or

MyBatis架构设计及源代码分析系列(一):MyBatis架构

如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBatis data mapper framework makes it easier to use a relational database with object-oriented applications. MyBatis couples objects with stored procedur

转:RTMPDump源代码分析

0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://.也提供 Android 版本. 最近研究了一下它内部函数调用的关系. 下面列出几个主要的函数的调用关系. RTMPDump用于下载RTMP流媒体的函数Download: 用于建立网络连接(NetConnect)的函数Connect: 用于建立网络流(NetStream)的函数 rtmpdump源代码

Kafka SocketServer源代码分析

Kafka SocketServer源代码分析 标签: kafka 本文将详细分析Kafka SocketServer的相关源码. 总体设计 Kafka SocketServer是基于Java NIO来开发的,采用了Reactor的模式,其中包含了1个Acceptor负责接受客户端请求,N个Processor负责读写数据,M个Handler来处理业务逻辑.在Acceptor和Processor,Processor和Handler之间都有队列来缓冲请求. kafka.network.Accepto

ftp server源代码分析20140602

当前是有些工具比如apktool,dextojar等是可以对我们android安装包进行反编译,获得源码的.为了减少被别人破解,导致源码泄露,程序被别人盗取代码,等等.我们需要对代码进行混淆,android的sdk中为我们提供了ProGrard这个工具,可以对代码进行混淆(一般是用无意义的名字来重命名),以及去除没有使用到的代码,对程序进行优化和压缩,这样可以增加你想的难度.最近我做的项目,是我去配置的混淆配置,因此研究了一下,这里分享一下. 如何启用ProGuard ant项目和eclipse

Spark SQL之External DataSource外部数据源(二)源代码分析

上周Spark1.2刚公布,周末在家没事,把这个特性给了解一下,顺便分析下源代码,看一看这个特性是怎样设计及实现的. /** Spark SQL源代码分析系列文章*/ (Ps: External DataSource使用篇地址:Spark SQL之External DataSource外部数据源(一)演示样例 http://blog.csdn.net/oopsoom/article/details/42061077) 一.Sources包核心 Spark SQL在Spark1.2中提供了Exte

【转载】linux环境下tcpdump源代码分析

linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02   原文链接   主题 Tcpdump 作者:韩大卫 @ 吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分核心代码. Tcpdump 的使用目的就是打印出指定条件的报文,即使有再多的正则表达式作为过滤条件.所以只要懂得tcpdump -nXXi eth0 的实现原理即可. 进入main之前,先看一些头文件 n