Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
Regular Contributor
Posts: 61
Registered: ‎08-15-2010
My Device: Z10
My Carrier: T-Mobile Germany
Accepted Solution

Any further optimization advice for a novice?

Hello there,

I have started with a small BB java project a few weeks ago. Now the  program is complete in functionality. And I would like to make it feel snappy.

 

The program looks up the contacts from the remote address book using email or name extracted from the address fields of a message. Some emails have many contacts in To: or CC: fields. Going through all of them obviously takes significant time on 9700 Blackberry processor API 5.0.

 

Currently it takes up to 5 seconds to go through 25 contacts. It feels like too much and I do not want to bother showing the hourglass for such a basic action. I have already checked and applied the advices from this topic http://supportforums.blackberry.com/t5/Java-Development/Aggressive-Optimization/m-p/122606#M13797

 

Can you think of further other optimization I could use for the part below?

 

Thank you for your advices!

Diego

 

 

Basically there are two methods to get emails and names from email net.rim.blackberry.api.mail.Message. Then I call them for all message field types in a "copypaste" style loop. This information is shown on to a user and program acts on his choice.

 

    private void addAddressChoiceField(int currentType, String currentTypeString, final net.rim.blackberry.api.mail.Message message){
try {
int numberOfRecipients = message.getRecipients(currentType).length;
if(numberOfRecipients>0){ //not adding empty fields at all
String[] addressList = new String[numberOfRecipients];
for (int i=numberOfRecipients-1 ;i>=0; i-- ){ //this iteration style should speed up a little
addressList[i]= message.getRecipients(currentType)[i].getAddr();
}
ClickableObjectChoiceField ocf = new ClickableObjectChoiceField(currentTypeString,addressList);
this.add(ocf);
ocf.setChangeListener(this);
}
} catch (Exception e) {
Dialog.alert(e.toString());
}
}

private void addNameChoiceField( int currentType, String currentTypeString, final net.rim.blackberry.api.mail.Message message){
try {
int numberOfRecipients = message.getRecipients(currentType).length;
if(numberOfRecipients>0){ //not adding empty fields at all
int namesListLength = 0;
String[] interimNamesList = new String[numberOfRecipients];
for (int i=numberOfRecipients-1 ;i>=0; i-- ){ //this iteration style should speed up a little
if( message.getRecipients(currentType)[i].getName() instanceof String) { // i.e. not null
interimNamesList[namesListLength++]= message.getRecipients(currentType)[i].getName() ;
}
}
String[] addressList= new String[namesListLength];
for (int i=namesListLength-1; i>=0; i-- ){ addressList[i]= interimNamesList[i]; } // now addressList should not have any null strings
ClickableObjectChoiceField ocf = new ClickableObjectChoiceField(currentTypeString,addressList);
this.add(ocf);
ocf.setChangeListener(this);
}
} catch (Exception e) {
Dialog.alert(e.toString());
}
}

public void update(final net.rim.blackberry.api.mail.Message message) {
String from_address;
try { from_address= message.getFrom().getAddr(); } catch (Exception ex) { from_address=""; }
user_input = new EditField("Lookup: ",from_address );
//TODO substitute with the pop-up text field like in the vanilla lookup from the address book.
user_input.setBorder(BorderFactory.createRoundedBorder( new XYEdges(10,10,10,10)));
add(user_input);
add(new RichTextField("Chose from Contact Address(es):"));
// using the predefined method in a copypaste loop through email address fields
addAddressChoiceField(Message.RecipientType.FROM, "From:", message);
addAddressChoiceField(Message.RecipientType.TO, "To:", message);
addAddressChoiceField(Message.RecipientType.CC, "CC:", message);
addAddressChoiceField(Message.RecipientType.BCC, "BCC:", message);
addAddressChoiceField(Message.RecipientType.REPLY_TO, "Reply_To:", message);
addAddressChoiceField(Message.RecipientType.SENDER, "Sender:", message);
add(new RichTextField("Chose from Contact Name(s)"));
addNameChoiceField(Message.RecipientType.FROM, "From:", message);
addNameChoiceField(Message.RecipientType.TO, "To:", message);
addNameChoiceField(Message.RecipientType.CC, "CC:", message);
addNameChoiceField(Message.RecipientType.BCC, "BCC:", message);
addNameChoiceField(Message.RecipientType.REPLY_TO, "Reply_To:", message);
addNameChoiceField(Message.RecipientType.SENDER, "Sender:", message);
UiApplication.getUiApplication().pushScreen(this);
}

 

