在Android应用程序中正确加载资源(使用openGL)

在Android应用程序中正确加载资源(使用openGL),android,graphics,resources,opengl-es-2.0,splash-screen,Android,Graphics,Resources,Opengl Es 2.0,Splash Screen,背景/概述 我的应用程序是一个openGL游戏(Android&用Java编写) 我不确定应该如何加载资源(即加载位图、创建对象等) 我目前正在使用onSurfaceCreated方法(见下面的示例)完成所有这些工作-对于我的Nexus10平板电脑来说,一切都可以在不到一秒钟内完成。然而,当我在老式手机(Galaxy Ace)上运行它时,在发生任何事情之前,我会得到一个大约3到4秒的空白屏幕。我认为这是不可接受的,所以我想尝试找到一种解决方法 我知道我可以在它做生意的时候放一个简单的飞溅(甚至只

背景/概述

我的应用程序是一个openGL游戏(Android&用Java编写)

我不确定应该如何加载资源(即加载位图、创建对象等)

我目前正在使用onSurfaceCreated方法(见下面的示例)完成所有这些工作-对于我的Nexus10平板电脑来说,一切都可以在不到一秒钟内完成。然而,当我在老式手机(Galaxy Ace)上运行它时,在发生任何事情之前,我会得到一个大约3到4秒的空白屏幕。我认为这是不可接受的,所以我想尝试找到一种解决方法

我知道我可以在它做生意的时候放一个简单的飞溅(甚至只是显示“加载”),但是我的问题是:

1)在加载所有内容之前,如何显示任何内容?我的意思是,所有内容都在onSurfaceCreated中加载,onDrawFrame在onSurfaceCreated之后才会运行。那么,在资源仍在加载时,如何显示任何内容呢

2)如果我显示一个简单的飞溅。在更新/更快的设备上,这看起来不是有点垃圾吗?我的意思是,只有当新手机/平板电脑上的所有东西都加载得如此之快时,splash才会在瞬间显示出来。我是否应该强制它在屏幕上停留的时间超过它在速度更快的设备上所需的时间

3)最后,一个相关的(也是非常重要的)问题-在游戏最终显示后,在旧手机上。在大约10-15秒的时间里,每样东西都会间歇性地抽搐和口吃。手机显然是在后台做一些事情。(这并不是说手机不能处理图形,因为它可以。在那10-15秒之后,一切都像黄油一样平滑)。任何关于这里可能发生的事情或如何追踪问题的线索都将不胜感激!!:-)

只是一个旁注:我已经阅读了关于SO和更广泛的互联网的各种类似问题,但它们似乎都涉及使用XML布局(而且似乎不太容易实现/成功),我应该指出,我没有在我的应用程序中使用任何XML,如果可能的话,我希望在代码中不使用XML来解决这个问题

代码示例

public void onSurfaceChanged(GL10 gl, int width, int height) {

//The following are performed once at game load
//All resources are being loaded / created here
//Load graphics files
res.loadResources(view);
//Create game objects (sprites, collision detection etc)
res.createObjects(view, width, height);
//Recycle bitmaps as they are no longer required
res.Recycle();
//Set initial values variables (required for subsequent methods)
res.setInitialValues();
//Set level layout
res.setLevel();
}
1) 您可以将大量初始化移到
异步任务中
。在活动完成之前,第一个屏幕不会显示
onCreate()

2) 是和否,取决于如何、什么和在哪里。大多数用户都可以理解带有一些加载信息的简单屏幕。不管怎样,大多数顶级游戏都带有某种品牌屏幕

3) 我不知道您是如何实现代码的,但我猜您的库或代码目前仍在进行一些初始化工作

Romain Guy分享了一些加快初始加载的技巧。

1)您可以将大量初始化转移到
异步任务中。在活动完成之前,第一个屏幕不会显示
onCreate()

2) 是和否,取决于如何、什么和在哪里。大多数用户都可以理解带有一些加载信息的简单屏幕。不管怎样,大多数顶级游戏都带有某种品牌屏幕

