Skip to content

Commit

Permalink
perform GUI construction on event dispatch thread, not on main thread
Browse files Browse the repository at this point in the history
noticed the issue thanks to the changes in ReactiveX/RxJava#883
  • Loading branch information
samuelgruetter committed Sep 14, 2014
1 parent 12dd238 commit ae9315d
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 67 deletions.
29 changes: 16 additions & 13 deletions RxJavaSwingThreads/src/main/java/RxJavaSwingThreads.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ public class RxJavaSwingThreads extends JFrame {

// all Strings ever present in textField (duplicates because of key down/up)
Observable<String> textFieldContents;

public static void main(String[] args) {
new RxJavaSwingThreads();
}

public RxJavaSwingThreads() {
makeLayout();
Expand All @@ -53,11 +49,9 @@ public String call(KeyEvent e) {
}
});

checkNotOnUiThread("constructor");

// spams System.out
// subscriptionsTouchingOnlySystemOut();

checkOnUiThread("constructor");

subscriptionsTouchingOnlySystemOut();
subscriptionsTouchingUi();
}

Expand All @@ -70,16 +64,16 @@ public void call(String s) {
textArea.setText(textArea.getText() + "\n" + s);
}
});

// Subscription 2 (bad)
// This is BAD because UI is modified on SecondsImpl's thread instead of event dispatching thread!
seconds.subscribe(new Action1<Long>() {
public void call(Long n) {
checkOnUiThread("title-changing-BAD-deactivated");
//RxSwing2.this.setTitle("t = " + n);
//RxJavaSwingThreads.this.setTitle("t = " + n + "s");
}
});

// Subscription 3 (correct)
// The correct way to achieve what we tried above
seconds.observeOn(SwingScheduler.getInstance()).subscribe(new Action1<Long>(){
Expand Down Expand Up @@ -184,6 +178,15 @@ public void mouseEntered(MouseEvent arg0) {}
public void mouseClicked(MouseEvent arg0) {}
});
}


public static void main(String[] args) {
// Schedule a job for the event-dispatching thread: creating and showing
// this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new RxJavaSwingThreads();
}
});
}
}

35 changes: 25 additions & 10 deletions RxScalaDict/src/main/scala/RxScalaDict.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import javax.swing.JFrame
import javax.swing.JScrollPane
import javax.swing.JTextArea
import javax.swing.JTextField
import rx.schedulers.SwingScheduler
import rx.lang.scala.Observable
import rx.lang.scala.Scheduler
import rx.lang.scala.schedulers._
Expand All @@ -30,14 +29,17 @@ class Win1 extends JFrame {
event.getComponent().asInstanceOf[JTextField].getText()

val throttled = input.distinctUntilChanged.filter(_.length >= 2).throttleWithTimeout(500 millis)

throttled.subscribe(println(_))

throttled.observeOn(IOScheduler()).map(
LookupInWordNet.matchPrefixInWordNet(_)
// for SwingScheduler, there is no Scala Wrapper yet, so we have to convert explicitly
// from Java Scheduler to Scala Scheduler:
).observeOn(new Scheduler{ val asJavaScheduler = SwingScheduler.getInstance}).subscribe(
throttled
// Switch from SwingScheduler to IOScheduler:
.observeOn(IOScheduler())
// Do IO which might take much time:
.map(LookupInWordNet.matchPrefixInWordNet(_))
// Switch back from IOScheduler to SwingScheduler:
.observeOn(SwingScheduler)
.subscribe(
matches => {
ThreadLogger.log("updating text in textArea")
textArea.setText(matches.mkString("\n"))
Expand All @@ -54,14 +56,27 @@ class Win1 extends JFrame {
add(textField, BorderLayout.NORTH)
add(sp, BorderLayout.CENTER)
setSize(400, 400)
setTitle("RxScalaDict")

setVisible(true)
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
}


// For SwingScheduler, there is no Scala Wrapper yet, so we have to convert explicitly
// from Java Scheduler to Scala Scheduler:
val SwingScheduler: Scheduler = new Scheduler {
val asJavaScheduler = rx.schedulers.SwingScheduler.getInstance
}
}

object RxScalaDict extends App {
new Win1().run
object RxScalaDict {
def main(args: Array[String]): Unit = {
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = {
new Win1().run
}
});
}
}

11 changes: 9 additions & 2 deletions RxScalaKonami/src/main/scala/Konami.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ class Win1 extends JFrame {
}
}

object Konami extends App {
new Win1().run
object Konami {
def main(args: Array[String]): Unit = {
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = {
new Win1().run
}
});
}
}