Here is the ObjectChoiceField just in case it matters.

 package com.diegor.contextLookup;



import net.rim.device.api.ui.component.ObjectChoiceField;

import net.rim.device.api.system.KeypadListener;



final public class ClickableObjectChoiceField extends ObjectChoiceField implements KeypadListener {





public ClickableObjectChoiceField() {

super();

// TODO Auto-generated constructor stub

}



public ClickableObjectChoiceField(String label, Object[] choices,

int initialIndex, long style) {

super(label, choices, initialIndex, style);

// TODO Auto-generated constructor stub

}



public ClickableObjectChoiceField(String label, Object[] choices,

int initialIndex) {

super(label, choices, initialIndex);

// TODO Auto-generated constructor stub

}



public ClickableObjectChoiceField(String label, Object[] choices,

Object initialObject) {

super(label, choices, initialObject);

// TODO Auto-generated constructor stub

}



public ClickableObjectChoiceField(String label, Object[] choices) {

super(label, choices);

// TODO Auto-generated constructor stub



}



public boolean navigationUnclick(int status, int time) {

this.getChangeListener().fieldChanged(getOriginal(), getIndex());

return super.navigationClick(status, time);

}



}
Developer
Posts: 16,645
Registered: ‎07-29-2008
My Device: Z10 LE, Z30, Passport
My Carrier: O2 Germany

Re: Any further optimization advice for a novice?

 

you should consider moving your processing to a separate thread.

----------------------------------------------------------
feel free to press the like button on the right side to thank the user that helped you.
please mark posts as solved if you found a solution.
@SimonHain on twitter
Developer
Posts: 19,623
Registered: ‎07-14-2008
My Device: Not Specified

Re: Any further optimization advice for a novice?

My quick review of your code suggests that you are adding a load of Fields to the screen.  If you do this processing but don't add the Fields to the screen, does it run significantly faster?  I would test this to see if it is the searching that you need to optimize or the display.  I strongly suspect that it is the "display" that is causing the slow down, in whihc case you shoul dlook to use something like a ListField to display your data. 

Regular Contributor
Posts: 61
Registered: ‎08-15-2010
My Device: Z10
My Carrier: T-Mobile Germany

Re: Any further optimization advice for a novice?

Simon, thanks for attention!

Could you clarify? Where should the threads part?

Here the loop starts:

1.I get the Message object and get all addresses in the "To:" field. (no global look up here)

2.Then I add them to one ObjectChoiceField

3. and then I add the ObjectChoiceField to the Screen.

here I go to the next email field, e.g. "CC:"

4. after I am done with all fields and all ObjectChoice fields in the Message I push the screen.

Then the user can send the global look up.

So which part would you put in a new thread?

 

I need to go through all the message address fields before I can show the next screen.

Developer
Posts: 16,645
Registered: ‎07-29-2008
My Device: Z10 LE, Z30, Passport
My Carrier: O2 Germany

Re: Any further optimization advice for a novice?

 

as soon as code takes a long time to run it should be considered to move the code to a thread.

 

peters assumption has its merits though, maybe you just add a lot of fields.

gather your data first, and add the fields afterwards. sprinkle some sysouts with currenttimemilis and you will see what takes so long.

