kivy / python-for-android

Turn your Python application into an Android APK
https://python-for-android.readthedocs.io
MIT License
8.35k stars 1.86k forks source link

onResume deadlock with pyjnius/pygame/sdl on android #890

Closed hottwaj closed 8 years ago

hottwaj commented 8 years ago

It seems that interaction between android.pyx, pyjnius and app pause mode can cause deadlock when going into pause mode or when resuming from pause mode. I am using the pygame bootstrap.

A few logs and things below to show what's going on

Here's the typical sequence - first I press the home button and the app should pause.

I/python  ( 4288): [INFO   ] [Android     ] Must go into sleep mode, check the app
I/python  ( 4288): [INFO   ] [Android     ] App paused, now wait for resume.
I/System.out( 4288): python pyjnius java:invoke(<proxy>, public abstract void android.view.ViewTreeObserver$OnGlobalLayoutListener.onGlobalLayout(), [Ljava.lang.Object;@33d6fa5f)

Note that after the "app paused" message, the Java interface of pyjnius receives a call which it tries to push to Python. The log message is from line 15 of NativeInvocationHandler.java of pyjnius

The above happens before I try to resume my app.

Once I try to resume, I get a black screen and nothing happens. I have logging setup on the pygame bootstrap and I can see that the app's onResume() method in PythonActivity.java is never called.

I get the following log output

W/ActivityManager( 1005): Activity stop timeout for ActivityRecord{20c40984 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity t8207}
I/ActivityManager( 1005): Activity reported stop, but no longer stopping: ActivityRecord{20c40984 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity t8207}
W/ActivityManager( 1005): Activity pause timeout for ActivityRecord{20c40984 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity t8207}
W/ActivityManager( 1005): Activity stop timeout for ActivityRecord{20c40984 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity t8207}
I/ActivityManager( 1005): Killing 4288:org.hottwaj.hottwajtest:python/u0a281 (adj 11): remove task
I/WindowState( 1005): WIN DEATH: Window{2a469087 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity}
W/WindowManager( 1005): Force-removing child win Window{200ab265 u0 SurfaceView} from container Window{2a469087 u0 org.hottwaj.hottwajtest/org.renpy.android.PythonActivity}

Normally I can only trigger this outcome if I press the "home" button. If I open the "apps task switcher" menu and then immediately switch back to my app, resume is handled correctly.

I believe this is a deadlock issue because occasionally an ANR crash occurs (not always though - probably because the call to NativeInvocationHandler.java does not always happen before app pause, but sometimes happens on resume).

Below is the ANR stack trace which sometimes occurs when the app is resumed and has been sat at black screen for a while - the crash occurs because pyjnius' NativeInvocationHandler.java can't get hold of a lock on a python thread (GIL maybe?)

It seems like either pyjnius or PythonActivity.java need to somehow "know" that the app is paused in order to somehow prevent this situation?

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 obj=0x87b73000 self=0xb7eaa318
  | sysTid=16668 nice=0 cgrp=apps sched=0/0 handle=0xb6f9cec8
  | state=S schedstat=( 908140747 218315933 1314 ) utm=82 stm=8 core=3 HZ=100
  | stack=0xbe449000-0xbe44b000 stackSize=8MB
  | held mutexes=
  native: #00 pc 0000f628  /system/lib/libc.so (syscall+28)
  native: #01 pc 00012dfd  /system/lib/libc.so (_Z33__pthread_cond_timedwait_relativeP14pthread_cond_tP15pthread_mutex_tPK8timespec+56)
  native: #02 pc 0012a6c8  /data/app/org.hottwaj.hottwajtest-2/lib/arm/libpython2.7.so (PyThread_acquire_lock+76)
  at org.jnius.NativeInvocationHandler.invoke0(Native method)
  at org.jnius.NativeInvocationHandler.invoke(NativeInvocationHandler.java:26)
  at java.lang.reflect.Proxy.invoke(Proxy.java:397)
  at android.view.ViewTreeObserver$OnGlobalLayoutListener.onGlobalLayout(ViewTreeObserver.java:-2)
  at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:889)
  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2128)
  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1239)
  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
  at android.view.Choreographer.doCallbacks(Choreographer.java:590)
  at android.view.Choreographer.doFrame(Choreographer.java:560)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:145)
  at android.app.ActivityThread.main(ActivityThread.java:6145)
  at java.lang.reflect.Method.invoke!(Native method)
  at java.lang.reflect.Method.invoke(Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
mrhdias commented 8 years ago

I have the same problem when back button is pressed (escape). I fix the issue with the following code:

def on_keyboard(self, window, key, scancode=None, codepoint=None, modifier=None):
    if platform == 'android' and key == 27:
        from jnius import autoclass, cast
        PythonActivity = autoclass('org.kivy.android.PythonActivity')
        activity = PythonActivity.mActivity
        Intent = autoclass('android.content.Intent')
        intent = Intent()
        intent.setAction(Intent.ACTION_MAIN)
        intent.addCategory(Intent.CATEGORY_HOME);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        currentActivity = cast('android.app.Activity', activity)
        currentActivity.startActivity(intent)
    return True