1

First of all, I don't speak English, so please forgive me for my terrible grammar.

I try to create Android game, using GLSurfaceView. I read "Beginning of Android Game" (the link is sample code) in Japanese and imitate its code, so my game almost works fine. But I have a problem. When android turns off the screen, onPause of the activity is called as expected. But sometimes (not always) onResume is called and then onPause() is called again, although the screen is still black. In onResume I call GLSurfaceView().resume, so onSurfaceCreated() and onSurfaceChanged() are also called. After this, the game puts out ANR.

[Expected flow]
onPause - (turn on the screen) - onResume - onSurfaceCreated - onSurfaceChanged
[Problematic flow]
onPause - onResume - onPause - onSurfaceCreated - onSurfaceChanged - (turn on the screen) - ANR

Even in the problematic case, GLSurfaceView.onDrawFrame is called normally, and the game is being rendered. But it doesn't accept any tap.Is there infinite loop?

I found similar questions here and here, for example. But I think there is no configuration Change. In Android Manifest,

android:configChanges="keyboard|keyboardHidden|orientation|screenSize

was already declared. And I can't move my code into Application class because I want to do something (e.g. reload some textures) in onSurfaceCreated method.

So my question is...

Why is the onResume method called?

Why it causes ANR?
And How can I avoid this ANR?
EDIT: the problem was solved.I appreciate it!

Here is my activity.

public abstract class GLGame extends Activity implements Game, Renderer {
    enum GLGameState {
        Initialized,
        Running,
        Paused,
        Finished,
        Idle
    }
    /////////////////////////////inter-ad
    private static class AdHandler extends Handler {
        private final WeakReference<GLGame> mActivity;
        AdHandler(GLGame activity) {
            mActivity = new WeakReference<GLGame>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            GLGame activity = mActivity.get();
            switch(msg.what){
            case 0:
                if (activity != null) {
                    activity.showInterstitial();
                }
                break;
            case 1:
                if (activity != null) {
                    activity.prepareInterstitial();
                }
                break;
            }

            return;
        }
    };
    Handler handler =new AdHandler(this);
    /////////////////////////////inter-ad    

    //ad
    static InterstitialAd interstitial;
    static AdRequest adRequest;
    AdView adView;
    //////////////////////////////// //ad