11 changes: 9 additions & 2 deletions RxScalaPaint/src/main/scala/Paint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,14 @@ object MouseEventSource {
)
}

object Paint extends App {
new Win1().run
object Paint {
def main(args: Array[String]): Unit = {
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = {
new Win1().run
}
});
}
}

41 changes: 20 additions & 21 deletions RxScalaPortScan/src/main/scala/PortScanFutures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ trait Result
case class Open(s: Socket) extends Result
case class Closed(port: Int) extends Result

object PortScanFutures extends App {
object PortScanFutures {

import FutureExtensions._

Expand All @@ -34,27 +34,26 @@ object PortScanFutures extends App {
// threads of the thread pool are terminated after being idle for 60 seconds.
// Without this line, the app terminates before all open ports are printed!
implicit val ec = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool())

Observable.from(1 to 65536).flatMap(port =>
future {
try {
Open(new Socket(host, port))
} catch {
case e: Exception => Closed(port)
}
}.asObservable
).subscribe(
res => res match {
case y: Open =>
println("Port " + y.s.getPort + " is open")
y.s.close
case y: Closed =>
},
err => err.printStackTrace(),
() => println("Done.")
)
}

def main(args: Array[String]): Unit = {
Observable.from(1 to 65536).flatMap(port =>
future {
try {
Open(new Socket(host, port))
} catch {
case e: Exception => Closed(port)
}
}.asObservable).subscribe(
res => res match {
case y: Open =>
println("Port " + y.s.getPort + " is open")
y.s.close
case y: Closed =>
},
err => err.printStackTrace(),
() => println("Done."))
}
}

// Extend Future with the asObservable method
object FutureExtensions {
Expand Down
31 changes: 16 additions & 15 deletions RxScalaPortScan/src/main/scala/PortScanThreadpool.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,33 @@ import scala.language.implicitConversions
import rx.lang.scala.Observable
import rx.lang.scala.schedulers.IOScheduler

object PortScanThreadPool extends App {
object PortScanThreadPool {

// Port scanning results
trait Result
case class Open(s: Socket) extends Result
case class Closed(port: Int) extends Result

val host = "localhost"

def scanPort(port: Int): Result = try {
Open(new Socket(host, port))
} catch {
case e: Exception => Closed(port)
}

Observable.from(1 to 65536)
.flatMap(port => Observable.just(port).observeOn(IOScheduler()).map(scanPort(_)))
// convert to blocking because otherwise app may terminate before all threads of
// Schedulers.threadPoolForIO have completed their work (these threads are daemons)
.toBlocking
.foreach(res => res match {
case y: Open =>
println("Port " + y.s.getPort + " is open")
y.s.close
case y: Closed =>
})
println("Done.")

def main(args: Array[String]): Unit = {
Observable.from(1 to 65536)
.flatMap(port => Observable.just(port).observeOn(IOScheduler()).map(scanPort(_)))
// convert to blocking because otherwise app may terminate before all threads of
// Schedulers.threadPoolForIO have completed their work (these threads are daemons)
.toBlocking
.foreach(res => res match {
case y: Open =>
println("Port " + y.s.getPort + " is open")
y.s.close
case y: Closed =>
})
println("Done.")
}
}
11 changes: 9 additions & 2 deletions RxScalaTooltip/src/main/scala/Tooltip.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,14 @@ class Win1 extends JFrame {
implicit def point2javaAwtPoint(p: Point): java.awt.Point = new java.awt.Point(p.x, p.y)
}

object Tooltip extends App {
new Win1().run
object Tooltip {
def main(args: Array[String]): Unit = {
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = {
new Win1().run
}
});
}
}

11 changes: 9 additions & 2 deletions RxScalaVoltageControl/src/main/scala/VoltageControl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,14 @@ object SwingSource {

}

object VoltageControl extends App {
new Win1().run
object VoltageControl {
def main(args: Array[String]): Unit = {
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = {
new Win1().run
}
});
}
}

0 comments on commit ae9315d

Please sign in to comment.