tags:

views:

428

answers:

2

I am trying to get my Blackberry application to display a custom modal dialog, and have the opening thread wait until the user closes the dialog screen.

final Screen dialog = new FullScreen();

...// Fields are added to dialog

Application.getApplication().invokeAndWait(new Runnable()
{

    public void run()
    {
        Application.getUiApplication().pushModalScreen(dialog);             
    }
});

This is throwing an Exception which says "pushModalScreen called by a non-event thread" despite the fact that I am using invokeAndWait to call pushModalScreen from the event thread.

Any ideas about what the real problem is?

Here is the code to duplicate this problem:

package com.test;

import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;

public class Application extends UiApplication {
    public static void main(String[] args)
    {
        new Application();
    }

    private Application()
    {
        new Thread()
        {
            public void run()
            {
                Application.this.enterEventDispatcher();
            }
        }.start();

        final Screen dialog = new FullScreen();
        final ButtonField closeButton = new ButtonField("Close Dialog");
        closeButton.setChangeListener(new FieldChangeListener()
        {

            public void fieldChanged(Field field, int context)
            {
                Application.getUiApplication().popScreen(dialog);
            }
        });
        dialog.add(closeButton); 
        Application.getApplication().invokeAndWait(new Runnable()
        {

            public void run()
            {
                try
                {
                    Application.getUiApplication().pushModalScreen(dialog);
                }
                catch (Exception e)
                {
                    // To see the Exception in the debugger
                    throw new RuntimeException(e.getMessage());
                }
            }
        });

        System.exit(0);
    }
}

I am using Component Package version 4.5.0.

+1  A: 

Building on Max Gontar's observation that the Exception is not thrown when using invokeLater instead of invokeAndWait, the full solution is to implement invokeAndWait correctly out of invokeLater and Java's synchronization methods:

public static void invokeAndWait(final Application application,
    final Runnable runnable)
{
    final Object syncEvent = new Object();
    synchronized(syncEvent)
    {
        application.invokeLater(new Runnable()
        {

            public void run()
            {   
                runnable.run();
                synchronized(syncEvent)
                {
                    syncEvent.notify();
                }
            }
        });
        try
        {
            syncEvent.wait();
        }
        catch (InterruptedException e)
        {
            // This should not happen
            throw new RuntimeException(e.getMessage());
        }
    }
}

Unfortunately, the invokeAndWait method cannot be overridden, so care must be used to call this static version instead.

JGWeissman
A: 

Seems as though there's a bunch of code in there that's unnecessary.

public class Application extends UiApplication { public static void main(String[] args) { new Application().enterEventDispatcher(); }

private Application()
{
    final Screen dialog = new FullScreen();
    final ButtonField closeButton = new ButtonField("Close Dialog");
    closeButton.setChangeListener(new FieldChangeListener()
    {
        public void fieldChanged(Field field, int context)
        {
            Application.getUiApplication().popScreen(dialog);
        }
    });
    dialog.add(closeButton); 

    // this call will block the current event thread
    pushModalScreen(dialog);

    System.exit(0);
}

}

Bradley