Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid's WallpaperService.Engine issues, workarounds required
    primarykey
    data
    text
    <p>During development of Live Wallpapers I jus got two issues and want to find the best possible workarounds.</p> <h3>Issue#1: <code>WallpaperService.Engine.onSurfaceCreated()</code> and <code>WallpaperService.Engine.onSurfaceChanged()</code> called after <code>WallpaperService.Engine.onDestroyed()</code></h3> <p>Under some circumstances Android call the <code>WallpaperService.Engine.onSurfaceCreated()</code> and <code>WallpaperService.Engine.onSurfaceChanged()</code> after <code>WallpaperService.Engine.onDestroyed()</code> was called. It is against the <code>WallpaperService.Engine</code> execution protocol defined by documentation.</p> <p>My current solution is just to have the explicit flag (<code>mAlreadyDestroyed</code>) which <em>false</em> by default but set to <em>true</em> in <code>onDestroy()</code> callback. The <code>WallpaperService.Engine.onSurfaceCreated()</code> and <code>WallpaperService.Engine.onSurfaceChanged()</code> check this flag and do nothing if it is <em>true</em>. Have anyone face this issue too and how you resolve it ?</p> <p>Below is a simple code you may use to check for this issue:</p> <pre><code>public class LWService extends WallpaperService { /** * Will show the bug with calling {@link #onSurfaceCreated(SurfaceHolder)} * and other surface callbacks after {@link #onDestroy()}. * */ private class LWEngineTest1 extends Engine { /** * Will be set to &lt;code&gt;true&lt;/code&gt; in {@link #onDestroy()}. */ private boolean mAlreadyDestroyed = false; /** * Log debug level message with adding object instance info to better * LogCat readability. * * @param message * message to log */ private void logD(final String message) { Log.d("LW_BUG_TEST", this.toString() + ":" + message); } /** * Log error level message with adding object instance info to better * LogCat readability. * * @param message * message to log */ private void logE(final String message) { Log.e("LW_BUG_TEST", this.toString() + ":" + message); } @Override public void onCreate(SurfaceHolder surfaceHolder) { logD("onCreate()"); } @Override public void onDestroy() { logD("onDestroy()"); mAlreadyDestroyed = true; } @Override public void onSurfaceCreated(SurfaceHolder holder) { logD("onSurfaceCreated()"); if (mAlreadyDestroyed) { logE("onSurfaceCreated() after onDestroy()"); } } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { logD("onSurfaceChanged()"); if (mAlreadyDestroyed) { logE("onSurfaceChanged() after onDestroy()"); } } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { logD("onSurfaceDestroyed()"); if (mAlreadyDestroyed) { logE("onSurfaceDestroyed() after onDestroy()"); } try { // To reveal the bug, without this line you may not got the // issue. Of course it is absolutely synthetic but allow to get // the issue guaranteed Thread.sleep(4000); } catch (InterruptedException exc) { } } } @Override public Engine onCreateEngine() { return new LWEngineTest1(); } } </code></pre> <p>And following is what I get in log on Samsung Galaxy S (Android 2.2.1). Just set this sample Wallpaper as current and then select another one (only related log entries preserved):</p> <pre><code>08-14 14:53:55.964: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@48008a28:onCreate() 08-14 14:53:55.980: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@48008a28:onSurfaceCreated() 08-14 14:53:55.980: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@48008a28:onSurfaceChanged() 08-14 14:54:17.651: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onCreate() 08-14 14:54:17.667: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceCreated() 08-14 14:54:17.667: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceChanged() 08-14 14:54:18.261: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@48008a28:onSurfaceDestroyed() 08-14 14:54:22.265: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@48008a28:onDestroy() 08-14 14:54:26.675: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceDestroyed() 08-14 14:54:30.675: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onDestroy() 08-14 14:54:30.687: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceCreated() 08-14 14:54:30.687: ERROR/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceCreated() after onDestroy() 08-14 14:54:30.687: DEBUG/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceChanged() 08-14 14:54:30.687: ERROR/LW_BUG_TEST(19274): lwtests.LWService$LWEngineTest1@4800f0b0:onSurfaceChanged() after onDestroy() </code></pre> <h3>Issue#2: <code>WallpaperService.Engine.onSurfaceDestroyed()</code> called after surface was actually destroyed</h3> <p>Under some circumstances Android may call the <code>WallpaperService.Engine.onSurfaceDestroyed()</code> after surface was actually destroyed. It is against the <code>SurfaceHolder.Callback.onSurfaceDestroyed()</code> specification. This issue is rather very specific and may be unnoticed even if occur in your code. The most time you will notice that issue if you has separate rendering thread. The rendering thread may notice died surface even before your main application's thread receive the mentioned <code>WallpaperService.Engine.onSurfaceDestroyed()</code>. In my case of OpenGL rendering I just got the <strong>EGL_BAD_NATIVE_WINDOW</strong> when <code>eglSwapBuffers()</code> are executed.</p> <p>My current solution is just to ignore the specified EGL error but I really don't like such kind of solutions. My code full of self-checking assertions and error conditions checks and I don't like to remove these assertions and checks (and specific error code ignoring is such kind of thing). Is there are any solutions exists for this issue ?</p> <p>Below is a simple code you may use to check for this issue:</p> <pre><code>public class LWService extends WallpaperService { /** * Will show the bug with non-conform to documentation (specification) * {@link #onSurfaceDestroyed(SurfaceHolder)}. */ private class LWEngineTest2 extends Engine { /** * Log error level message with adding object instance info to better * LogCat readability. * * @param message * message to log */ private void logE(final String message) { Log.e("LW_BUG_TEST", this.toString() + ":" + message); } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { if (holder.getSurface().isValid() &amp;&amp; null == holder.lockCanvas()) { logE("onSurfaceDestroyed() : uuups ... broken surface"); } // If you have separate rendering thread it may already notice that // surface already invalid and encounter problems due to that fact. // E.g. eglSwapBuffers() may generate EGL_INVALID_NATIVE_WINDOW // error. // mRenderingThread.releaseSurface(); } } @Override public Engine onCreateEngine() { return new LWEngineTest1(); } } </code></pre> <p>And following is what I get in log on Acer A500 (Android 3.1). Just select this Wallpaper for preview and then press Back button in preview activity (only related log entries preserved):</p> <pre><code>08-14 16:10:55.580: ERROR/Surface(31787): Surface (identity=1298) state = -19 08-14 16:10:55.660: ERROR/Surface(31787): getBufferLocked(0, 0, 0, 0, 00000033) failed (No such device) 08-14 16:10:55.660: ERROR/Surface(31787): dequeueBuffer failed (No such device) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): Exception locking surface 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): java.lang.IllegalArgumentException 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.view.Surface.lockCanvasNative(Native Method) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.view.Surface.lockCanvas(Surface.java:346) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at com.android.internal.view.BaseSurfaceHolder.internalLockCanvas(BaseSurfaceHolder.java:184) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at com.android.internal.view.BaseSurfaceHolder.lockCanvas(BaseSurfaceHolder.java:157) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at lwtests.LWService$LWEngineTest2.onSurfaceDestroyed(LWService.java:271) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.service.wallpaper.WallpaperService$Engine.reportSurfaceDestroyed(WallpaperService.java:773) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.service.wallpaper.WallpaperService$Engine.detach(WallpaperService.java:790) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:902) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:61) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.os.Handler.dispatchMessage(Handler.java:99) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.os.Looper.loop(Looper.java:132) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at android.app.ActivityThread.main(ActivityThread.java:4025) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at java.lang.reflect.Method.invokeNative(Native Method) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at java.lang.reflect.Method.invoke(Method.java:491) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) 08-14 16:10:55.660: ERROR/BaseSurfaceHolder(31787): at dalvik.system.NativeStart.main(Native Method) 08-14 16:10:55.660: ERROR/LW_BUG_TEST(31787): lwtests.LWService$LWEngineTest2@407ba3b8:onSurfaceDestroyed() : uuups ... broken surface </code></pre> <p>I have reported both these issues in Android issues tracker (<a href="http://code.google.com/p/android/issues/detail?id=19243&amp;colspec=ID%20Type%20Status%20Owner%20Summary%20Stars" rel="nofollow">19243</a>, <a href="http://code.google.com/p/android/issues/detail?id=19245&amp;colspec=ID%20Type%20Status%20Owner%20Summary%20Stars" rel="nofollow">19245</a>) but practically it is more interesting to know how to resolve these issues for already released versions of Androids. This is why I am asking here for solutions anyone may know. And while I have already the own workarounds (may be will be usefull to someones) I also want to know another possibilities to fight these issues.</p> <p>Thanks in advance.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload