Tuesday, November 24, 2009

Building A Project On The BlackBerry

Update 2: The Netbeans link within the body died a while back. Updated instructions for 6.7 and newer are available here.

Updated: I forgot to add the main method and UiApplication section. It is now part 9 of the instructions.

While Thorsten & myself wrote build instructions for the BlackBerry in the forum/mailing list in the past they are somewhat outdated by now and aren't as easy to find using Google. So here is a step by step guide on porting to the BlackBerry devices with some explanations of the Caveats.

The standard LWUIT won't work on the Blackberry since it references API's such as 184/226, notice that LWUIT doesn't need or use these API's when they are not available! On the Blackberry device the very reference to these API's will cause the application to fail. Furthermore, as I explained in a previous post, Blackberry devices have some "issues" when using classes compiled without the RIM toolchain and LWUIT isn't compiled with the RIM toolchain.
Last but not least, the performance and user experience of the MIDP based LWUIT on Blackberry isn't very good...

The solution for all these issues is to use the RIM API's, which unfortunately means you need to create two separate versions of your application (3 if you need blackberry Storm support) .

I won't go into too many details on the BlackBerry devices, but a couple of years ago they introduced the Storm device which was their first touch screen device (featuring a click screen). Supporting the Storm requires importing classes of the API that aren't supported in older versions of RIM's API, hence Storm compatibility requires using a relatively new BlackBerry OS that most users don't have.
So you will need two builds and two blackberry Java Development Environments (JDE's) if you need to support Storm.

The instructions bellow are also geared towards the NetBeans IDE and Ant build.

LWUIT currently has two maintained BlackBerry ports, one created by Thorsten & another created by myself. Each has its advantages and you can easily go back and forth between them. Since I used NetBeans I will explain based on my port but you can easily replace the content of the src directory with Thorsten's port and it should be pretty seamless.

A standard LWUIT MIDlet should work fine on a RIM device using the ports, however RIM also offers support for a CLDC application. Using this approach tends to provide improved performance/security handling for RIM although its completely optional.

  1. Download the JDE versions, I recommend 4.2.x and 4.7.x (for Storm support).
  2. Follow the NetBeans BlackBerry knowledge base article to add the JDE versions as platforms to NetBeans (no need to follow the part about editing the build.xml).
  3. Fetch the LWUIT sources from SVN and open the BlackBerry project. Clean & Build the project (notice that you MUST clean & build, a plain build will often fail since the RIM port needs to replace some classes from LWUIT).
  4. If using storm as well, change the configuration of the Blackberry LWUIT port to "touch" and again clean & build.
  5. Download the BB ant tasks which are much easier to work with than the approach taken by the knowledge base article.
  6. Add "configurations" to the NetBeans project for BlackBerry and BlackBerryTouch. Set them to use the appropriate JDE Platforms.
  7. In the blackberry configurations add an "Ability" called RIM. This will allow you to use #ifdef RIM
    rather than #ifdef BlackBerryTouch && BlackBerry
    (yes, I can't stand #ifdef's but they are better than just copy and pasting the entire code and with RIM we don't have a choice).
  8. In the libraries for the project add both LWUIT & the appropriate blackberry project. Make sure to add the touch JAR only to the touch (Storm) configuration and the standard jar to the other BlackBerry configuration.
  9. If you are building a CLDC UiApplication add the following to your MIDlet class definition (instead of extends MIDlet):
    public MyMIDlet extends //#ifdef RIM
    net.rim.device.api.ui.UiApplication
    //#else
    //# javax.microedition.midlet.MIDlet
    //#endif
    {

    Then add this to the body of the MIDlet:
    //#ifdef Blackberry
    public static void main(String[] argv) {
    new MyMIDlet().startApp();
    }
    //#endif

    You might want to also add similar methods for notifyDestroyed/platformRequest:
    public void notifyDestroyed() {
    System.exit(0);
    }
    public void platformRequest(String s) {
    net.rim.blackberry.api.browser.Browser.getDefaultSession().displayPage(s);
    }

  10. Open the build.xml file of your NetBeans project and add the following, update the paths and names marked in bold (notice this is geared towards a CLDC UiApplication, you might need to modify this slightly for a MIDlet):
    <target name="post-init">
    <available file="${platform.home}/bin/rapc.exe" property="do.rapc">
    <available file="${platform.home}/simulator/9500.bat" property="bbtouch">
    <condition property="jpda.port" value="8000">
    <isset property="do.rapc">
    </isset>
    </condition>

    <target name="post-preprocess" depends="copyBBSources,copyBBTouchSources">
    </target>

    <typedef resource="bb-ant-defs.xml" classpath="bb-ant-tools.jar">
    <target name="bbbuild" description="blackberry build" depends="init,copyBBSources,copyBBTouchSources">
    <echo message="Compiling ${preprocessed.dir}"></echo>
    <mkdir dir="${dist.dir}/bbant">
    <rapc verbose="true" output="${name}" jdehome="${platform.home}" import="${platform.bootclasspath}" destdir="${dist.dir}/bbant/" noconvert="true">
    <src location="${basedir}/${preprocessed.dir}">
    <jdp title="Vendor" version="1" type="cldc" icon="${basedir}/src/icon.png" />
    </jdp>
    <!-- sigtool jdehome="C:\Program Files\Research In Motion\BlackBerry JDE 4.7.0" codfile="${dist.dir}/bbant/${name}.cod" password="" / -->
    <alx destdir="${dist.dir}/bbant" filename="${name}.alx">
    <application id="AppName">
    <codset>
    <fileset dir="${dist.dir}/bbant" includes="*.cod">
    </fileset>
    </codset>
    </application>

    <mkdir dir="${dist.dir}/bbant-final">
    <jadtool description="desc" id="appId" input="${dist.dir}/${dist.jad}" destdir="${dist.dir}/bbant-final">
    <fileset dir="${dist.dir}/bbant" includes="*.cod">
    </fileset>
    </jadtool>

    <target name="copyBBTouchSources" if="bbtouch">
    <echo message="Copying blackberry touch sources"></echo>
    <copydir forceoverwrite="true" src="$%7Bproject.BlackberryPort%7D%5Cbuild%5Ctouch%5Cpreprocessed" dest="${basedir}/${preprocessed.dir}">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/M3G.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/SVGImage.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/animations/Transition3D.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/impl/midp/SVGImplementation.java">
    </touch>

    <target name="copyBBSources" if="do.rapc">
    <echo message="Copying blackberry sources ${preprocessed.dir}"></echo>
    <copydir src="$%7Bproject.LWUIT%7D%5Csrc" dest="${basedir}/${preprocessed.dir}">
    <copydir forceoverwrite="true" src="$%7Bproject.BlackberryPort%7D%5Cbuild%5Cpreprocessed" dest="${basedir}/${preprocessed.dir}">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/M3G.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/SVGImage.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/animations/Transition3D.java">
    <touch file="${basedir}/${preprocessed.dir}/com/sun/lwuit/impl/midp/SVGImplementation.java">
    </touch>

    <target name="post-jar" if="do.rapc">
    <rapc verbose="true" output="${name}" jdehome="${platform.home}" import="${platform.bootclasspath}" destdir="${platform.home}/simulator/" noconvert="true">
    <src location="${basedir}/${preprocessed.dir}">
    <jdp title="Vendor" version="1" type="cldc" icon="${basedir}/src/icon.png" />
    </jdp>
    </src>

    <target name="post-clean">
    <delete failonerror="false">
    <fileset dir="${platform.home}/simulator">
    <include name="**/${name}.*">
    </include>
    </fileset>
    </delete>
    </target>


25 comments:

  1. Thanks for this Shai! I think with regards to the ever reappearing bb questions this is really helpful!

    ReplyDelete
  2. Hi Shai,
    Thanks, this is really gr8.

    Can this be continued with a sample on how to implement the same with an BB CLDC application.

    ReplyDelete
  3. Thanx a million! It would be nice to post this on the LWUIT forum as well, which by the way is currently down :S!

    ReplyDelete
  4. Hello Shai,
    I was trying to add the ant extension in the build.xml file but gave an error. i think some of the closing tags are missing.

    Can you point out the changes that we need in the build.xml file for MIDP application?
    thanx again

    ReplyDelete
  5. Hello Shai,

    I managed to run the application on the Blackberry 9530 emulator, which worked well except for the following:

    If Display.getInstance() is called in the main thread the application doesn't work.

    I have a main menu and several sub menus consisting of buttons in a Grid layout. The icons of the submenus are not painted excepted for the selected one.


    The rest is great.

    It would be great if you have some insights regarding the above points.

    I'm using LWUIT svn version 769

    ReplyDelete
  6. The blogger system doesn't work well with the XML text so I had to manually do the whole "escaping" of gt/lt... Took me ages...

    Display.getInstance() won't work before Display.init() is invoked.

    To create a MIDlet just replace the "cldc" text with MIDlet in the build.

    Images might cause problems with the blackberry compiler since it changes PNG files, when packaged in resource files this doesn't happen.

    ReplyDelete
  7. Display.init() followed by getInstance() works for me, I have no idea why you are running into a problem there.
    No idea about the icon issue, maybe its a blackberry problem. Try their forum.

    I'll try to put a sample build.xml in the readme file in the SVN

    ReplyDelete
  8. Hi Shay.
    I got over the part where it complained about the xml not being well formed. Now it says:
    Compiling ${preprocessed.dir}
    Created dir: path_to_my_project\${dist.dir}\bbant
    (AND YES, IT ACTUALLY CREATES A FOLDER CALLED ${${dist.dir}} LITERALLY!!)
    Compiling 1 source files to ${name}.cod
    I/O Error: D:\NetBeansProjects\golden-phone_cb_g_2\${preprocessed.dir} (The system cannot find the file specified)
    D:\NetBeansProjects\golden-phone_cb_g_2\build.xml:24: Java returned: -1

    So it obviously fails to interpret ${name},${preprocessed.dir} and so on, it uses them literally. Why and what can I do to fix this? Thanks.

    ReplyDelete
  9. Use verbose mode with Ant to figure what went wrong, use the echo tag etc. Look in the project properties to see that everything is defined as expected.

    ReplyDelete
  10. Property "platform.home" has not been set

    Property "dist.dir" has not been set


    and so on...
    where do I set these from?

    ReplyDelete
  11. Property "platform.home" has not been set
    Unable to find ${platform.home}\bin\rapc.exe to set property do.rapc
    Property "platform.home" has not been set
    Unable to find ${platform.home}\simulator\9500.bat to set property bbtouch
    parsing buildfile jar:file:/D:/NetBeansProjects/myproject/bb-ant-tools.jar!/bb-ant-defs.xml with URI = jar:file:/D:/NetBeansProjects/myproject/bb-ant-tools.jar!/bb-ant-defs.xml from a zip file
    Property "preprocessed.dir" has not been set
    Compiling ${preprocessed.dir}
    Property "dist.dir" has not been set
    Skipping D:\NetBeansProjects\myproject\${dist.dir}\bbant because it already exists.
    Property "name" has not been set
    Property "platform.home" has not been set
    D:\NetBeansProjects\myproject\build.xml:99: jde home must be a directory
    at ca.slashdev.bb.tasks.BaseTask.setJdeHome(BaseTask.java:56)
    at ca.slashdev.bb.tasks.RapcTask.setJdeHome(RapcTask.java:134)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.tools.ant.IntrospectionHelper$7.set(IntrospectionHelper.java:1101)
    at org.apache.tools.ant.IntrospectionHelper$AttributeSetter.setObject(IntrospectionHelper.java:1502)
    at org.apache.tools.ant.IntrospectionHelper.setAttribute(IntrospectionHelper.java:405)
    at org.apache.tools.ant.RuntimeConfigurable.maybeConfigure(RuntimeConfigurable.java:393)
    at org.apache.tools.ant.RuntimeConfigurable.maybeConfigure(RuntimeConfigurable.java:350)
    at org.apache.tools.ant.Task.maybeConfigure(Task.java:202)
    at org.apache.tools.ant.UnknownElement.configure(UnknownElement.java:196)
    at org.apache.tools.ant.UnknownElement.maybeConfigure(UnknownElement.java:163)
    at org.apache.tools.ant.Task.perform(Task.java:347)
    at org.apache.tools.ant.Target.execute(Target.java:390)
    at org.apache.tools.ant.helper.ProjectHelper2.parse(ProjectHelper2.java:179)
    at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:224)
    at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:497)
    at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:151)

    ReplyDelete
  12. sorry to keep coming back with this issue... what do you mean by "you might need to modify this slightly for a MIDlet"? could you give me a hint, be a little bit more specific? thanks and sorry for the trouble...

    ReplyDelete
  13. How can i change the Font size for the Native Font in BB?

    i use the SIZE_SMALL and the BB Implementation always return me the SIZE_NORMAL (with 20px) and if i chance the diff to a value lesser the font is still the same. I need smaller fonts :(

    ReplyDelete
  14. This is a really good news for blackberry development, today I tried to follow the step by step using your build script, but I found there are so many errors in the build.xml, could you help us update a workable build script?

    ReplyDelete
  15. Nice one . i am just fallowing thise code now these code. definitely i will makew this Application.

    ReplyDelete
  16. how to display LWUIT Command as a Menu in Blackberry.i use LWUIT1.4

    ReplyDelete
  17. Adding a command to the form makes it work like a menu.

    ReplyDelete
  18. There are better instructions here: http://lwuit.blogspot.com/2010/12/better-way-to-blackberry-and-misc.html

    I'm running on BB with a menu just fine. Did you use the native menu? I think that's broken in the blackberry but I'm not sure if we disabled it.

    ReplyDelete
  19. First thanks for your blog Shai it is basically a unique resource most of us couldnt live without! Now onto my issue:

    Ive got a midlet which I have managed to build and run on Blackberry using RIM_UI blackberry lwuit port. It works pretty nice apart from:
    - "back" commands dont appear in the menu when I hit the blackberry menu key, I can use setBackCommand instead to make it work with the blackberry back key which is fine actually - just wondering why they dont appear, the rest do..
    - if there is no commands on that screen (ie only a back command which wont get added) then the blackberry menu key brigns up an empty menu, which is misleading, ugly and pointless, is there a way to stop the menu appearing if it has nothing on it? I dont mind if the user has to hit the blackberry back key but I want the empty menu to simply not appear when they hit the blackberry menu key..
    Thanks in advance,
    Gaz.

    ReplyDelete
  20. Hi, I have built my midlet for Blackberry Storm, and it runs on the emulator - but I cant touch things to select them, on the emulator im still having to use the cursor keys to move around and enter to select things - how do I get it so I can simply touch something and it will select? Do I need to turn on touch somehow? Thanks!

    ReplyDelete
  21. OK I fixed my issue, I had to define "touch" ability, now when i click on an item it gets highlighted, but it still doesnt do the "click" to actually go into it? How do I enable it so when I click on a menu item it actually goes in, right now I still have to click on it then hit enter!
    Thanks.

    ReplyDelete
  22. The solution is to use the NewOS or NewOSSigned configuration for touch support since RIM broke compatibility for touch in their 4.7 OS.
    About the menu issue I have no idea, did you change the command behavior settings?

    ReplyDelete
  23. Hey can you tell me what exactly is different in NewOS and NewOSSigned that makes the click work? is it just touch ability? ive already added that but still no click..

    ReplyDelete
  24. NewOS/Signed compile additional files from the tree that are filtered out in the default configuration.

    ReplyDelete