It's interesting to see how these various methods actually work in
javascript.

In all cases click listeners are managed using the following methods.
If you look at the basic Button constructor you can see it calls the
addClickListener(..) function

function $addClickListener(this$static, listener){
  if (!this$static.clickListeners) {
    this$static.clickListeners = $ClickListenerCollection(new
ClickListenerCollection());
    sinkEvents(this$static.element, 1 | (this
$static.element.__eventBits || 0));
  }
  $add_2(this$static.clickListeners, listener);
}

Then the user click is detected


function onBrowserEvent(event_0){
  if ($eventGetTypeInt(event_0) == 1) {
    if (this.clickListeners) {
      $fireClick(this.clickListeners, this);
    }
  }
}

Then the elements with ClickListeners attached are queried to see if
any match the element the user clicked. Obviously the more there are,
the longer this will take.


function $fireClick(this$static, sender){
  var listener, listener$iterator;
  for (listener$iterator = $AbstractList$IteratorImpl(new AbstractList
$IteratorImpl(), this$static); listener$iterator.i < listener
$iterator.this$0.size_0();) {
    listener = dynamicCast($next_0(listener$iterator), 5);
    $onClick(listener, sender);
  }
}

Now we can look at how three methods of setting up Buttons with
ClickListsners is set up in javascript.

Method 1: A shared ClickListener - note using a hash table lookup
would be much more efficient than this if there where a lot of
them.


    ClickListener buttonListener = new ClickListener() {
        public void onClick(Widget sender) {
            if (sender==sharedButtonOne) {
                Window.alert("Shared One");
                return;
            }
            if (sender==sharedButtonTwo) {
                Window.alert("Shared Two");
                return;
            }

        }
    };

    Button sharedButtonOne = new Button("Shared 1",buttonListener);
    Button sharedButtonTwo = new Button("Shared 2",buttonListener);

This translates as follows. Note the single listener instance is
translated to Sandbox$1 (my scratch module is called SandBox BTW).
However many Buttons are added we will still have this single instance
of it.

function $SandBox(this$static){
  this$static.layout = $HorizontalPanel(new HorizontalPanel());
  this$static.buttonListener = new SandBox$1();
  this$static.sharedButtonOne = $Button_0(new Button(), 'Shared 1',
this$static.buttonListener);
  this$static.sharedButtonTwo = $Button_0(new Button(), 'Shared 2',
this$static.buttonListener);
  return this$static;
}

function $SandBox$1(this$static, this$0){
  this$static.this$0 = this$0;
  return this$static;
}

function $onClick(this$static, sender){
  if (sender == this$static.this$0.sharedButtonOne) {
    $wnd.alert('Shared One');
    return;
  }
  if (sender == this$static.this$0.sharedButtonTwo) {
    $wnd.alert('Shared Two');
    return;
  }
}

function SandBox$1(){
}

_ = SandBox$1.prototype = new Object_0();
_.typeId$ = 23;
_.this$0 = null;



Method 2: using anonymous inner class

    Button innerButtonOne = new Button("Inner One", new ClickListener
() {
        public void onClick(Widget sender) {
            Window.alert("Inner One");
        }
    });

    Button innerButtonTwo = new Button("Inner Two", new ClickListener
() {
        public void onClick(Widget sender) {
            Window.alert("Inner Two");
        }
    });


This translates as follows: Note that we now have two listener
objects, both of which will be added to the main ClickListener
collection. With 100 buttons, we would have 100 of them.

function $SandBox(this$static){
  this$static.layout = $HorizontalPanel(new HorizontalPanel());
  this$static.innerButtonOne = $Button_1(new Button(), 'Inner One',
new SandBox$1());
  this$static.innerButtonTwo = $Button_1(new Button(), 'Inner Two',
new SandBox$2());
  return this$static;
}


function onClick(sender){
  $wnd.alert('Inner One');
}

function SandBox$1(){
}

_ = SandBox$1.prototype = new Object_0();
_.onClick = onClick;
_.typeId$ = 23;
function onClick_0(sender){
  $wnd.alert('Inner Two');
}

function SandBox$2(){
}

_ = SandBox$2.prototype = new Object_0();
_.onClick = onClick_0;
_.typeId$ = 24;



method 3: your EButton


    EButton ajaysButtonOne = new EButton("Ajay's One") {
        public void onClick(Widget sender) {
            Window.alert("Ebutton 1");
        }
    };

    EButton ajaysButtonTwo = new EButton("Ajay's Two") {
        public void onClick(Widget sender) {
            Window.alert("Ebutton 2");
        }
    };


This translates as follows: As you can see a separate instance (SandBox
$1 & sandBox$2) created for each EButton instance. It appears to have
basically turned itself into a ClickListener equivalent to method 2.
It's instructive to compare this with how a normal Button is
constructed in methods 1 & 2.