----------------------------------------------------------
feel free to press the like button on the right side to thank the user that helped you.
please mark posts as solved if you found a solution.
@SimonHain on twitter
Regular Contributor
Posts: 61
Registered: ‎08-15-2010
My Device: Z10
My Carrier: T-Mobile Germany

Re: Any further optimization advice for a novice?

[ Edited ]

I have rewritten the code to process the data first. It runs a bit faster now. I am not sure if it worth spending time on the further optimisation and introducing threads. I'll probably get back to this advice after collecting feedback from actual users. BTW here is the app with the source code http://kobylkin.com

Also it would be great to have your comment on the approach with the iteration arrays, that I create in the constructor of the class.


final class ChooseContactScreen extends MainScreen implements ObserverInterface , FieldChangeListener{
//this screen should be displayed after the menu item "Lookup Contact" is selected
private EditField user_input;
// to be able to use the "for" cycle
int[] recipientTypeIterator= { //TODO change to final
Message.RecipientType.FROM,
Message.RecipientType.TO,
Message.RecipientType.CC,
Message.RecipientType.BCC,
Message.RecipientType.REPLY_TO,
Message.RecipientType.SENDER,
};
int recipientTypeListLength = recipientTypeIterator.length;
String[] fieldNameIterator= { //TODO change to final
"From:",
"To:",
"CC:",
"BCC:",
"Reply_To:",
"Sender:",
};
public ChooseContactScreen() {
super();
}
...
public void update(final net.rim.blackberry.api.mail.Message message) {
...
  //making this in two steps to prepare for the multithreading and MVC paradigm         

String[/*field type*/][/*address or name*/][/*members*/] addressList = new String[recipientTypeListLength][2][/*different for all fields*/];

for (int i=recipientTypeListLength-1;i>=0;i--) {//this iteration style should speed up a little

   try {
    int numberOfRecipients = message.getRecipients( recipientTypeIterator[i] ).length;
    if(numberOfRecipients>0) {
     addressList[i] = new String[2][numberOfRecipients];
     for (int j=numberOfRecipients-1 ;j>=0; j-- ){ //this iteration style should speed up a little
      addressList[i][0][j]= message.getRecipients(recipientTypeIterator[i])[j].getAddr();
      addressList[i][1][j]= message.getRecipients(recipientTypeIterator[i])[j].getName();
     }       
    }
   }
catch (MessagingException e) {
   // if the above functionality to be placed in an own method here we should just return null instead  
    this.add(new RichTextField("ERROR:Problem processing address fields from the message!"));   
    UiApplication.getUiApplication().pushScreen(this);    
    return;  
   }
  }
   

  add(new RichTextField("Chose from Contact Address(es):"));   

 
  for (int i=0;i<recipientTypeListLength;i++) {  
   if ( addressList[i][0] instanceof String[]  ) { // not null
    ClickableObjectChoiceField ocf;  
    this.add( ocf  = new ClickableObjectChoiceField(fieldNameIterator[i],addressList[i][0] ));         
    ocf.setChangeListener(this);  
   };
  }

 
  add(new RichTextField("Chose from Contact Name(s)"));

 
  for (int i=0;i<recipientTypeListLength;i++) {  
   if ( addressList[i][1] instanceof String[]  ) { // not null
    for(int k=( addressList[i][1].length-1);k>=0;k--){
     if( ! (addressList[i][1][k] instanceof String) ) Arrays.removeAt(addressList[i][1],k); // remove null members, implicitly using the reverse cycle order
    }
    ClickableObjectChoiceField ocf;  
    this.add(ocf  = new ClickableObjectChoiceField(fieldNameIterator[i],addressList[i][1] ));         
    ocf.setChangeListener(this);  
   };
  }
   
  UiApplication.getUiApplication().pushScreen(this);    
 }
 

 

Regular Contributor
Posts: 61
Registered: ‎08-15-2010
My Device: Z10
My Carrier: T-Mobile Germany

Re: Any further optimization advice for a novice?

[ Edited ]

Man, this forum layout is awful. Can RIM get a proper formatter for the _developers_ forum that does not screw code formatting this much?

Developer
Posts: 19,623
Registered: ‎07-14-2008
My Device: Not Specified

Re: Any further optimization advice for a novice?

I have one more suggestion.  Rather than using this.add(...) to add each field directly to your screen, which might cause a layout for every field, why not add these fields to a manager, and when you have finished, do a this.add(..) for your manager - means only one layout for all your Fields. 

 

Just a thought. 

Regular Contributor
Posts: 61
Registered: ‎08-15-2010
My Device: Z10
My Carrier: T-Mobile Germany

Re: Any further optimization advice for a novice?

[ Edited ]

Here is the updated version of the code.I have replaced multiple ObjectChoiceFields with a single ObjectListField.

 

final class ChooseContactScreen extends MainScreen implements ObserverInterface {
// This screen should be displayed after the menu item "Lookup Contact" is
// selected. It is the only visible screen of this app.

private String lookupString = "Error in ContextLookup! Choose a contact first!";
// some help to be able to use the "for" cycle for message fields extraction
final int[] recipientTypeIterator = { Message.RecipientType.FROM,
Message.RecipientType.TO, Message.RecipientType.CC,
Message.RecipientType.BCC, Message.RecipientType.REPLY_TO,
Message.RecipientType.SENDER, };
final int recipientTypeListLength = recipientTypeIterator.length;
String[] fieldNameIterator = { "From:", "To:", "CC:", "BCC:", "Reply_To:",
"Sender:", };
final ObjectListField contactsOLF = new ObjectListField();

public ChooseContactScreen() {
super();
this.setTitle("Select Contact To Look Up");
}

// SKIPPED menu items definition

