Time for action – stepping the event loop

Let's extend the previous example to step our application when events are processed.

  1. Open jni/Types.hpp and define a new type status to represent return codes:
    #ifndef _PACKT_TYPES_HPP_
    #define _PACKT_TYPES_HPP_
    
    #include <cstdlib>
    
    typedef int32_t status;
    
    const status STATUS_OK   = 0;
    const status STATUS_KO   = -1;
    const status STATUS_EXIT = -2;
    
    #endif
  2. Create the jni/ActivityHandler.hpp header and define an "interface" to observe native activity events. Each possible event has its own handler method: onStart(), onResume(), onPause(), onStop(), onDestroy(), and so on. However, we are generally interested in three specific moments in the activity life cycle:
    • onActivate(), invoked when the activity is resumed and its window is available and focused
    • onDeactivate(), invoked when the activity is paused or the display window loses its focus or is destroyed
    • onStep(), invoked when no event has to be processed and computations can take place
      #ifndef _PACKT_ACTIVITYHANDLER_HPP_
      #define _PACKT_ACTIVITYHANDLER_HPP_
      
      #include "Types.hpp"
      
      class ActivityHandler {
      public:
          virtual ~ActivityHandler() {};
      
          virtual status onActivate() = 0;
          virtual void onDeactivate() = 0;
          virtual status onStep() = 0;
      
          virtual void onStart() {};
          virtual void onResume() {};
          virtual void onPause() {};
          virtual void onStop() {};
          virtual void onDestroy() {};
      
          virtual void onSaveInstanceState(void** pData, size_t* pSize) {};
          virtual void onConfigurationChanged() {};
          virtual void onLowMemory() {};
      
          virtual void onCreateWindow() {};
          virtual void onDestroyWindow() {};
          virtual void onGainFocus() {};
          virtual void onLostFocus() {};
      };
      #endif
  3. Enhance jni/EventLoop.hpp with the following methods:
    • activate() and deactivate(), executed when an activity availability changes
    • callback_appEvent(), which is static and routes events to processActivityEvent()

    Also, define some member variables as follows:

    • mActivityHandler observes activity events. This instance is given as a constructor parameter and requires the inclusion of ActivityHandler.hpp
    • mEnabled saves the application state when the application is active/paused
    • mQuit indicates the event loop needs to exit
      #ifndef _PACKT_EVENTLOOP_HPP_
      #define _PACKT_EVENTLOOP_HPP_
      
      #include "ActivityHandler.hpp"
      #include <android_native_app_glue.h>
      
      class EventLoop {
      public:
          EventLoop(android_app* pApplication,
                  ActivityHandler& pActivityHandler);
      
          void run();
      
      private:
          void activate();
          void deactivate();
      
          void processAppEvent(int32_t pCommand);
      
          static void callback_appEvent(android_app* pApplication,
              int32_t pCommand);
      
      private:
          android_app* mApplication;
          bool mEnabled;
          bool mQuit;
      
          ActivityHandler& mActivityHandler;
      };
      #endif
  4. Edit jni/EventLoop.cpp. The constructor initialization list itself is trivial to implement. Then, fill the android_app application context with additional information:
    • userData points to any data you want. It is the only information accessible from callback_appEvent() declared previously. In our case, this is the EventLoop instance (that is, this).
    • onAppCmd points to an internal callback triggered each time an event occurs. In our case, this is the role devoted to the static method callback_appEvent().
      #include "EventLoop.hpp"
      #include "Log.hpp"
      
      EventLoop::EventLoop(android_app* pApplication,
              ActivityHandler& pActivityHandler):
              mApplication(pApplication),
              mEnabled(false), mQuit(false),
              mActivityHandler(pActivityHandler) {
          mApplication->userData = this;
          mApplication->onAppCmd = callback_appEvent;
      }
      ...
    • Update the run() main event loop. Instead of blocking when there is no more activity event to process, ALooper_pollAll() must let the program flow continue to perform the recurrent processing. Here, processing is performed by the listener in mActivityHandler.onStep(). This behavior is obviously only needed when the application is enabled.
    • Also, allow the activity to be terminated programmatically using the AnativeActivity_finish() method.
      ...
      void EventLoop::run() {
          int32_t result; int32_t events;
          android_poll_source* source;
      
          // Makes sure native glue is not stripped by the linker.
          app_dummy();
      
          Log::info("Starting event loop");
          while (true) {
              // Event processing loop.
              while ((result = ALooper_pollAll(mEnabled ? 0 : -1,         NULL,
                      &events, (void**) &source)) >= 0) {
                  // An event has to be processed.
                  if (source != NULL) {
                      Log::info("Processing an event");
                      source->process(mApplication, source);
                  }
                  // Application is getting destroyed.
                  if (mApplication->destroyRequested) {
                      Log::info("Exiting event loop");
                      return;
                  }
              }
      
              // Steps the application.
              if ((mEnabled) && (!mQuit)) {
                  if (mActivityHandler.onStep() != STATUS_OK) {
                      mQuit = true;
                      ANativeActivity_finish(mApplication->activity);
                  }
              }
          }
      }
      ...

What just happened?

We changed our event loop to update our application, instead of blocking uselessly, when there are no more events to process. This behavior is specified in ALooper_pollAll() by its first parameter, timeout:

  • When timeout is -1, as defined previously, call is blocking until events are received.
  • When timeout is 0, call is non-blocking so that, if nothing remains in the queue, the program flow continues (the inner while loop is terminated) and makes it possible to perform recurrent processing.
  • When timeout is greater than 0, we have a blocking call, which remains until an event is received or the duration is elapsed.

Here, we want to step the activity (that is, perform computations) when it is in active state (mEnabled is true); in that case, timeout is 0. When the activity is in deactivated state (mEnabled is false), events are still processed (for example, to resurrect the activity) but nothing needs to get computed. The thread has to be blocked to avoid consuming battery and processor time uselessly; in that case, timeout is -1.

Once all pending events are processed, the listener is stepped. It can request the application to be terminated, for example, if the game is finished. To leave the application programmatically, the NDK API provides the AnativeActivity_finish() method to request activity termination. Termination does not occur immediately but after the last few events (pause, stop, and so on) are processed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.66.206