I am using a ListField Control to display data returned from xml webservice. I want to refresh the ListField or the screen every minute to update the ListField with new records or data.
I tried using the code below but it is not working properly (It is hanging).
public MyApp() {
// Push a screen onto the UI stack for rendering.
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(new MyScreen());
}
},5000,true);
}
ResponseHandler handler = new ResponseHandler();
ListField listUsers = new ListField(handler.getItem().size());
public MyScreen() {
setTitle("yQAforum");
//Fetch the xml from the web service
String wsReturnString = GlobalV.Fetch_Webservice("myDs");
//Parse returned xml
SAXParserImpl saxparser = new SAXParserImpl();
ByteArrayInputStream stream = new ByteArrayInputStream(wsReturnString.getBytes());
try {
saxparser.parse( stream, handler );
}
catch ( Exception e ) {
response.setText( "Unable to parse response.");
}
//Return vector sze from the handler class
listUsers.setSize(handler.getItem().size());
listUsers.setCallback(this);
listUsers.setEmptyString("No Users found", 0);
add(listUsers);
}
You are attempting to fetch data from your webservice on the UI thread. That's almost always the wrong thing to do.
The UI thread (also known as the main thread) is responsible for drawing the UI, and tracking user actions, like touches, or navigation via a trackpad/trackball. If the UI thread is blocked waiting for a remote web server to respond, it cannot service the UI.
There's a couple changes you should make:
public MyApp() {
// Push a screen onto the UI stack for rendering.
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(new MyScreen());
}
},5000,true);
}
should be changed to
public MyApp() {
// Push a screen onto the UI stack for rendering.
pushScreen(new MyScreen());
}
The MyApp() constructor will already be called on the UI thread, so there is no need to use invokeLater() to perform the pushScreen() call on the UI thread. It already will be called on the UI thread, if run from within the MyApp constructor. Also, the 5000 msec delay isn't really helpful. This will just delay the startup of your app by 5 seconds, which users will hate.
If you are trying to implement a splash screen, or something similar, when the app starts up, please search stack overflow for "BlackBerry splash screen", and I'm sure you'll find results.
Now, once your MyScreen class is created, you should take care not to fetch web service results from the UI thread. The MyScreen constructor will be run on the UI thread. If you want, you can initiate a web service request on a background thread, once the screen is shown. One way to do that is to use onUiEngineAttached():
protected void onUiEngineAttached(boolean attached) {
if (attached) {
// TODO: you might want to show some sort of animated
// progress UI here, so the user knows you are fetching data
Timer timer = new Timer();
// schedule the web service task to run every minute
timer.schedule(new WebServiceTask(), 0, 60*1000);
}
}
public MyScreen() {
setTitle("yQAforum");
listUsers.setEmptyString("No Users found", 0);
listUsers.setCallback(this);
add(listUsers);
}
private class WebServiceTask extends TimerTask {
public void run() {
//Fetch the xml from the web service
String wsReturnString = GlobalV.Fetch_Webservice("myDs");
//Parse returned xml
SAXParserImpl saxparser = new SAXParserImpl();
ByteArrayInputStream stream = new ByteArrayInputStream(wsReturnString.getBytes());
try {
saxparser.parse( stream, handler );
}
catch ( Exception e ) {
response.setText( "Unable to parse response.");
}
// now, update the UI back on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// TODO: record the currently selected, or focused, row
//Return vector sze from the handler class
listUsers.setSize(handler.getItem().size());
// Note: if you don't see the list content update, you might need to call
// listUsers.invalidate();
// here to force a refresh. I can't remember if calling setSize() is enough.
// TODO: set the previously selected, or focused, row
}
});
}
}
You'll need to add some error handling, in case the web service doesn't respond, or takes longer than a minute (you wouldn't want to be making a new request, if the last one hadn't finished).
But, this should get you started.
Note: once you fix the problem with running network code on the UI thread, you may still find that your code doesn't work. There could be problems fetching the web service data. You'll have to debug that. I am only showing you one problem with the code posted. If you still have problems with the web service fetch, post another question (with the UI thread problem fixed). Thanks.
Related
Inside my application, I'm displaying a website using BrowserField. And when each link inside the site is selected, I need to show loading screen so that the user won't feel blank.
I was able to add the loading screen inside this method
public void documentCreated(BrowserField browserField,
ScriptEngine scriptEngine, Document document)
But the problem is only when connection is established, this method will be called and so there will be a delay before the loading screen is displayed.
So I tried implementing the ProtocolController and adding the loading screen inside this method
public void handleNavigationRequest(BrowserFieldRequest request)
But still, the loading screen is displayed after a small delay (same as when it was under documentCreated method)
This is my code snippet
public void handleNavigationRequest(BrowserFieldRequest request)
throws Exception {
if (!NetworkUtil.isNetworkAvailable()) {
Dialog.inform(Strings.NETWORK_ERROR);
} else {
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
BaseScreen.showLoadingProgress(Strings.LOADING);
}
});
InputConnection ic = handleResourceRequest(request);
browserField.displayContent(ic, request.getURL());
}
}
I tried this outside the thread as well....Still the same is happening. For testing, I added a dialog inside this method and it was coming on the same time I'm clicking any link inside the site. Only this loading screen takes time to load.
Is there any way to make this happen ?
Also, the browser field is taking a bit longer to load the website compared to the native browser.
Am I missing something here ! Please help
I have tried the documentUnloading method as you suggested. But it is not getting triggered. Given below is the code snippet, could you please check what I'm doing wrong here...!!
protected void onUiEngineAttached(boolean attached) {
if (attached) {
BaseScreen.showLoadingProgress(Strings.LOADING);
}
super.onUiEngineAttached(attached);
}
try {
listener = new BrowserFieldListener() {
// Page starts loading...
public void documentCreated(BrowserField browserField,
ScriptEngine scriptEngine, Document document)
{
// show the loading screen
//showLoadingProgress(Strings.LOADING);
}
public void documentError(BrowserField browserField,
Document document) {
hideLoadingProgress();
Dialog.inform(Strings.NETWORK_ERROR);
}
public void documentAborted(BrowserField browserField,
Document document) {
hideLoadingProgress();
Dialog.inform(Strings.NETWORK_ERROR);
}
public void documentUnloading(BrowserField browserField,
Document document) {
BaseScreen.showLoadingProgress(Strings.LOADING);
}
// Page loaded
public void documentLoaded(BrowserField browserField,
Document document) {
// the document has loaded, hide loading popup ...
BaseScreen.hideLoadingProgress();
}
};
} catch (Exception ex) {
Dialog.inform(Strings.NETWORK_ERROR);
}
browserField.addListener(listener);
// add the browser field to a ui manager or screen
add(browserField);
// request the content
browserField.requestContent(URL);
I do this using the BrowserFieldListener (see BrowserFieldListener.html). It is slightly counter intuitive, but I display the loading screen in documentUnloading(), and remove it in documentLoaded(). When I first populate the BrowserField I also push the loading screen, and when the screen with the BrowserField is closed, I make sure the loading screen is popped too. So not a pretty solution, but it works for me.
And yes, in general, the BrowserField is slower than the Browser. I have not found a way round it. However one significant aspect is caching. Look for information on creating your own cache for the BrowserField - there is Thread on here and a KB article on the BB Web site. Sorry can't find them atm, will update when I do.
Update
As found by the OP, the caching article is here.
Further Update
Just to clarify two things:
You must associate the Listener with the BrowserField, using the addListener method.
Assuming you do the usual requestContent() for the initial load of your BrowserField, you will need to push the loading screen yourself because the first method in listener that will be invoked (assuming it has worked of course), will be documentLoaded().
A sample demonstrating how to use the Listener is included here:
listener sample
Hello I am trying to create a please wait screen.This screen will appear when my program requests data from web service and will hide when the process is finished.Also I want to add a time out if request process lasts longer than 90 seconds.
can anyone help or show me a guiding example about that matter.
public static void showBusyDialog() {
try
{
if (busyDialog == null) {
busyDialog = new Dialog("Please Wait", null, null, 0, Bitmap.getPredefinedBitmap(Bitmap.HOURGLASS));
busyDialog.setEscapeEnabled(false);
}
synchronized (Application.getEventLock()) {
busyDialog.show();
}
}
catch(Exception e)
{
}
}
and my hiding code is
public static void hideBusyDialog() {
try
{
if (busyDialog == null) {
// busyDialog = new Dialog("Please wait...", null, null, 0, Bitmap.getPredefinedBitmap(Bitmap.HOURGLASS));
busyDialog.setEscapeEnabled(false);
}
synchronized (Application.getEventLock()) {
busyDialog.close();
}
}catch(Exception e)
{
}
}
Many BlackBerry® smartphone applications need to wait for some network activity (or another blocking operation, which must process in the background), while still holding up the User Interface (UI) and displaying a progress indicator.
You can follow through this links
Links
Sample "Please Wait" screen - part 1
Sample "Please Wait" screen - part 2
Sample "Please Wait" screen - part 4
you can download simple examples for Please wait screen
PleaseWait1.zip 25 KB
PleaseWait2.zip 25 KB
PleaseWait3.zip 25 KB
Note :in case above Links not working then just follow following contents
There seem to be two common issues when programming this:
1) As applications are not allowed to block the Event Thread, how do they get the UI processing to wait?
2)How can the background Thread update the UI?
This article is intended to help with these issues and provide a fully functioning "Please Wait" sample Popup Screen. However, as there is quite a lot to explain, in this first article, we will just create a popup screen that will show itself, hold up the UI, and then remove itself once the background processing has finished. This does not give us any progress indication, nor does it let the user cancel the wait. These points will be covered in a followup article. But the code supplied with this article will be useful anyway, especially when the duration of the background processing is not known and the user may not cancel the processing.
First, we start with the background processing we need to run. While this could be anything, typically this will be network processing, like the following:
httpConn = (HttpConnection)Connector.open(_url + ";deviceside=true");
responseCode = httpConn.getResponseCode();
responseMessage = "Response Code: " + Integer.toString(responseCode);
To initiate this network processing, we have a MainScreen that contains
1) A BasicEditField that allows the entry of a URL
2) A RichTextField that should display the response code (or error message). Here are the important parts of that screen:
BasicEditField _requestedURLField = new BasicEditField("http://", "www.blackberry.com", 255, BasicEditField.FILTER_URL);
RichTextField _responseField = new RichTextField("<response code>", RichTextField.NON_FOCUSABLE);
We would like the MainScreen to be updated with the result. As noted above, background processing can't directly update the UI; UI updating code must be on the Event Thread. There are several ways to get a background process onto the Event Thread, see the related article for more. In this case, we will use the following code:
// Make things final so we can use them in the inner class
final String textString = responseMessage;
final RichTextField rtf = _resultField;
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
rtf.setText(textString);
}
});
Now we must define the PleaseWaitPopupScreen to be displayed while waiting.
To give the user something to look at while they are waiting, we have an animated .gif, which is diplayed using the code in the AnimatedGIFField (see related link). And, so the user knows what they are waiting for, the PleaseWaitPopupScreen is supplied with a String to display, as the following constructor shows:
private PleaseWaitPopupScreen(String text) {
super(new VerticalFieldManager(VerticalFieldManager.VERTICAL_SCROLL | VerticalFieldManager.VERTICAL_SCROLLBAR));
GIFEncodedImage ourAnimation = (GIFEncodedImage) GIFEncodedImage.getEncodedImageResource("cycle.agif");
_ourAnimation = new AnimatedGIFField(ourAnimation, Field.FIELD_HCENTER);
this.add(_ourAnimation);
_ourLabelField = new LabelField(text, Field.FIELD_HCENTER);
this.add(_ourLabelField);
}
PleaseWaitPopupScreen provides a method – showScreenAndWait(..) – which will create and display the Popup screen, run the Background processing, and then dismiss the Popup screen.
The final piece of the puzzle involves supplying showScreenAndWait(..) with the processing to run.
Java has the concept of a Runnable, which is an Object that contains a public void run() method that should be executed. In this case, we have the Connection code and screen update code, given above, that should be executed. So, this code is packaged up into a new Runnable Object, which is supplied to showScreenAndWait(..). And here is that method. Note how a new Thread is created and run.
public static void showScreenAndWait(final Runnable runThis, String text) {
final PleaseWaitPopupScreen thisScreen = new PleaseWaitPopupScreen(text);
Thread threadToRun = new Thread() {
public void run() {
// First, display this screen
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(thisScreen);
}
});
// Now run the code that must be executed in the Background
try {
runThis.run();
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException("Exception detected while waiting: " + t.toString());
}
// Now dismiss this screen
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().popScreen(thisScreen);
}
});
}
};
threadToRun.start();
}
And this is the key part of the PleaseWaitPopupScreen. Note how this code will create and display a Popup screen to the user, including an animated icon, while it is running the background processing. Input from the user is blocked by the Popup screen until the processing completes. The originating screen is updated as a result of the background processing.
Download the associated .zip archive, which contains the source included in this article.
In the next article, we will extend this code to be able to handle:
a) Status updates from the Background Thread
b) "Time to go" indication
c) Being cancelled by the BlackBerry smartphone user
Just put timer after you show busy dialog.
showBusyDialog();
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
hideBusyDialog();
}
};
timer.schedule(task, 9000);
this is for time out. If the process finishes less than 90 seconds you should call
timer.cancel();
timer = null;
task = null;
I am using a popup screen to show the update status of background uploading processes. I want to cancel the uploading in between. I am trying to achieve this either by addin a button to the pop up screen or with the physical backbutton of the device. But it seems that none of the events generated are caught by the app.
Here is how I am creating a popup screen and displaying it t user
DialogFieldManager manager = new DialogFieldManager();
//DialogFieldManager manager = (DialogFieldManager)getDelegate();
statusUpdate = new LabelField("Please Wait...");
manager.addCustomField(statusUpdate);
_gaugeField = new GaugeField("", 0, 100, 0, GaugeField.PERCENT);
manager.addCustomField(_gaugeField);
cncl_Btn = new ButtonField("Cancel",ButtonField.CONSUME_CLICK | ButtonField.FIELD_HCENTER | ButtonField.NEVER_DIRTY);
manager.addCustomField(cncl_Btn);
cancelFlag = 0;
cncl_Btn.setChangeListener(new FieldChangeListener()
{
public void fieldChanged(Field field, int context) {
// Auto-generated method stub
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
cancelFlag = 1;
//onClose();//as this method exited from application
// close();//this method gave me IllegalStateException
}
});
}
});
//BackUpScreen.this.addMenuItem(_viewItem);
popup = new PopupScreen(manager);
UiApplication.getUiApplication().pushScreen(popup);
Soon afther this line I am calling the actual upload process in a thread like this
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
//.... do other stuff I wanted done...
backUpThread = Thread.currentThread();
uploadItems();
}
});
But if I press the cancel button inside the popup screen its not responding. I checked this by adding a breakpoint inside the fieldchange listener method of button.
How can I do this in blackberry?
The call to invokeLater (int the second bit of code) causes that Runnable to be executed on the event thread. If anything you do on the event thread blocks then the UI will become unresponsive as you describe. Any calls that may block must not be run on the event thread.
I am using AttachmentDownloadManager class download method to download the email attachments to the device.I am displaying a progress screen while the attachments gets downloaded to device.In the downloadCompleted() event listener method, I am trying to stop/close the progress screen but unable to do so.
Below is the code snippet:
attachmentDownloadMngr = new AttachmentDownloadManager();
attachmentDownloadMngr.download(attachmentBodyPart, null, new IGAEmailAttachmentListener(this));
Below are DownloadProgressListener event listener methods:
public void updateProgress(Object element, int current, int total)
{
synchronized(UiApplication.getEventLock())
{
Ui.getUiEngine().pushGlobalScreen(progressScreen,1,UiEngine.GLOBAL_QUEUE);
}
}
public void downloadCompleted(Object element)
{
synchronized(UiApplication.getEventLock())
{
Ui.getUiEngine().popScreen(progressScreen);
}
}
public void downloadCancelled(Object element)
{
}
progressScreen is instance of PopupScreen where I am displaying a guagefield. The progress screen does not get any events even after the attachment download is completed.
Please help me to solve this issue.
You seem to be pushing a globalscreen every time you get a callback to updateprogress() . I wouldn't do it. I would rather push the globalscreen with the gaugefield when I start the download, and then set the value of gaugefield using gaugeField.setValue(value).
the way you are doing it would mean, the event thread pushes too many screens on to the stack, and I guess it would throw a run time exception and kill your app.
I have an app that is listening in background and when the user clicks "send" it displays a dialogue. However I need to bring my app to foreground so the user answers some questions before letting the message go. but I haven't been able to do this, this is the code in my SendListener:
SendListener sl = new SendListener(){
public boolean sendMessage(Message msg){
Dialog myDialog = new Dialog(Dialog.D_OK,
"message from within SendListener",
Dialog.OK,Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
Dialog.GLOBAL_STATUS)
{
//Override inHolster to prevent the Dialog from being dismissed
//when a user holsters their BlackBerry. This can
//cause a deadlock situation as the Messages
//application tries to save a draft of the message
//while the SendListener is waiting for the user to
//dismiss the Dialog.
public void inHolster()
{
}
};
//Obtain the application triggering the SendListener.
Application currentApp = Application.getApplication();
//Detect if the application is a UiApplication (has a GUI).
if( currentApp instanceof UiApplication )
{
//The sendMessage method is being triggered from
//within a UiApplication.
//Display the dialog using is show method.
myDialog.show();
App.requestForeground();
}
else
{
//The sendMessage method is being triggered from
// within an application (background application).
Ui.getUiEngine().pushGlobalScreen( myDialog, 1,
UiEngine.GLOBAL_MODAL );
}
return true;
}
};
store.addSendListener(sl);
App is an object I created above:
Application App = Application.getApplication();
I have also tried to invoke the App to foreground using its processID but so far no luck.
i have managed to achieve something similar to what you're describing but the difference is, my dialogs are displayed asynchronously, which might actually be easier... so in your case..
the first i could suggest you try is get the event lock before pushing the screen, ala:
synchronized(Application.getEventLock()){
final UiEngine ui = Ui.getUiEngine();
ui.pushGlobalScreen(theScreen, 1, UiEngine.GLOBAL_MODAL);
}
I would also just create a custom class of type MainScreen and push that instead of plain Dialog.
There, that's better (now with code formatting).
public class MYSendListener implements SendListener {
private UiApplication _myApp;
public MySendListener(UiApplication myApp) {
_myApp = myApp;
}
public boolean sendMessage(Message m) {
...
_myApp.requestForeground();
}
}
Cache your app instance inside your send listener when you construct it, and use that when sendMessage is fired.
Application.getApplication() only gets you the app of the calling thread.