Could someone give me a easy and brief description of EventDispatcher?

Hi ! :wave:

A search in the community did not give me satisfying results. I got some open source extensions, but could not understand anything.

I also searched the MIT AppInventor source code, but couldn't understand a thing.

Could someone explain me things about the EventDispatcher ?

What I'm doing in my project is :

I'm creating a Thread and doing a procedure there and I wan't to raise an event in the main thread when the procedure is complete.

Could someone give me an example of how to do this ?

BTW, the thread is in a nested class.

Do you just need an example?

  @SimpleFunction(description = "Returns the sum of the given list of integers.")
  public void SumAll(YailList integers) {
    int sum = 0;
    for (final Object o : integers.toArray()) {
      try {
        sum += Integer.parseInt(o.toString());
      } catch (NumberFormatException e) {
        throw new YailRuntimeError(e.toString(), "NumberFormatException");
      }
    }
    SumReturn(sum);    
  }

  @SimpleEvent(description = "This event fires after the sum is calculated and returns the sum")
  public void SumReturn(int sum) {
    EventDispatcher.dispatchEvent(this, "SumReturn", sum);
  }

image

1 Like

Thanks for the example, but what does the EventDispatcher do ? Where to put it ? Where to mark its beginning ?

EventDispatcher class has a method dispatchEvent which helps to raise an event method. @TIMAI2 has already given you an example.

Do you think this will work ?

(I can't test things right now. Pls see from my code.)

public class Test extends AndroidNonvisibleComponent {
  
  Thread thread;
  Test obj;
  Test.ThreadTest tt;
  
  Test(ComponentContainer c){
    super(c.$form);
    obj = this;
    tt = obj.new ThreadTest();
  }
  
  private class ThreadTest implements Runnable{
    public void startThread(){
      Test.thread = new Thread(Test.new ThreadTest());
    }
    public void run(){
      int v1 = 1, v2 = 2, v3 = 3, v4 = 4;
      obj.gotValues(v1, v2, v3, v4);
    }
  }
  
  @SimpleFunction
  public void getValues(){
    tt.startThread();
  }
  
  @SimpleEvent
  public void gotValues(int v1, int v2, int v3, int v4){
    EventDispatcher.dispatchEvent(this, "Sample Event", v1, v2, v3, v4);
  }
  
  public static void main(String... args){
    
  }
  
}

Things outside class definition not included.

All events must run on the UI thread. Since you're spawning a background thread to do your computing, you shouldn't be calling the event method from the background thread. Instead, you would do something like:

...
public void run() {
  final result = ...; // compute some thing
  form.runOnUiThread(new Runnable() {
    public void run() {
      gotValues(result);
    }
  });
}
...

Also, the default implementation of start should be sufficient. Don't override it unless you have a good reason, and typically you should call super.

2 Likes

I couldn't understand this part.

Also, sorry for this. It should be tt.start();

P.S.

I edited the method name of start() to startThread().

Do you think this will work ?

public class Test extends AndroidNonvisibleComponent {
  
  Thread thread;
  Test obj;
  Test.ThreadTest tt;
  
  Test(ComponentContainer c){
    super(c.$form);
    obj = this;
    tt = obj.new ThreadTest();
  }
  
  private class ThreadTest implements Runnable{
    public void startThread(){
      Test.thread = new Thread(Test.new ThreadTest());
    }
    public void run(){
      int v1 = 1, v2 = 2, v3 = 3, v4 = 4;
      obj.returnValues(v1, v2, v3, v4);
    }
  }
  
  @SimpleFunction
  public void getValues(){
    thread.startThread();
  }
  
  private void returnValues(int v1, int v2, int v3, int v4){
    thread.destroy();
    gotValues(v1, v2, v3, v4);
  }
  
  @SimpleEvent
  public void gotValues(int v1, int v2, int v3, int v4){
    EventDispatcher.dispatchEvent(this, "Sample Event", v1, v2, v3, v4);
  }
  
  
}

This time I used thread.run() instead of tt.run().
Also, I used a method to destroy that thread before returning the value to the method.

First, the names of the classes and methods in your code are quite confusing. You are going above and above than what is required. You are making it complicated than it is. It looks like you are dealing with decompiled code of progaurded class.

On the startThread() method, you have assigned value of thread object of Test. You should use start() method of Thread class to begin execution of code in run method of Runnable.

In getValues() method, you are using startThread() method for object of type Thread. This method doesn't exist in Thread class.

In gotValues method, you are using EventDispatcher.dispatchEvent() which needs three params, first Component, second event name and third is array of arguments. You should have done something like this,

EventDispatcher.dispatchEvent(this, "gotValues", v1, v2, v3, v4);

.......

There is difference between run and start method of Thread.

........

And at last, you should always dispatch event in the UI thread. The code is already suggested by Evan above.

One more example,

public class Test extends AndroidNonVisibleComponent {
  public Test(ComponentContainer container) {
    super(container.$form());

  }

  @SimpleFunction
  public void SumInNewThread(int a, int b) {
    //
    new Thread(new Runnable() {
      @Override
      public void run() {
        final int sum = a + b;
        // Now i have sum of these number, since i cant dispatch event on background thread, i will use runOnUiThread method from Activity class 
        form.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            GotSum(sum);
          }
        });
      }
    }).start();
  }
  @SimpleEvent
  public void GotSum(int sum) {
    EventDispatcher.dispatchEvent(this, "GotSum", sum);
  }
}
3 Likes

Like this ?

public class Test extends AndroidNonvisibleComponent {
  
  Test(ComponentContainer c){
    super(c.$form);
  }
  
  @SimpleFunction
  public void getValues(){
    new Thread(new Runnable(){
      @Override
      public void run(){
        // Processing stuff here.
        int v1 = 1, v2 = 2, v3 = 3, v4 = 4;
        //Returning values through this.
        form.runOnUiThread(new Runnable(){
          @Override
          public void run(){
            gotValues(v1, v2, v3, v4);
          }
        });
      }
    });
  }
  
  @SimpleEvent
  public void gotValues(int v1, int v2, int v3, int v4){
    EventDispatcher.dispatchEvent(this, "Sample Event", v1, v2, v3, v4);
  }
  
}

If you are not using java -8 supported tools like Rush, you should declare final when assigning value to v1, v2, v3 and v4 so that it can be acessed from run method like,

final int v1=1,v2=2,v3=3,v4=4;

And you again have not used start method. See example I have provided above.

1 Like

Also, it is recommended to follow Naming Conventions.

1 Like

I use Niotron IDE actually. Will that work ?

Why not you check by yourself and share result here :thinking: ?

1 Like

I'll check it later. (It takes me too much time.)

I'm doing another project now. Thanks for your help.

Btw, which Class's object is form ?

super(container.$form()) will call the superclass AndroidNonVisibleComponent 's constructor with form that contains the container.

As Form extends AppInventorCompatActivity which extends Activity, you can directly write

form.runOnUiThread(...);

Or you can get activity from container like this

activity = (Activity) container.$context();

And use the activity's runOnUiThread method

activity.runOnUiThread(...);
1 Like

For more clarity, you can look to the source of AndroidNonVisibleComponent, ComponentContainer & Form.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.