Android的WallpaperService.Engine问题,需要解决方法

时间:2011-08-14 14:17:19

标签: android live-wallpaper

在动态壁纸的开发过程中,我遇到了两个问题,并希望找到最佳的解决方法。

问题#1:在WallpaperService.Engine.onSurfaceCreated()

之后调用WallpaperService.Engine.onSurfaceChanged()WallpaperService.Engine.onDestroyed()

在某些情况下,Android会在WallpaperService.Engine.onSurfaceCreated()被调用后调用WallpaperService.Engine.onSurfaceChanged()WallpaperService.Engine.onDestroyed()。它违反了文档定义的WallpaperService.Engine执行协议。

我当前的解决方案只是拥有显式标记(mAlreadyDestroyed),默认情况下 false ,但在onDestroy()回调中设置为 true WallpaperService.Engine.onSurfaceCreated()WallpaperService.Engine.onSurfaceChanged()检查此标记,如果 true 则不执行任何操作。有没有人面对这个问题以及你如何解决它?

以下是您可以用来检查此问题的简单代码:

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 <code>true</code> 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();
    }
}

以下是登录Samsung Galaxy S(Android 2.2.1)时的内容。只需将此示例壁纸设置为当前,然后选择另一个(仅保留相关的日志条目):

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()

问题#2:WallpaperService.Engine.onSurfaceDestroyed()在表面被实际销毁后调用

在某些情况下,Android可能会在表面被实际销毁后调用WallpaperService.Engine.onSurfaceDestroyed()。这违反了SurfaceHolder.Callback.onSurfaceDestroyed()规范。此问题非常具体,即使在您的代码中出现也可能不被注意。如果您有单独的渲染线程,您将注意到该问题的最多时间。即使在主应用程序的线程收到提到的WallpaperService.Engine.onSurfaceDestroyed()之前,呈现线程也可能会注意到死表面。在我的OpenGL渲染中,我在执行eglSwapBuffers()时得到了 EGL_BAD_NATIVE_WINDOW

我目前的解决方案是忽略指定的EGL错误,但我真的不喜欢这种解决方案。我的代码充满了自检断言和错误条件检查,我不想删除这些断言和检查(并且忽略特定的错误代码就是这种事情)。这个问题是否存在任何解决方案?

以下是您可以用来检查此问题的简单代码:

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() && 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();
    }
}

以下是我登录Acer A500(Android 3.1)的内容。只需选择此壁纸进行预览,然后在预览活动中按“返回”按钮(仅保留相关的日志条目):

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

我已在Android问题跟踪器(1924319245)中报告了这两个问题,但实际上知道如何为已发布的Androids版本解决这些问题更为有趣。这就是为什么我在这里要求任何人都知道的解决方案。虽然我已经有了自己的解决方法(可能会对某些人有用),但我也想知道另一种解决这些问题的可能性。

提前致谢。

2 个答案:

答案 0 :(得分:1)

可以并行执行多个引擎。你必须管理它们。要检查这一点,只需为您创建的每个引擎添加一个ID。您可以通过以下方式验证:

HotelCategory

我注意到在不同设备上的行为略有不同。所以我建议你在不同的设备上测试你的解决方案。

答案 1 :(得分:0)

我在Incredible运行的Cyanogen 7.0.3(Gingerbread 2.3.3)上试用了你的代码。我没有看到你遇到的问题。

以下是经过Picker&gt;后的日志结果。预览&gt;设置壁纸:

08-24 07:47:04.261: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@40630938:onCreate()
08-24 07:47:04.301: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@40630938:onSurfaceCreated()
08-24 07:47:04.301: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@40630938:onSurfaceChanged()
08-24 07:47:10.187: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4062d100:onCreate()
08-24 07:47:10.227: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4062d100:onSurfaceCreated()
08-24 07:47:10.227: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4062d100:onSurfaceChanged()
08-24 07:47:10.858: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@40630938:onSurfaceDestroyed()
08-24 07:47:14.862: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@40630938:onDestroy()

然后在设置完全不相关的壁纸后,该实例将被销毁:

08-24 07:48:33.268: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4062d100:onSurfaceDestroyed()
08-24 07:48:37.272: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4062d100:onDestroy()

这是Picker&gt;之后的结果吗?预览&gt;返回(没有设置壁纸):

08-24 07:49:50.133: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4063eb20:onCreate()
08-24 07:49:50.173: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4063eb20:onSurfaceCreated()
08-24 07:49:50.173: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4063eb20:onSurfaceChanged()
08-24 07:49:54.157: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4063eb20:onSurfaceDestroyed()
08-24 07:49:58.161: DEBUG/LW_BUG_TEST(32534): LWService$LWEngineTest1@4063eb20:onDestroy()