2

I would like to create a new thread in onCreate and communicate with the UI thread using post on a View. However, the posted statements never seem to be run. Here's a small example:

import android.app.Activity
import android.os.Bundle
import android.widget.TextView    
import kotlin.concurrent.*
import org.jetbrains.anko.*

class MainActivity: Activity(), AnkoLogger {
  protected override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val view = TextView(this)
    setContentView(view)

    thread() {
      info("before post")
      view.post({ info("inside post") })
      info("after post")
    }
  }
}

Looking at the log, I can only see before post and after post, but never inside post.

What am I doing wrong?

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118
Cactus
  • 27,075
  • 9
  • 69
  • 149

3 Answers3

5

The underlying issue actually has nothing to do with Kotlin.

The problem is that View.post() only schedules its work successfully on the main thread if the view is currently attached to the view hierarchy. The final attach of the view doesn't happen at the time of setContentView(). It happens some time later.

In the event that the subject view of the post() is not currently attached (as is the case shown in the problem), the View creates a RunQueue for the current thread (stored as a thread local) and schedules the work on that RunQueue.

So, the problem for the given sample is as follows. Since View.post() is not called on the main thread, it will create a new RunQueue for the current, non-main thread without checking to see if it's being directed by a Looper (as is the main thread). This means the scheduled Runnable essentially goes into a RunQueue that is not processed until a Looper starts. In the case shown here, that Looper is never started, the new thread terminates, and the work is never executed.

If the new thread with post was delayed until after the view was attached, for example, when a click is registered on it, the post will likely be able to schedule the work on the main thread. But that's not the case here since the post occurs 1) before the view is attached, and 2) on a different non-Looper thread that terminates immediately.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • What would be the correct time to start the `thread()` without relying on user interaction like the click in your example? – Cactus Apr 02 '16 at 08:08
  • 1
    I would guess during the View's onAttachedToWindow(), or a registered View.OnAttachStateChangeListener. – Doug Stevenson Apr 02 '16 at 08:11
  • Oh and BTW I never thought it'd be Kotlin-specific, but tagged it as Kotlin so that someone who looks at the minimal repro example can figure out what language that is. – Cactus Apr 02 '16 at 08:11
0

I was able to work around this by starting the thread itself from a post, i.e.

view.post({
  thread() {
    info("before post")
    view.post({ info("inside post") })
    info("after post")
  }
})

but I would like to understand why I needed to do that.

Cactus
  • 27,075
  • 9
  • 69
  • 149
0

I would recommend you to have a look at Handlers. Its a safer way of updating UI objects when performing operations on a different thread. Basically you just have to pass in the handler object to your thread in order to update the Ui

More info here http://developer.android.com/intl/pt-br/reference/android/os/Handler.html

Jefferson Tavares
  • 983
  • 1
  • 8
  • 23