UI线程在随机时间后停止运行

时间:2016-07-09 21:14:22

标签: java android multithreading

我这个问题已经存在了大约一个月了,我在这里提出了一个不同的问题:https://stackoverflow.com/questions/38005624/activity-stops-after-certain-time?noredirect=1#comment63454873_38005624

该问题似乎是UI线程的问题。在看似随机的运行时间之后,UI线程似乎停止了工作。使用'.post()'或'runOnUIThead()'传递给它的任何'Runnable'对象都不会运行。即使是简单的日志消息。发生问题时,onClickListener对象也不会触发。我已经开始在开始的2秒内开始运行,我已经跑完了整个游戏,没有任何问题;一切都在相同的代码上。

如果您需要,我的代码如下:
活动:

package edu.rit.jacob.timeturner;

import android.app.ActivityManager;
import android.app.Application;
import android.app.Dialog;
import android.content.Intent;
import android.os.Looper;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Layout;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;

public class UniverseActivity extends AppCompatActivity {

public static final String TAG = UniverseActivity.class.getSimpleName();

private UniverseThread thread;
public UniverseThread getThread(){ return thread; }

public static int SECTORS_HEIGHT = 3;
public static int SECTORS_WIDTH = 3;

private Galaxy player;
public Galaxy getPlayer() {
    return player;
}

//components:
private UniverseView surfaceView;
private ImageView playerSprite;
private ImageView otherSprite;
private TextView testText;
private SeekBar speedBar;
private ProgressBar timebar;

//Pop ups
private Dialog infoPopUp;
private Dialog startScreen;

//other Galaxies
Galaxy[] galaxies = new Galaxy[SECTORS_HEIGHT * SECTORS_WIDTH];

@Override
protected void onCreate(Bundle savedInstanceState) {
    //Setup Activity
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    setContentView(R.layout.activity_universe);

    Runnable init = new Runnable() {
        @Override
        public void run() {
            initInfoPopUp();
            initActivityComponents();
            initPlayerAndSprites();
            initOtherGalaxies();
            initStartScreen();
        }
    };
    runOnUiThread(init);

    thread = new UniverseThread(surfaceView.getHolder(), surfaceView);
    thread.setRunning(true);
    thread.start();

    startScreen.setTitle("The Beginning...");
    startScreen.show();

    Log.d(TAG, "App launched with " + ((ActivityManager)getSystemService(ACTIVITY_SERVICE)).getLargeMemoryClass() + "MB max");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    boolean retry = true;
    while(retry){
        try{
            thread.join();
            retry = false;
        }
        catch (InterruptedException e){
            //try again
        }
    }
}

private void initStartScreen(){
    startScreen = new Dialog(this);
    startScreen.setContentView(R.layout.new_game_start);

    ((TextView)startScreen.findViewById(R.id.textView4)).setText(getString(R.string.start_game_info));

    startScreen.findViewById(R.id.buttonStart).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            player.setName("" + ((EditText)startScreen.findViewById(R.id.editText)).getText());
            startScreen.dismiss();
        }
    });
}

private void initInfoPopUp(){
    infoPopUp = new Dialog(this);
    infoPopUp.setContentView(R.layout.info_popup);
    Button infoOption1 = (Button) infoPopUp.findViewById(R.id.option1);
    Button infoOption2 = (Button) infoPopUp.findViewById(R.id.option2);

    infoOption1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "Option 1 clicked");
            //player.incrementGas(galaxies[player.getSector() - 1].getGas());
            player.merge(galaxies[player.getSector() - 1], true);
            galaxies[player.getSector() - 1] = null;
            infoPopUp.dismiss();
        }
    });

    infoOption2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "Option 2 clicked");
            //player.incrementGas(galaxies[player.getSector() - 1].getGas() / 2);
            //galaxies[player.getSector() - 1].incrementGas(-galaxies[player.getSector() - 1].getGas() / 2);
            player.merge(galaxies[player.getSector() - 1], false);
            infoPopUp.dismiss();
        }
    });
}

private void initActivityComponents(){
    surfaceView = (UniverseView) findViewById(R.id.surfaceView);
    testText = (TextView) findViewById(R.id.testText);
    speedBar = ((SeekBar)findViewById(R.id.seekBar));
    timebar = ((ProgressBar)findViewById(R.id.progressBar));
}

