Free-threaded threads can be created using the standard Java method of implementing the java.lang.Runnable interface.
The following example shows a class that implements Runnable and takes two controls (a trackbar and a label) as parameters to its constructor. From the thread's run method, it transitions to the trackbar's thread by calling the trackbar's invokeAsync method. InvokeAsync passes a delegate called tDelegate (an instance of com.ms.wfc.app.MethodInvoker) that specifies a callback method called tCallBack. Inside that method, the control's thread can safely manipulate the control's properties, in this case, changing the trackbar's tick style. This causes the trackbar's window handle to be re-created. If the thread was not transitioned as demonstrated here, the trackbar would be re-created on the new thread rather than on the thread containing the message loop; in this case, the control trackbar wouldn't receive any new messages and would fail to respond.
Example
import com.ms.wfc.app.*; import com.ms.wfc.core.*; import com.ms.wfc.ui.*; // // Runnable is the interface you need to implement to make a new // java thread. // public class RunnableClass implements Runnable { final int SLEEP = 500; Label l; TrackBar tb; // // This is the thread for our class. // Thread thread; // // Makes a special delegate so WFC can call it from the control's // thread. // MethodInvoker tDelegate = new MethodInvoker(tCallback); // // Make a new Java thread; tell it to begin running via the // start() method. // public RunnableClass (TrackBar tb, Label l) { this.l = l; this.tb = tb; thread = new Thread(this, "RunnableClass thread"); thread.start(); } public void run() { while (true) { // // Call the specified method from the label's thread. // tb.invokeAsync (tDelegate); try { Thread.sleep (SLEEP); } catch (InterruptedException e) { } } } int nCount = 0; int nTickStyles[] = {TickStyle.BOTH, TickStyle.BOTTOMRIGHT, TickStyle.NONE, TickStyle.TOPLEFT}; // // This code is executed on the trackbar's thread. // private void tCallback() { int nIndex = nCount % (nTickStyles.length); l.setText ("hello from tCallBack: " + nCount); tb.setTickStyle (nTickStyles [nIndex]); nCount++; int nValue = tb.getValue(); if (nValue >= tb.getMaximum()) tb.setValue(0); else tb.setValue (nValue + 1); } public void stopThread() { thread.stop(); } }
Exiting a thread in this case is just a matter of running the thread's stop method. In this example, the Form class that creates the RunnableClass object calls that object's stopThread method when it is disposed. The following code fragment demonstrates this.
... import RunnableClass; public class SimpleRunnable extends Form { // This is the class that implements the Runnable interface. RunnableClass runnableClass; public SimpleRunnable() { initForm(); runnableClass = new RunnableClass (tb, l); } public void dispose() { runnableClass.stopThread(); super.dispose(); components.dispose(); } Container components = new Container(); Edit eDescription = new Edit(); TrackBar tb = new TrackBar(); Label l = new Label(); private void initForm() { // Code to initialize the controls omitted... } public static void main(String args[]) { Application.run(new SimpleRunnable()); } }
Alternately, to create a new application thread without having to implement the Java Runnable interface or extend java.lang.Thread, you can use the Application.createThread method. The createThread method takes a delegate as a parameter (MethodInvoker is often used, but any delegate can be used). In this case, all logic can be contained in one class, typically the Form-based class of the application. The following example code fragment shows how this works.
Example
import com.ms.wfc.app.*; import com.ms.wfc.core.*; import com.ms.wfc.ui.*; public class SimpleAppThread extends Form { final int SLEEP = 700; Thread thread; // Specify the thread context to run a method on. MethodInvoker cbDelegate = new MethodInvoker( cbThrdCallback ); public SimpleAppThread() { initForm(); // // Creates a new thread and runs the methodInvoker method // on the new thread. The returned thread object is needed // so we can stop the thread when this form is closed (disposed). // Note that thread.start() is called automatically. // thread = Application.createThread (new MethodInvoker (this.methodInvoker)); } private void methodInvoker() { while (true) { // cbThrdCallback is called on the check box's thread. cb.invoke (cbDelegate); try { Thread.sleep (SLEEP); } catch (InterruptedException e) { } } } int nCount = 0; // // Thread callback that sets check box alignment property. // This code is to be executed on the check box's thread. // private void cbThrdCallback() { cb.setText ("threadCallback loop: " + nCount++); if (nCount % 2 == 0) cb.setTextAlign (LeftRightAlignment.LEFT); else cb.setTextAlign (LeftRightAlignment.RIGHT); } public void dispose() { thread.stop(); super.dispose(); components.dispose(); } private void cbSuspend_click(Object sender, Event e) { if (cbSuspend.getChecked()) { cbSuspend.setText ("press to resume thread"); thread.suspend(); } else { cbSuspend.setText ("press to suspend thread"); thread.resume(); } } Container components = new Container(); CheckBox cbSuspend = new CheckBox(); CheckBox cb = new CheckBox(); private void initForm() { // Code to initialize the controls here. } public static void main(String args[]) { Application.run(new SimpleAppThread()); } }
Here the thread is stopped by calling the thread’s stop method from the form's dispose method. The thread is not in a separate class; its methods can be called directly from the Form-based class.
The Application class also contains the Application.exitThread method, which closes the thread's message loop and shuts down all windows on the thread (note that it does not stop or exit the thread itself). In comparison, Application.exit closes message loops on all threads and closes all windows.