×

Discussion Board

Results 1 to 9 of 9
  1. #1
    Regular Contributor
    Join Date
    Aug 2003
    Location
    Cracow, Poland
    Posts
    50

    Major problem with delegation to Canvas

    Hi all!

    I've been developing a Java game for the MIDP platform for a while. When I was about to port my game to other phones I didn't want to create different versions for different phones. Instead, I tried to use a generic solution and I designed a few classes.

    A typical game must have at least one class that extends Canvas (or FullCanvas in case of Nokia's phones). Therefore if porting to another device a class extending device's specific class must be refactoried.

    I designed LightCanvas class which has a reference to a Canvas (the real Canvas which is used to draw on). LightCanvas has similiar methods to Canvas class but it doesn't extend it. Every class in my game that extended Canvas or FullCanvas has been refactoried to extend LightCanvas.

    I designed an abstract factory (MIDletPlatform) with createCanvas(LightCanvas lightCanvas) method that creates a device specific class e.g. FullCanvas and wraps the lightCanvas into it.

    The LightCanvas class:
    Code:
    public abstract class LightCanvas {
    
      private Canvas canvas;
    
      protected LightCanvas() {
        canvas = MIDletPlatform.getInstance().createCanvas(this);
      }
    
      public abstract void paint(Graphics g);
    
      public void keyPressed(int keyCode) {
        //do nothing
      }
    
      public void keyReleased(int keyCode) {
        //do nothing
      }
    
      public void repaint() {
        canvas.repaint();
      }
    
      public void serviceRepaints() {
        canvas.serviceRepaints();
      }
    
       //other methods that delegate to canvas methods
       ...
    
    }
    A method from MIDletPlatform class:
    Code:
      public Canvas createCanvas(LightCanvas canvas) {
        try {
          HeavyCanvas heavyCanvas = (HeavyCanvas)Class.forName("pl.ganimedes.midp.device.nokia.NokiaCanvas").newInstance();
          heavyCanvas.setCanvas(canvas);
          return (Canvas)heavyCanvas;
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException("Cannot create heavy canvas: " + e);
        }    
      }
    Here is one of HeavyCanvases: NokiaCanvas that just delegates to a light canvas.
    Code:
    public class NokiaCanvas extends FullCanvas implements HeavyCanvas {
    
      private LightCanvas canvas;
    
      public void setCanvas(LightCanvas canvas) {
        this.canvas = canvas;
      }
    
      protected void paint(Graphics g) {
        canvas.paint(g);
      }
    
      protected void keyPressed(int keyCode) {
        canvas.keyPressed(keyCode);
      }
    
      protected void keyReleased(int keyCode) {
        canvas.keyReleased(keyCode);
      }
    
    }
    Here is a sample code:
    Code:
    class MyLightCanvas extends LightCanvas {
       public void paint(Graphics g) {
         //a code that draws something 
         ...
       }
       
       ...
    }
    
    aDisplay.setCurrent(MIDletPlatform.getInstance().createCanvas(new MyLightCanvas()));
    And everything works fine except one thing: when I call repaint() and then serviceRepaints() within my MyLightCanvas the screen isn't refreshed even though LightCanvas delegates to the real canvas. I wonder why it doesn't work... I tried on different emulators and none of them worked as expected. The only emulator that works is mine ;-) Maybe it's because the serviceRepaints is declared in Canvas as final. Or maybe the devices have a bug...

    Has anyone encountered a similar problem?

    I'm looking forward to your feedback! Thanks in advance!

  2. #2
    Regular Contributor
    Join Date
    May 2003
    Posts
    151
    bartekn wrote:
    >I designed LightCanvas class which has a reference to a Canvas
    >(the real Canvas which is used to draw on). LightCanvas has
    >blah blah blah blah blah blah blah blah blah blah to extend
    >LightCanvas.


    This approach is just too heavyweight for mobile phones - It is a much better solution to use preprocessing on your source files to include phone specific stuff.

    I use Antenna (http://antenna.sourceforge.net) which quite frankly rocks and so can do:

    //#ifdef NOKIA
    class GameCanvas extends FullCanvas
    //#else
    class GameCanvas extends Canvas
    //#endif
    {


    }


    This also avoids having to have multiple copies of the code in the same midlet to cater for different phones....

  3. #3
    Regular Contributor
    Join Date
    Aug 2003
    Location
    Cracow, Poland
    Posts
    50
    Thanks for pointing out a quite nice tool, Antenna. I wish I had knew about that tool earlier.

    However, I don't agree with you that using my solution is THAT heavy weight. I want my game to run on different phones without providing different versions of JARs (of course it's not possible for all phones, especially for color and B/W devices).

    As a matter of fact both solutions have advantages and disadvantages ;-)

  4. #4
    Regular Contributor
    Join Date
    Mar 2003
    Location
    Silicon Scally
    Posts
    69
    To save you some time: I wrote something similar a while ago, for exactly the same reason. Anyway, it worked quite well on the emulators... then I tried it on real phones - most models failed to install the midlet because, I suspect, it referenced non-existent classes. If the midlet had any class that called or extended the Siemens API, for example, it wouldn't install on a Nokia or Motorola handset. I decided in the end just to go with different builds (I use CodeWarrior, so it's quite easy to substitute classes tailored for different phones).

    Carl.

  5. #5
    Regular Contributor
    Join Date
    Aug 2003
    Location
    Cracow, Poland
    Posts
    50
    Hmmm... strange indeed. I thought that class loaders are supposed to load classes if, and only if, a class is needed, not all of them at once.

    There is one thing that must be pointed out here - you can't use directly a device specific class, this class should be looked up by name i.e. Class.forName(...).

    BTW: I found a bug in my framework, now it works fine. I tested it on Nokia phones and various emulators... I haven't tested it on real phones other than Nokia, but I'll do it soon.

  6. #6
    Regular Contributor
    Join Date
    Mar 2003
    Location
    Silicon Scally
    Posts
    69
    I suspect there's some verifying and checking going off when the midlet is installed. Motorola phones, IIRC, do a whole load of stuff to the contents of the jar (like separating the classes and data) on install (which explains why it takes so long).

    Carl.

  7. #7
    Super Contributor
    Join Date
    Mar 2003
    Location
    Israel
    Posts
    2,280
    Hmmm... strange indeed. I thought that class loaders are supposed to load classes if, and only if, a class is needed, not all of them at once.
    Actually, I remember reading that the default J2ME behaviour is exactly that: all the classes are loaded on application startup.

    shmoove

  8. #8
    Regular Contributor
    Join Date
    Aug 2003
    Location
    Cracow, Poland
    Posts
    50
    @shmoove - where did you read it? I'm really interested in it.

  9. #9
    Super Contributor
    Join Date
    Mar 2003
    Location
    Israel
    Posts
    2,280
    I don't remember exactly where, but I remember it was during my baby steps in J2ME, so it was probably one of the documents linked from sun's site.
    I'll check through my documentation and see if I can find it.

    shmoove

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
×