Domanda

I am building an Android application (using Scala 2.9) and am using a Thread that renders to a SurfaceView; this is for a game so it should update as often as it can. I imagine this issue is similar to other game "event loops" where the input comes from a different thread.

Here is a gross approximation of the current approach that relies on synchronization. It "works well enough", but I have general misgivings about having to use explicit synchronization and "tying up" the View/input thread.

View, "UI thread":

def View.onTouchEvent(e) { // on UI thread
  Game.handleInput(e)
}

Game, "Game Thread":

def Game.handleInput(e) = S synchronized { // on UI thread
  alterStateBasedOnInput
}
def Game.run () { // on Game thread 
  while (running) {
    S synchronized { 
      doGameStuff
    }
    View.post(someStateRelayedViaRunnable)
    yield
  }
}

Instead of explicitly using synchronization, I'd like to have something like this:

def View.onTouchEvent(e) { // on UI thread
   Game.sendMessage(e)
}
def Game.run () { // on Game thread 
  while (running) {
    processMessage
    doGameStuff
    View.sendMessage(someState) // hopefully same as Game.sendMessage
    yield
  }
}

Now, this is relatively easy to implement manually using a ConcurrentLinkedQueue or similar, but I would really not like to reinvent the wheel here. In addition, it would be nice to use such an actor/queue to post-back to the UI as well - right now I am using Android's support for posting an (asynchronous) Runnable to the UI thread.

I've briefly looked at several different actor implementations (mainly standard Scala and the Scalaz) and some different Java "message passing" libraries such as Jetlang, but most seem to use implicit threads or a thread executor service. But, in my case I wish to [run the actor and] process messages at a specific time on a specific thread. For View.sendMessage the messages should also be processed on the UI thread, but timing is not as important and can piggyback off of the Runnable execution noted above.

Then, I guess my question is, given the above:

What would be a good - as in "efficient" and idiomatic - approach to feed data between these two threads?

(I am also willing to entertain the suggestion that I completely fail to understand Scala actors and/or Scalaz actors and/or other message passing libraries; Scalaz seems like it might be able to work as I envision, but is hard for me to follow.)

È stato utile?

Soluzione

Well, while I would still like to know of a generic/reusable approach to the above, practicality calls. This can also be done by running a Looper on the game thread and then putting the game "event loop stuff" inside the IdleHandler, but I did not like that inversion ..

Here is how I have currently implemented it:

Game/Thread class:

var handler: Handler = _ // handler created on View thread

// Send Message to Looper that exists on View thread
// (Created implicitly for all UI threads.)
def publishEvent(event: OutputEvent) {
  handler.obtainMessage(0, event).sendToTarget
}

protected val queue = new ConcurrentLinkedQueue[InputEvent]

def queueEvent(event: InputEvent) { // on UI thread
  queue.add(event)
}

def processQueuedEvents() { // on game Thread
  @tailrec
  def processNextEvent {
    val event = queue.poll
    if (event ne null) {
      doStuffWithInputEvent(event)
      processNextEvent
    }
  }    
  processNextEvent
}

override def run() { // on game Thread
  while (running) {
    processQueuedEvents
    doOtherGameStuff ~ call_publishEvent ~ etc
  }
}

View class:

// Created on UI thread, passed to Game instance
// (The Looper will dispatch Messages to Handlers.)
val handler = new Handler {
  override def handleMessage(m: Message) {
    val event = m.obj
    doStuffWithOutputEvent(event)
  }
}

// on UI thread
override def onTouch(v: View, ev: MotionEvent): Boolean = {
   // safely queued, will be processed at the start of each game loop
   game.queueEvent(InputEvent(..))
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top