private void initPlayerAndSprites(){
    playerSprite = (ImageView) findViewById(R.id.playerGalaxy);
    player = new Galaxy("Milky Way", true, new Velocity(), playerSprite, 25.0f, 1, this);
    otherSprite = (ImageView) findViewById(R.id.otherGalaxy);

    //Tapping on player galaxy
    playerSprite.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "Player sprite clicked, stopping time and bringing up window...");
            speedBar.setProgress(0);
            infoPopUp.setTitle(player.getName());
            infoPopUp.findViewById(R.id.infoPicture).setBackgroundResource(R.drawable.random_temp_galaxy_01);
            infoPopUp.findViewById(R.id.option1).setVisibility(View.INVISIBLE);
            infoPopUp.findViewById(R.id.option2).setVisibility(View.INVISIBLE);
            ((TextView)infoPopUp.findViewById(R.id.infoText)).setText(player.toString());
            infoPopUp.show();
        }
    });

    //Tapping on other galaxy
    otherSprite.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(otherSprite.getVisibility() == View.VISIBLE){
                Log.d(TAG, "Other sprite clicked, stopping time and bringing up window...");
                speedBar.setProgress(0);
                infoPopUp.setTitle(galaxies[player.getSector() - 1].getName());
                infoPopUp.findViewById(R.id.infoPicture).setBackgroundResource(R.drawable.random_temp_galaxy_01);
                infoPopUp.findViewById(R.id.option1).setVisibility(View.INVISIBLE);
                infoPopUp.findViewById(R.id.option2).setVisibility(View.INVISIBLE);
                ((TextView)infoPopUp.findViewById(R.id.infoText)).setText(galaxies[player.getSector() - 1].toString());
                infoPopUp.show();
            }
        }
    });
}

private void initOtherGalaxies(){
    for(Galaxy g: galaxies) g = null;
    int i = 0, galaxiesNum = 0;
    Log.d(TAG, "Populating the universe...");
    while(i < galaxies.length){
        double rand = Math.random();
        if(rand <= 0.25){
            galaxies[i] = new Galaxy("Random galaxy", new Velocity(0,0), otherSprite, 25.0f, i + 1, (Math.random()), this);
            galaxiesNum += 1;
        }
        i += 1;
    }
    Log.d(TAG, "Universe populated with " + galaxiesNum + " galaxies.");
}

public int getSpeed(){
    return speedBar.getProgress();
}

public void setTimeProgress(int progress){
    try {
        if (timebar.getProgress() != progress) {
            switch (progress) {
                case 10:
                    //((ProgressBar) findViewById(R.id.progressBar)).getProgress()
                    progress += 1;
                    //bring up new popup
                    break;
                case 50:
                    //do stuff
                    break;
                case 100:
                    //do end game stuff
                    boolean retry = true;
                    while (retry) {
                        try {
                            thread.join();
                            retry = false;
                        } catch (InterruptedException e) {
                            //try again
                        }
                    }
            }
            timebar.setProgress(progress);
        }
    }
    catch (NullPointerException e){
        Log.d(TAG, "Failed to grab progressBar");
        e.printStackTrace();
    }
}