 final public void update(final net.rim.blackberry.api.mail.Message message) {
// First we collect all Addresses and Names from the message.
String[] contacts1dimArray = new String[0];
for (int i = recipientTypeListLength - 1; i >= 0; --i) {
// this iteration style should speed up a little
try {
int numberOfRecipients = message
.getRecipients(recipientTypeIterator[i]).length;
if (numberOfRecipients > 0) {
for (int j = numberOfRecipients - 1; j >= 0; --j) {
String name = message
.getRecipients(recipientTypeIterator[i])[j]
.getName();
Arrays
.add(
contacts1dimArray,
(name instanceof String) ? name
// not null
: message
.getRecipients(recipientTypeIterator[i])[j]
.getAddr());

}
}
} catch (MessagingException e) {
contacts1dimArray[0] = "Error retrieving message contacts!";
return;
}
}

Arrays.sort(contacts1dimArray, new StringComparator());

contactsOLF.set(contacts1dimArray);
// a few fields, that are of no interest
...
final HorizontalFieldManager hm = new HorizontalFieldManager();

this.add(contactsOLF);
this.add(emptyLine);
this.add(copyright);
hm.add(feedback);
hm.add(license);
this.add(hm);

UiApplication.getUiApplication().pushScreen(this);
}

final public boolean onClose() {
UiApplication.getUiApplication().popScreen(this);
this.deleteAll();
return true;
}

}

I have optimized a lot out of it. Especially in the part that gets contact information out of the message. The retrieving of more than 10 contacts still can take up to 5+ seconds. It's 4 seconds to much :-)

Can you suggest where the multythreading coud be employed in that first "for" cycle in "update" method? The app needs to wait for this cycle to complete before painting the screen anyway. So what advantage could multythreading have?


 

Contributor
Posts: 44
Registered: ‎06-07-2010
My Device: 9630
My Carrier: Verizon

Re: Any further optimization advice for a novice?

Within your loop, I noticed that you're calling message.getRecipients(...) frequently.  Not sure how long each call takes, but you can certainly optimize your code by calling that once by setting it to a local variable within each iteration of the loop, then using that variable instead.