3) 我不知道您是如何实现代码的,但我猜您的库或代码目前仍在进行一些初始化工作

Romain Guy分享了一些加速初始加载的技巧。

1)如果你想在加载资源时显示启动屏幕,你应该在后台线程上尽可能多地加载。这是一个很好的实践,释放GL线程来做任何你想做的事情,因为它并不忙于加载资源。不过,与OpenGL的任何直接交互都需要在GL线程上完成,比如上传纹理或编译着色器

通常,您将使用
AsyncTask
加载需要与UI线程交互的后台资源。但是,
GLSurfaceView
不使用GL的主UI线程,因此
AsyncTask
在这里不起作用。不过,我们可以通过其他方式很容易地获得类似的行为。只需使用
GLSurface.queueEvent()
,将资源代码加载到自己的后台线程上,并将结果发布到GL线程上即可。后台资源加载可以通过多种方式完成;在本例中,让我们使用一个具有单个后台线程的执行器。所有内容都加载到
onSurfaceChanged()
,确保在用户暂停/停止并启动/恢复应用程序时重新创建:

private Executor mExecutor = Executors.newSingleThreadExecutor();

@Override
public void onSurfaceChanged(final int width, final int height) {
    // Let's define a few states for the application; NONE, SPLASH,
    // GAME.
    mRenderState = NONE;

    // Currently, the screen is black. Let's get something on screen as
    // quickly as possible. Create a thread and start loading resources.
    mExecutor.execute(new Runnable() {
        @Override
        public void run() {
            final Resources res = mContext.getResources();

            // Load splash screen resource on background thread.
            final Bitmap splashBitmap = BitmapFactory.decodeResource(res, R.drawable.splash);

            // Splash screen is loaded, post a Runnable that will run on the GL thread.
            mGLSurfaceView.queueEvent(new Runnable() {
                @Override
                public void run() {
                    // Upload splash texture to GL, on GL thread.
                    GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, splashBitmap, 0);
                    // Recycle bitmap. We'll create a lot of bitmaps during loading.
                    // Each bitmap is only needed until we have uploaded it to GL. After
                    // that it's just wasting memory. Especially on older phones.
                    splashBitmap.recycle();
                    // Notify application that the splash screen is loaded,
                    // and it's okay to render it.
                    mRenderState = SPLASH;

                    // Screen is no longer black. User sees a splash screen,
                    // perhaps with a progress bar or a nice animation.

                    // Start loading the other textures.
                    // ...
                    // Texture loading finished.
                    mRenderState = GAME;
                }
            });
        }
    });
}

@Override
public void onDrawFrame()
{
if (mRenderState == SPLASH) {
        // Render splash screen and a progress bar.
    } else if (mRenderState == GAME) {
        // Render game.
    }
}
2)您可以测量加载启动屏幕所需的时间,并在快速设备和慢速设备上进行比较。如果它的加载速度与fast设备一样快,只需不将mRenderState设置为SPLASH,用户就会一直看到一个黑屏。希望黑屏能在不到一秒钟内被游戏取代。如果需要,可以在加载资源时添加更多计时,如果需要的时间太长,请启用初始渲染状态

此外,淡入淡出闪屏可能会让用户感到烦恼,而不仅仅是将闪屏弹出或弹出

3)有几件事可能会导致口吃,但在放弃之前,我至少会检查一下这些:

a) 内存管理。Android限制了每个进程的内存堆大小,而较旧的设备每个进程的堆内存比较新的设备少得多,比如16-32MB对128MB。这意味着,当您加载许多繁重的资源时,垃圾收集器将在旧设备上做更多的工作。在“logcat”中查找大量类似以下信息:

D/dalvikvm: GC_CONCURRENT freed 4027K, 30% free 29173K/41635K, paused 3ms+8ms
这些可能仅仅是您先前加载的位图被从内存中清除,或者您正在创建许多其他对象,这些对象触发旧设备上的GC。希望通过在加载时循环使用位图可以缓解这一问题

b) 只需检查其他进程是否占用CPU
adb shell top -m 10