public void changeBackground(){
    Runnable changeBackground = new Runnable() {
        @Override
        public void run() {
            //Set new background
            Log.d(TAG, "Changing to background " + player.getSector());
            //surfaceView.setBackgroundResource(R.drawable.background01 + (player.getSector() - 1));
            testText.setText("Sector: " + player.getSector());

            //Check for other galaxy
            if(galaxies[player.getSector() - 1] != null){
                otherSprite.setVisibility(View.VISIBLE);
            }
            else{
                otherSprite.setVisibility(View.INVISIBLE);
            }
            synchronized (this) {
                this.notify();
            }
        }
    };
    Log.d(TAG, "Passing background runnable to the UI thread");
    synchronized (changeBackground){
        runOnUiThread(changeBackground);
        try {
            changeBackground.wait();
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public void checkForCollision(){
    if(playerSprite.getX() + (playerSprite.getWidth() / 2) >= otherSprite.getX() &&
            playerSprite.getX() + (playerSprite.getWidth() / 2) <= otherSprite.getX() + otherSprite.getWidth() &&
            playerSprite.getY() + (playerSprite.getHeight() / 2) >= otherSprite.getY() &&
            playerSprite.getY() + (playerSprite.getHeight() / 2) <= otherSprite.getY() + otherSprite.getHeight()){
        if(otherSprite.getVisibility() == View.VISIBLE){
            //switch (player.)

            Runnable collide = new Runnable() {
                @Override
                public void run() {
                    speedBar.setProgress(0);
                    otherSprite.setVisibility(View.INVISIBLE);
                    ((TextView)infoPopUp.findViewById(R.id.infoText)).setText("");
                    //infoPopUp.findViewById(R.id.infoPicture).setBackgroundResource(R.drawable.hubble_merger_01);
                    infoPopUp.findViewById(R.id.option1).setVisibility(View.VISIBLE);
                    infoPopUp.findViewById(R.id.option2).setVisibility(View.VISIBLE);
                    infoPopUp.setTitle("Incoming!");
                    infoPopUp.show();
                }
            };
            runOnUiThread(collide);
        }
    }

    //Set gauge
    Runnable setGauge = new Runnable() {
        @Override
        public void run() {
            ((GaugeView)findViewById(R.id.gaugeView)).setTargetValue(player.getGas());
        }
    };
    runOnUiThread(setGauge);
}

}

线程:

package edu.rit.jacob.timeturner;

import android.content.Context;
import android.graphics.Canvas;
import android.hardware.*;
import android.hardware.SensorManager;
import android.provider.Settings;
import android.util.Log;
import android.view.SurfaceHolder;
import android.widget.ImageView;

public class UniverseThread extends Thread {

public static final String TAG = UniverseThread.class.getSimpleName();

private double time;
public double getTime(){
    return time;
}

private double speed;
public double getSpeed(){
    return speed;
}

private boolean running;
public void setRunning(boolean running){
    this.running = running;
}
public boolean isRunning(){
    return running;
}

//Reference to the UniverseActivity
private SurfaceHolder surfaceHolder;

//Reference to the UniverseView that is the background
private UniverseView universeView;

public UniverseThread(SurfaceHolder surfaceHolder, UniverseView universeView){
    super();
    this.surfaceHolder = surfaceHolder;
    this.universeView = universeView;
    running = false;
    time = 0;
}

public static float x = 0, y = 0, z = 0;

@Override
public void run(){
    Log.d(TAG, "Starting Game Loop");
    time = 0.0;
    speed = 0.0;

    //SensorManager gets SENSOR_SERVICE from android
    //Accesses gravity sensor
    SensorManager sm = (SensorManager) this.universeView.getContext().getSystemService(Context.SENSOR_SERVICE);
    sm.registerListener(new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            x = event.values[0];
            y = event.values[1];
            z = event.values[2];
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    }, sm.getDefaultSensor(Sensor.TYPE_GRAVITY), SensorManager.SENSOR_DELAY_GAME);

    //save player, velocity, and sprite
    Galaxy player = ((UniverseActivity)universeView.getContext()).getPlayer();
    Velocity vel = player.getVel();
    ImageView sprite = player.getSprite();
    boolean changeBackground;

    float timesRun = 0f;

    while(running){
        //every 16 milliseconds (~60 Hz)
        timesRun += 1;
        final String testStr = "Thread has run for " + (timesRun) + " frames.";
        Runnable test = new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, testStr);
            }
        };
        if(timesRun % 60 == 0) ((UniverseActivity) universeView.getContext()).runOnUiThread(test);

        //increment the time
        speed = ((UniverseActivity)universeView.getContext()).getSpeed();
        time += (speed / 7200.0);
        ((UniverseActivity)universeView.getContext()).setTimeProgress((int) time);

        //update player's velocity
        ((UniverseActivity)universeView.getContext()).getPlayer().getVel().incrementXV(-x / 9.81f);
        ((UniverseActivity)universeView.getContext()).getPlayer().getVel().incrementYV(y / 9.81f);

        //update player's position
        //move sprite
        sprite.setX((float)(sprite.getX() + (vel.getxV() * speed / 400)));
        sprite.setY((float)(sprite.getY() + (vel.getyV() * speed / 400)));

        changeBackground = false;
        //bottom -> top
        if (sprite.getY() + (sprite.getHeight() / 2) > universeView.getHeight()) {
            sprite.setY(0 - (sprite.getHeight() / 2));
            sprite.setX(universeView.getWidth() / 2);
            player.updateSector("BOTTOM");
            changeBackground = true;
        }
        //right -> left
        if (sprite.getX() + (sprite.getWidth() / 2) > universeView.getWidth()) {
            sprite.setY(universeView.getHeight() / 2);
            sprite.setX(0 - (sprite.getWidth() / 2));
            player.updateSector("RIGHT");
            changeBackground = true;
        }
        //top -> bottom
        if (sprite.getY() + (sprite.getHeight() / 2) < 0) {
            sprite.setY(universeView.getHeight() - (sprite.getHeight() / 2));
            sprite.setX(universeView.getWidth() / 2);
            player.updateSector("TOP");
            changeBackground = true;
        }
        //left -> right
        if (sprite.getX() + (sprite.getWidth() / 2) < 0) {
            sprite.setY(universeView.getHeight() / 2);
            sprite.setX(universeView.getWidth() - (sprite.getWidth() / 2));
            player.updateSector("LEFT");
            changeBackground = true;
        }
        if(changeBackground)
        {((UniverseActivity)universeView.getContext()).changeBackground();}

        ((UniverseActivity)universeView.getContext()).checkForCollision();

        try{
            this.sleep(16L);
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    Log.d(TAG, "Game Loop terminated");
}

}

要清楚,在运行时期间不会抛出任何异常或错误。例如,logcat中唯一的消息是我自己的,以及用于注册点击的系统消息。应用程序不会崩溃,精灵仍然是完全可控的。无法解决的主要问题是更改背景,检查碰撞以及onClickListeners。

有没有人遇到过这样的事情?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

因此,在将问题缩小到我传递给UI线程的Runnable对象之后,我采取措施尽量减少我调用runOnUIThread()的时间,最初它没有似乎有任何影响,但最终我停止了这个问题,这是我唯一可以贡献它的东西。我可能最终会做一些更具体的测试,以解决问题和相应的解决方案。