processing / processing-android

Processing mode and core library to create Android apps with Processing
http://android.processing.org
781 stars 293 forks source link

Sketch variables behave nonsensically unless declared static #78

Open matteosistisette opened 10 years ago

matteosistisette commented 10 years ago

Consider a sketch like this:

boolean myVariable=false;
void setup() {
  orientation(LANDSCAPE); 
  //...
}
void draw () {
  if (myVariable) println ("myVariable is true");
  else println ("myVariable is false");
}
void someCallback() {
  myVariable=true;
}

I've run into situations where the method someCallback gets called (and I've definitely checked it does get called, 100%), yet the result of reading the variable in draw() never changes, i.e. it's like as if the variable being written in someCallback() were not the same variable being read from within draw().

someCallback() is some callback method called by a library such as oscP5 or any library that defines callbacks to be implemented by the sketch. I wonder whether the same may happen when writing the variable from a callback such as mousePressed(). Didn't try that.

Declaring the variable as static "fixes" the issue. But obviously, that's a horrible hack, not a solution. There's no reason why one should declare sketch variables static, and also, sketches that were not written specifically for Android mode will break when run in Android mode.

I guess there are situations where the whole sketch is reinstantiated (which shouldn't happen here in the very first place, since I'm calling orientation(LANDSCAPE)), but I can't see how a different sketch instance could be running the method that writes the variable while another one calls its own draw() method. No two instances of the same sketch should ever coexist and be "alive" under any circumstances. Actually that should be forced in such a strong way that if that ever happens, an exception is thrown or at least a warning issued.

codeanticode commented 9 years ago

hello @matteosistisette under what version of the android mode do you see this behavior?

matteosistisette commented 9 years ago

I feel stupid but I don't know how to figure that out. Help/About Processing doesn't help, and surprisingly, the only folder within the "modes" folder is "java".

Anyway, I've investigated the issue a little bit (not much). The issue happens when the callback method that modifies the non-static sketch variable is called from an oscP5 object (but I think that this could be reproduced with any class that you instantiate passing "this", i.e. the sketch to the constructor and which calls callback methods in the sketch).

I have: OscP5 oscP5; void setup() { orientation(LANDSCAPE); oscP5=new OscP5(this, .....); } void draw() { ..... } void oscEvent() { ..... }

What happens is that the sketch is clearly instantiated twice. In the setup() method I create an instance of OscP5. The first instance of the sketch instantiates its OscP5 object succesfully. For some reason (perhaps because of how orientation() is handled), the sketch is then "destroyed" and a new one is instantiated. Something that shouldn't happen in the first place, and if it does have to happen (definitely not in this case, but it would be the case when you don't have a fixed orientation and the orientation changes), then the sketch is not being destroyed completely. The new instance of the sketch then fails to instantiate its own oscP5 object, because the one instantiated by the first instance of the sketch is still "alive" and listening to the port. So, the "old" oscP5 (belonging to the dead sketch) receives OSC messages and calls the callback method of the "dead" instance of the sketch (something that should be detected and throw an exception, if it happens at all which it shouldn't).

Several things are going wrong:

Fixing the third would "only" allow to properly debug issue due to the second issue, if that cannot avoided completely.

I do understand that this may be a design issue with no easy solution (how can Processing keep track of the objects that have been created which have references to a sketch? how can it force their destruction? Doesn't seem easy in Java). Perhaps Processing lacks a proper framewkork for registering event listeners. Usually this is implemented like this: a library class has a constructor whose first parameter is the sketch, and it calls method with given names as callbacks. However that does not provide (as far as I can see) a generalized way of guaranteeing that no references to a dead sketch are leaked. This is kind of left to the responsibility of the author of the library and that of the sketch. If the pattern of passing the sketch as a parameter to the constructor of objects that generate events is to be kept, I can only imagine one way of solving this problem: classes that implement any kind of events (i.e. are going to call callback methods of the sketch) should be required to extend a given abstract class or implement a given interface; they should be required to call a given method from the constructor (so the sketch would know they have reference to itself), and should implement a "destructor"...... But i think the only proper solution would be to provide a full-blown event framework.