Kivy – Problems with on_new_intent in Apps
July 23, 2014 Leave a comment
In an earlier post I wrote about the wonderful on_new_intent hook which allows your application to receive intents from other apps on an Android device. When the other app sends an intent your app will process it in whatever callback you bound to on_new_intent (which I will call on_new_intent() for the sake of simplicity). To receive an intent from another app will probably mean that the user is actively using that other app. That means that your app has been paused. So, when Android passes the intent to your app it’s going to want to on_resume your app. Can you see where this is going?
The problem with using on_new_intent in an app is that on_resume() and on_new_intent() are both fired at more or less the same time. There is no guarantee that one of them will be called first and no guarantee that the one called first will return before the second one is called (in my experiments they can fire in either order and can fire in the middle of the other). In other contexts this might not be a problem. Here it is. The purpose of on_resume is to reinitialise the App state to where it was as at the last call to on_pause(). The purpose of on_new_intent is to initialise or update the App state based on the data provided by the intent. This is not a problem for services because services are running in the background and on_resume need not be called.
The future solution is that I’ve logged an issue. My current solution is a hackish workaround – delay the execution of on_new_intent to give on_resume a chance to start, and use flags for on_new_intent to wait for it to finish. If the timing is bad, this will fail, but seems to be ok in practice so far (ie last 24 hours).
Sample code (init the attributes to False when you instantiate the app too btw):
class GridderApp(App):
#[stuff deleted]
def on_resume(self):
if self.on_new_intenting: # let it run, don't bother about initialising to saved
return
self.resuming = True
self.uiwidget.load(path=".", filename=PAUSE_FILE_NAME, store_save= False)
self.resuming = False
def on_new_intent(self, intent):
self.on_new_intenting = True
sleep(0.25) # give on_resume a chance to fire
# if it fires after on_new_intent has finished, it will reinitialise to the saved state
# and we want to avoid that!
while self.resuming:
sleep(0.1)
intent_data = intent.getData()
try:
file_uri= intent_data.toString()
except AttributeError:
file_uri = None
if file_uri is None:
return
else:
if file_uri[:7]=="file://":
self.uiwidget.load(path="", filename=url2pathname(file_uri[7:]))
else:
return
self.on_new_intenting= False