Blackberry - Maximum number of screens to be pushed in UI stack - memory

Do anyone know maximum how many screen we can push in an UiApplication without calling popscreen() and what will be the maximum file size of each screen class.
Thanks as Regards
Mintu

UPDATE according to Marc comment
BB Documentation
Cite from How To - Manage UI interactions:
When managing your application screens on the user interface (UI) stack, do not mismatch the pushScreen() and popScreen(). When the BlackBerry smartphone user finishes interacting with the screens, pop screens off the UI stack; otherwise, the display stack continues to grow until the BlackBerry smartphone runs out of memory. You should not use more than a few modal screens at one time, because each screen uses a thread, and you should not pop the screen only once. If you pop a screen too many times, the BlackBerry smartphone trackwheel/trackball and keyboard can become unresponsive.
Cite from What Is - TooManyThreadsError:
A single application can create up to 16 threads. The total number of threads that can exist on a BlackBerry device from all applications is 128. Therefore, if an application has already created 16 threads, the 17th attempt fails and a TooManyThreadsError exception is thrown. Similarly, if there are 128 threads already created on the BlackBerry device and an application attempts to create another thread, a TooManyThreadsError exception is thrown.
Test Application
alt text http://img52.imageshack.us/img52/5937/8300testscrcount.jpg
class PushScr extends MainScreen {
static int mScrCount = 0;
Screen mContext = this;
public PushScr() {
mScrCount++;
add(new LabelField("screen count: " + mScrCount));
add(new LabelField("threads count: " + Thread.activeCount()));
add(new LabelField("memory used: "
+ Memory.getRAMStats().getAllocated()));
}
protected void makeMenu(Menu menu, int instance) {
super.makeMenu(menu, instance);
menu.add(new MenuItem("push screen", 0, 0) {
public void run() {
Ui.getUiEngine().pushScreen(new PushScr());
}
});
menu.add(new MenuItem("push modal screen", 0, 0) {
public void run() {
Ui.getUiEngine().pushModalScreen(new PushScr());
}
});
if (mScrCount > 1) {
menu.add(new MenuItem("pop screen", 0, 0) {
public void run() {
Ui.getUiEngine().popScreen(mContext);
}
});
}
}
}
Conclusion
So in case of modal screens max count is 16 and common size limited by device jvm memory.
Otherwise it's all about device SRAM memory amount.

Related

BackgroundFetch in Codename One

I'm developing a Codename One app for iOS and I'm trying to use the BackgroundFetch interface.
I copied the sample code as it is written in Javadoc (https://www.codenameone.com/javadoc/com/codename1/background/BackgroundFetch.html) and I added the ios.background_modes=fetch build hint.
Launching the app on the simulator, the background operation is correctly executed.
Launching it on a real device (iPhone 7s, iOs 12.1.4), the behaviour is unpredictable. Despite the setPreferredBackgroundFetchInterval(10), I noticed almost every time I launch the app, the background operation is not executed. Rarely, the background operation is executed, but the app must be in background some minutes before to resume it, instead of 10 seconds, as set through the setPreferredBackgroundFetchInterval(10) method.
The Display.isBackgroundFetchSupported() method returns true.
I don't understand how to make if affordable and predictable.
EDIT
I modified the sample code, only in the performBackgroundFetch() implementation (the Display.setPreferredBackgroundFetchInterval(10) is not changed). I just put some text in the label:
#Override
public void performBackgroundFetch(long deadline, Callback<Boolean> onComplete) {
supported.setText("deadline: " + deadline + "; timeMillis: " + System.currentTimeMillis());
onComplete.onSucess(Boolean.TRUE);
}
I observed two different behaviours for simulator and real device.
In simulator, the method is executed exactly 10 seconds after entering in pause status. In real device, the method isn't executed 10 seconds after entering in pause status: in some cases, it's executed after 20 minutes (in other cases, it's not executed at all).
However, in both cases, I could calculate the difference between the deadline and the time when the method executed: it's always 25 minutes.
As an example, you can see the following screenshot of the app (running on iPhone):
Deadline = 1560246881647
Timestamp = 1560245381647
Deadline - Timestamp = 1500000 ms = 1500 s = 25 minutes.
As I understood, on iOS, there is a limit of 30 seconds to perform background fetches, otherwise the OS will kill the app. Moreover, the Display.setPreferredBackgroundFetchInterval() is used to set the preferred time interval between background fetches, but it's not guaranteed, as iOS keeps the control over the execution of background fetches.
What is the right way to use background fetch?
Here is the complete code:
public class MyApplication implements BackgroundFetch{
private Form current;
private Resources theme;
List<Map> records;
Label supported;
// Container to hold the list of records.
Container recordsContainer;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
}
public void start() {
if(current != null){
// Make sure we update the records as we are coming in from the
// background.
updateRecords();
current.show();
return;
}
Display d = Display.getInstance();
// This call is necessary to initialize background fetch
d.setPreferredBackgroundFetchInterval(10);
Form hi = new Form("Background Fetch Demo");
hi.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
supported = new Label();
if (d.isBackgroundFetchSupported()){
supported.setText("Background Fetch IS Supported");
} else {
supported.setText("Background Fetch is NOT Supported");
}
hi.addComponent(new Label("Records:"));
recordsContainer = new Container(new BoxLayout(BoxLayout.Y_AXIS));
//recordsContainer.setScrollableY(true);
hi.addComponent(recordsContainer);
hi.addComponent(supported);
updateRecords();
hi.show();
}
/**
* Update the UI with the records that are currently loaded.
*/
private void updateRecords() {
recordsContainer.removeAll();
if (records != null) {
for (Map m : records) {
recordsContainer.addComponent(new SpanLabel((String)m.get("title")));
}
} else {
recordsContainer.addComponent(new SpanLabel("Put the app in the background, wait 10 seconds, then open it again. The app should background fetch some data from the Slashdot RSS feed and show it here."));
}
if (Display.getInstance().getCurrent() != null) {
Display.getInstance().getCurrent().revalidate();
}
}
public void stop() {
current = Display.getInstance().getCurrent();
if(current instanceof Dialog) {
((Dialog)current).dispose();
current = Display.getInstance().getCurrent();
}
}
public void destroy() {
}
/**
* This method will be called in the background by the platform. It will
* load the RSS feed. Note: This only runs when the app is in the background.
* #param deadline
* #param onComplete
*/
#Override
public void performBackgroundFetch(long deadline, Callback<Boolean> onComplete) {
supported.setText("deadline: " + deadline + "; timeMillis: " + System.currentTimeMillis());
onComplete.onSucess(Boolean.TRUE);
}
}
The setPreferredBackgroundFetchInterval javadoc states:
Sets the preferred time interval between background fetches. This is only a preferred interval and is not guaranteed. Some platforms, like iOS, maintain sovereign control over when and if background fetches will be allowed. This number is used only as a guideline.

Refresh Screen to Update ListField with new Data

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.

Loading images asynchronously on ListField Blackberry

How to display images fetched from url on listfield asynchronously? I am getting exception when i run the code. Here is my code. I am getting Uncaught NoClassFoundError
private Bitmap getBitmap(final String strEventCode)
{
if(hst.containsKey(strEventCode))
return (Bitmap) hst.get(strEventCode);
else
{
Thread t = new Thread(new Runnable()
{
public void run()
{
Bitmap bmp = HttpUtils.getBitmap(strHalfUrl+strEventCode+".jpg");
hst.put(strEventCode, bmp);
}
});
t.start();
}
return null;
}
I draw image using following code using ListFieldCallBack:
class ListCallBack implements ListFieldCallback
{
public void drawListRow(final ListField list, final net.rim.device.api.ui.Graphics g, final int index, final int y, final int w)
{
Event objEvent = (Event) eventData.elementAt(index);
if(list.getSelectedIndex() == index)
{
g.setColor(Color.LIGHTGRAY);
g.fillRect(0, y, getWidth(), getHeight());
}
Bitmap bmp = getBitmap(objEvent.getStrName());
if(bmp==null)
g.drawBitmap(0, y+5, loadingImage.getWidth(),loadingImage.getHeight(),loadingImage, 0, 0);
else
g.drawBitmap(0, y+5, bmp.getWidth(),bmp.getHeight(),bmp, 0, 0);
g.setColor(Color.BLACK);
int yPos = y + list.getRowHeight() - 1;
g.drawLine(0, yPos, w, yPos);
//final Bitmap b=(Bitmap)myImages.elementAt(index);
//g.drawBitmap(0, y+5, b.getWidth(),b.getHeight(),b, 0, 0);
}
public Object get(ListField list, int index)
{
return eventData.elementAt(index);
}
public int getPreferredWidth(ListField list)
{
return Display.getWidth();
}
public int indexOfList(ListField listField, String prefix, int start)
{
return eventData.indexOf(prefix,start);
}
}
I'd change a few things:
In BB, there's a max number of threads you can spawn, and it is <20 I think. You are spawning a thread per image so soon or later you'll encounter a TooManyThreadsException. You should instead have a single worker thread in your screen that downloads images. You could use a consumer-producer pattern: the user, as he scrolls down, generates image download requests (producer), and the worker thread (consumer) enqueues them to be processed. You can use a LIFO structure custom made from a Vector, and wait on it while there are no requests.
You should provide a default image to show while the real image hasn't been downloaded yet.
I'm almost sure your current code is not thread safe concerning to the hst hashtable. You should ensure your consumer is thread safe. There are plenty of examples in the net about how to do this.
Of course the worker thread should terminate when the user changes screen. In your current code, threads remain alive and will try to update the hashtable even if the user has changed to another screen.
As an optimization, if you know in advance how many threads your app usually has (without counting the image thing), and you know the max threads limit isn't going to be exceeded, you could have a thread pool of, let say, 5 threads to download images instead of a single worker thread. When all 5 threads are busy, you'll start enqueuing requests. You can also add a max time per request mechanism to prevent a thread being busy with a failed download, so instead of timing out at 2 minutes, time out at 30s and make a second request.
What is HttpUtils.getBitmap()? If it is Java code written for Java-SE, then it will not work well on a BlackBerry, as BlackBerry devices only support Java-ME, which has substantially less capability than a modern Java-SE runtime.
As for the async loading, you need to pass an event back to the UI once the fetch is complete. If you are fetching many photos at once, you will also want to add some sort of batching to that event, as sending an event for each photo can overrun the event queue on the UI app.

BlackBerry Please Wait Screen with Time out

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;

Navigate one screen to another screen based on time period in BlackBerry app

For my Blackberry application I am using a start up screen with progress bar. I am filling the progress bar using a timer and after the progress bar is complete, I need to navigate to another screen.
I am checking like this, where 'i' is time, increasing from 0 to 100.
timer.cancel();
if(i>=99)
UiApplication.getUiApplication().pushScreen(new TipCalculatorScreen());
This code is not working.
For progress bar I am using code like this:
private GaugeField percentGauge;
percentGauge = new GaugeField(null, 0, 100,50, GaugeField.PERCENT);
timer=new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
int i=0;
public void run() {
percentGauge.setValue(i);
i++;
if(i>=99)
{
timer.cancel();
//for page navigating i am given like this here screen is not navigating getting error
UiApplication.getUiApplication().pushScreen(new nextscreen());
}
}
}, 100,100);
You need to make changes to the UI on the UI thread. The TimerTask is executing on its own thread. Instead of
UiApplication.getUiApplication().pushScreen(new nextscreen());
you should use
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication()..pushScreen(new nextscreen());
}
});
The update to your gauge control probably needs the same treatment.

Resources