Executing methods asynchronously

154 views
Skip to first unread message

Tiziano1960

unread,
Feb 8, 2018, 7:59:31 AM2/8/18
to App Inventor Open Source Development
I would like everyone could execute an asynchronous task choosen among any self-defined procedure/function. So if I write a procedure block I should be able to grab it and launch it asynchronously. So I made an extension which, by reflection, re-instantiate the desired Class (é.g. Screen2), get all the methods in it, invoke a certain method and execute it asynchronously. It works, the methods listed within a class are replicated, but I cannot find my own procedure blocks within those methods . So I cannot execute them. Is it possible to extract them like methods above are extracted or in some other ways?

Evan Patton

unread,
Feb 8, 2018, 7:21:27 PM2/8/18
to App Inventor Open Source Development
This is possible, but not as trivial as one might expect. The reason this is not as straightforward as one might like is because App Inventor does not generate Java code. Rather, it generates YAIL, which is a language built on Kawa Scheme. The YAIL gets directly compiled into Java bytecode. Since the source language is not Java, the compiled Screen code doesn't look quite the same from a reflection point of view.

To accomplish what you are aiming to do, you'd have follow this rough sketch:

1. Using reflection, iterate over the fields of the class, examining the fields starting with "Lit" to see if they are instance of SimpleSymbol.
2. If the literal is a SimpleSymbol, see if its getName() returns a string starting with "p$". This indicates that the symbol is a function name (procedure names are prefixed with p$ in App Inventor).
3. Call the screen's lookupInFormEnvironment function with the symbol acquired in Step 2. This should return an Object that is instanceof ModuleMethod. Note that the lookupInFormEnvironment method gets defined by the runtime when the app is compiled, so you will have to use reflection in order to call it.
4. "Call" the ModuleMethod instance using one of the apply methods, such as applyN.

Regards,
Evan

Tiziano1960

unread,
Feb 9, 2018, 12:34:32 AM2/9/18
to App Inventor Open Source Development
Ok, it seems quite tricky, but I will try. Thanks again
Message has been deleted
Message has been deleted

Tiziano1960

unread,
Feb 13, 2018, 4:23:18 AM2/13/18
to App Inventor Open Source Development
Well as regard as point n. 3 below, I'm stuck between two methods with same name name and different parameters: 
lookupInFormEnvironment(gnu.mapping.Symbol) and 
lookupInFormEnvironment(gnu.mapping.Symbol, java.lang.Object)
this is the code which is not working ( exception= Throwable )

1// after reflection I got class "myCN", then I try to retrieve the method 
2 // and then invoke it with parameter  Field "f" which  I got as per your point 2 below, not the name of field but the field itself
3 //  I have tried other combinations of parameter even with 
3 // lookupInFormEnvironment(gnu.mapping.Symbol, java.lang.Object) with parameter (f, f.get(myCN))
3 // with no success
4                       Method getNameMethod = myCN.getClass().getMethod("lookupInFormEnvironment", gnu.mapping.Symbol.class); 
5                       Object myReflectedTask =  getNameMethod.invoke(myCN, f);  
6// not it works with parameter like                                                                                                                                                                                                                                 6                            
7 // then if the above code was successful, below I try to launch the procedure                                               
8                       // verify and launch task
9                        if (myReflectedTask instanceof ModuleMethod){
10                            
11                            ModuleMethod myRT = (ModuleMethod)myReflectedTask;                             
12                            myRT.apply0();   } 

I have no clue about correct parameter types in rows 4 and 5, can you kindly give a hint?
Thanks

Evan Patton

unread,
Feb 13, 2018, 3:19:32 PM2/13/18
to App Inventor Open Source Development
The 2-param version of lookupInFormEnvironment is used to return a default value. If you use the 1-param version false is returned.

Here's a sketch of what you want:

Map<String, ModuleMethod> methods = new HashMap<String, ModuleMethod>();
Class screenClass = ;
Method lookupInFormEnvironment = form.getClass().getMethod("lookupInFormEnvironment", Symbol.class, Object.class);
Method symGetName = Symbol.class.getMethod("getName");
Field[] fields = screenClass.getDeclaredFields();
for (Field f : fields) {
  if (f.getName().startsWith("Lit") && (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
    f.setAccessible(true);
    Symbol sym = (Symbol) f.get(null);
    String name = (String) symGetName.invoke(sym);
    if (name.startsWith("p$")) {  // this symbol is a procedure name
      ModuleMethod lambda = (ModuleMethod) lookupInFormEnvironment.invoke(form, sym, null);
      if (lambda != null) {
        methods.put(name.substr(2), lambda); // strips the p$ prefix, p$doFoo becomes doFoo
      }
    }
  }
}
// At this point methods will be populated with (potentially mangled) procedure names mapped to the implementation

You may need to add more error checking and there may be some syntax errors. Let us know how it goes.

Regards,
Evan

Tiziano1960

unread,
Feb 17, 2018, 10:27:25 AM2/17/18
to App Inventor Open Source Development
Unfortunately I cannot obtain a valid copy of my desired methods, they  always assume the value null in the last if construct below. 
The fields are extracted correctly and I can report them in a list for checking purpose, together with literals names and other infos like the strings beginning with p$
As I do not manage to attibute a correct value to screenClass I do extract the fields  like this
Class<?> myClass = Class.forName(myClassName);
Object myCN = myClass.newInstance(); 
Field[] dfields = myCN.getClass().getDeclaredFields();

moreover the if construct used below to separate the symbols beginning with LIT etc. does not work as it  does not include the literals contanining p$string, so I use another method like this
Object newObj = dfields[i].get(myCN);                 
  if (String.valueOf(newObj).contentEquals("p$" + myTask)) etc. which works 

Evan Patton

unread,
Feb 18, 2018, 10:47:17 PM2/18/18
to App Inventor Open Source Development
I'm not sure why you are creating a new instance of the screen. Why not use the instance that is provided through the form field on AndroidNonvisibleComponent?

Evan

Tiziano1960

unread,
Feb 24, 2018, 1:38:14 PM2/24/18
to App Inventor Open Source Development
no matter  the approach I  try , I always obtain, in the last if statement, a null value ... even if the procedures are inercepted as existing symbols  and no error is catched (e.g. NoSuchMethodException, InvocationTargetException, etc)

 if (lambda != null) {       // always return false!!  

I have just a doubt about the reason why this statement   extracts  very few literals and  never that one related to my procedure

 if (f.getName().startsWith("Lit") && (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {    //the problem seems to be the modifier

so I must replace it with this one to extract all literals, 

if (f.toString().contains("SimpleSymbol" )){  //all symbols extracted are static  literals

Evan Patton

unread,
Feb 26, 2018, 2:32:27 PM2/26/18
to App Inventor Open Source Development
Hi Tiziano,

The actual process will differ depending on whether you are in the Companion context or a compiled app. I could probably generate a working prototype, but I probably won't be able to get to it until the end of the week.

Regards,
Evan

Tiziano1960

unread,
Feb 26, 2018, 4:36:26 PM2/26/18
to App Inventor Open Source Development
Thanks Evan, 
just to clarify, I tried both contexts .
Bye

Evan Patton

unread,
Mar 2, 2018, 11:24:50 PM3/2/18
to App Inventor Open Source Development
Hi Tiziano,

Here's an example that I put together that works in both the REPL and compiled apps. I included the Java source, compiled AIX, and a sample project that demonstrate the functionality. It provides the ability to do 3 things: Call a procedure by name, Asynchronously call a procedure by name (e.g., the procedure will run on another thread), and to call a procedure after a delay. There are also versions that take a list of arguments to pass to the procedure call. Let me know if you have any questions.

Cheers,
Evan

Evan Patton

unread,
Mar 2, 2018, 11:25:23 PM3/2/18
to App Inventor Open Source Development

Tiziano1960

unread,
Mar 3, 2018, 4:53:13 AM3/3/18
to App Inventor Open Source Development
As French people would say: "c'est MAGNIFIQUE!!". My approach was really flawed and couldn't lead to the problem solution. Well, I believe your brilliant extension must be published on the appinventor platform community and its clones. If you don't have time to do it, please let me share with communities the link you're sharing here. Thank you very much for this achievement. Bye

Evan Patton

unread,
Mar 7, 2018, 5:14:30 PM3/7/18
to App Inventor Open Source Development
This was just intended as a proof of concept and there are a few things that I'd like to add or clean up. In particular, this allows for us to do something neat things like map/reduce, currying, and other functional programming techniques. Let me try out a few things and I can push it to our extensions repo.

Regards,
Evan
Message has been deleted

Tiziano1960

unread,
May 5, 2018, 1:01:40 AM5/5/18
to App Inventor Open Source Development
Dear Evan,
I would like to integrate part of your code (adapted to circumstances ) in my extensions showing notifications. Can  I do that?  I obviously will show  this contribution inside code and in author references.

Evan Patton

unread,
May 7, 2018, 1:25:35 PM5/7/18
to App Inventor Open Source Development
Be my guest. I will consider it licensed under ASL 2.0, same as the App Inventor code base.

Regards,
Evan
Reply all
Reply to author
Forward
0 new messages