    GLSurfaceView glView;
    GLGraphics glGraphics;
    Audio audio;
    Input input;
    FileIO fileIO;
    Screen screen;
    GLGameState state = GLGameState.Initialized;
    Object stateChanged = new Object();
    long startTime = System.nanoTime();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        /////////////////////////////calculate and normalize the view size    
        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);

        float density = displaymetrics.densityDpi/160f;
        int adpix = (int)( 50*density);
        int navvarpix = (int)( 25*density);
        int heightSubAd = displaymetrics.heightPixels -navvarpix-adpix;

        int ratioWidth=10;
        int ratioHeight=16;
        boolean isWidthLong = displaymetrics.widthPixels/(float)heightSubAd > ratioWidth/(float)ratioHeight;
        int width = isWidthLong ? heightSubAd * ratioWidth / ratioHeight : displaymetrics.widthPixels;
        int height = isWidthLong ? heightSubAd : displaymetrics.widthPixels * ratioHeight/ratioWidth;

        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(width, height);
        lp.setMargins((displaymetrics.widthPixels-width)/2, 0, 0, (displaymetrics.widthPixels-width)/2);
        ///////////////////////////// //calc    

        glView = new GLSurfaceView(this);
        glView.setLayoutParams(lp);
        glView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);
        glView.setRenderer(this);

        adView = new AdView(this, AdSize.BANNER, "MY_NUMBER");
        lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, adpix);
        lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
        adView.setLayoutParams(lp);

        RelativeLayout layout = new RelativeLayout(this);
        layout.setBackgroundColor(Color.BLACK);
        layout.setId(0);
        layout.addView(glView);
        layout.addView(adView);
        adView.loadAd(new AdRequest());

        setContentView(layout);

        /////////////////////////////////request inter-ad
        interstitial = new InterstitialAd(this, "MY_NUMBER");
        adRequest = new AdRequest();
        adRequest.addTestDevice("MY_NUMBER");
        adRequest.addTestDevice("MY_NUMBER");
        interstitial.loadAd(adRequest);
        /////////////////////////////////inter-ad

        glGraphics = new GLGraphics(glView);
        fileIO = new AndroidFileIO(getAssets());
        audio = new AndroidAudio(this);
        input = new AndroidInput(this, glView, 1, 1);
    }

    public void onResume() {
        super.onResume();
        glView.onResume();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glGraphics.setGL(gl);

        synchronized(stateChanged) {
            if(state == GLGameState.Initialized)
                screen = getStartScreen();
            state = GLGameState.Running;
            screen.resume();
            startTime = System.nanoTime();
        }
    }

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

    @Override
    public void onDrawFrame(GL10 gl) {
        GLGameState state = null;

        synchronized(stateChanged) {
            state = this.state;
        }

        if(state == GLGameState.Running) {
            float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
            startTime = System.nanoTime();

            screen.update(deltaTime);
            screen.present(deltaTime);
        }

        if(state == GLGameState.Paused) {
            screen.pause();
            synchronized(stateChanged) {
                this.state = GLGameState.Idle;
                stateChanged.notifyAll();
            }
        }

        if(state == GLGameState.Finished) {
            screen.pause();
            screen.dispose();
            synchronized(stateChanged) {
                this.state = GLGameState.Idle;
                stateChanged.notifyAll();
            }
        }
    }

    @Override
    public void onPause() {
        synchronized(stateChanged) {
            if(isFinishing())
                state = GLGameState.Finished;
            else
                state = GLGameState.Paused;
            while(true) {
                try {
                    stateChanged.wait();
                    break;
                } catch(InterruptedException e) {
                }
            }
        }
        glView.onPause();
        super.onPause();
    }

    public GLGraphics getGLGraphics() {
        return glGraphics;
    }

    @Override
    public Input getInput() {
        return input;
    }

    @Override
    public FileIO getFileIO() {
        return fileIO;
    }

    @Override
    public Graphics getGraphics() {
        throw new IllegalStateException("We are using OpenGL!");
    }

    @Override
    public Audio getAudio() {
        return audio;
    }

    @Override
    public void setScreen(Screen screen) {
        if (screen == null)
            throw new IllegalArgumentException("Screen must not be null");

        this.screen.pause();
        this.screen.dispose();
        screen.resume();
        screen.update(0);
        this.screen = screen;
    }

    @Override
    public Screen getCurrentScreen() {
        return screen;
    }

    @Override
    public Handler getHandler(){
        return handler;
    }

    private void showInterstitial(){
        if(interstitial.isReady()){
            interstitial.show();
        }
    }

    private void prepareInterstitial(){
        interstitial.loadAd(adRequest);
    }

}

thank you for your kindness!

Community
  • 1
  • 1
kyomope
  • 21
  • 2
  • You can get idea about what is the actually reason for ANR by anr trace file. DDMS -> File Explorer -> data -> anr -> traces.text – justDroid Mar 09 '15 at 12:41
  • @maveň Thank you for your EDIT. I should have written this way. It's really readable! – kyomope Mar 09 '15 at 14:54
  • @justDroid Thank you for your advice. And I **am** idiot. As soon as I read the `traces.txt`, I noticed that there is obviously doubtful code. In `onPause()` I call `stateChanged.wait()`. And it actually stuck. It works when the game is running and `onDrawFrame()` is usually being called. But in this case, `onSurfaceCreate()` is called just after `onPause()` unexpectedly, so `wait()` never ends. I add `stateChanged.notifyAll()` to `onSurfaceCreate` and then ANR doesn`t appear again! Thanks a lot! – kyomope Mar 09 '15 at 15:23
  • Now, my remaining question is just one thing. Why `resume()` is called when the screen turns off? – kyomope Mar 09 '15 at 15:29

0 Answers0