function $SandBox(this$static){
  this$static.layout = $HorizontalPanel(new HorizontalPanel());
  this$static.ajaysButtonOne = $SandBox$1(new SandBox$1(), "Ajay's
One");
  this$static.ajaysButtonTwo = $SandBox$2(new SandBox$2(), "Ajay's
Two");
  return this$static;
}

function $EButton(this$static, text){
  $ButtonBase(this$static, $doc.createElement('button'));
  adjustType(this$static.element);
  this$static.element['className'] = 'gwt-Button';
  $setInnerText(this$static.element, text);
  $addClickListener(this$static, this$static);
  return this$static;
}

function EButton(){
}

_ = EButton.prototype = new Button();
_.typeId$ = 23;
function $SandBox$1(this$static, $anonymous0){
  $EButton(this$static, $anonymous0);
  return this$static;
}

function onClick(sender){
  $wnd.alert('Ebutton 1');
}

function SandBox$1(){
}

_ = SandBox$1.prototype = new EButton();
_.onClick = onClick;
_.typeId$ = 24;
function $SandBox$2(this$static, $anonymous0){
  $EButton(this$static, $anonymous0);
  return this$static;
}

function onClick_0(sender){
  $wnd.alert('Ebutton 2');
}

function SandBox$2(){
}

_ = SandBox$2.prototype = new EButton();
_.onClick = onClick_0;
_.typeId$ = 25;


So my reading of this is:

1) All three methods involve creating a Button instance which
translates into a native Button element provided by the browser and
attached to the DOM.
2) All three methods involve creating one or more listener objects
(sandBox$1, SandBox$2 etc) that are added to the static ClickListsner
Collection
3) When a Click event is fired from the browser, the click listeners
are queried to see if any are registered with the clicked DOM element.
4) In method one there is only one of them, so that question is
answered very quickly
5) In methods 2 & 3, the more buttons there are, the longer this
takes.
6) Although EButtons appear to register themselves with themselves
($addClickListener(this$static, this$static)) they still go through
the same rigmarole through the listener collection if one is clicked -
they do not bypass it by saying "oh, I can just call myself" as far as
I can see.
7) The EButton option results in marginally more code than the inner
class option, and massively more than the shared listener option
(multiply it all up by e.g. 100), may run slower as a result even by
comparison with the inner class option, and will certainly increase
the size of the javascript files.
8) I think it demonstrates conclusively why using the single listener
technique can make a huge difference if you have a large number of
clickable widgets in an application.





On Feb 3, 8:14 am, Ajay Garg <[email protected]> wrote:
> Hmm.. I looked at GWT's code, and went all the way 
> uptohttp://google-web-toolkit.googlecode.com/svn/javadoc/1.5/com/google/g...
> code.
>
> Button is a sub-subclass of FocusWidget, and this is what happens.
> Whenever a clickListener is added to a Button for the first time,
> "Event.Click" event is sunk in for the newly instantiated Button
> instance, and the clickListener added to ClickListenerCollection.
> Next, whenever an onBrowserEvent(Event event) method is triggered, the
> onClick() method is called for each of the clickListener registered in
> ClickListenerCollection.
>
> So, as far as managing a large number of ClickListeners goes, if we go
> by using EButton, the number of ClickListeners is equal to the number
> of anonymous abstract classes made out of EButton.
>
> I think a more thinking point would be to consider "public void
> sinkEvents(int eventBitsToAdd)" method of UIObject class, which
> happens to be a superclass of FocusWidget. This method is called
> whenever a ClickListener is added to an instance for the first time.
> Thus, it does not matter whether the ClickListener interface being
> added is the same reference as the anonymous abstract class of
> Ebutton, or a central wrapper ClickListener (per component, as is
> being talked about).
>
> Thus, in a nutshell, if we create an anonyous abstract class of
> EButton :
>
> 1. We ensure tight coupling, in the sense that the widget, and its
> "action-on-click" are bound together.
>
> 2. Since we do not require any external ClickListener (remember that
> EButton is a clickListener to itself), memory leak is not a problem;
> whenever the anonymous instance goes out of scope, that is the end. In
> other words, there is no chance of a dangling reference in the
> "WrapperWidget"'s onClick method.
>
> 3. There is no extra overhead of maintaining any extra ClickListener
> references, as the number of ClickListener references is equal to the
> number of anonymous instances.
>
> Your thoughts ..??
>
> On Feb 2, 11:17 pm, gregor <[email protected]> wrote:
>
> > > This case requires one class ( anonymous extended EButton) creation.
>
> > > Your thoughts ..??
>
> > Yes, but you are not reducing the number of event listeners by doing
> > this. Each button is now itself a separate click event listener. So I
> > don't think this answers the warning given in the link about large
> > numbers of event listeners. Also I doubt if there would be much
> > material difference if any in the javascript generated between your
> > EButton and a normal Button with an anonymous ClickListener, or none
> > that would have any effect on performance anyway - you can always
> > compile with -PRETTY and check.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/Google-Web-Toolkit?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to