应用因音乐而“无响应”,但实际上应用没有问题

时间:2019-05-21 20:38:52

标签: java android

我已经为Android创建了一个运行良好的游戏,但是由于某种原因,背景声音在玩游戏时经常在MainActivity中为我提供以下内容:

I/art: Thread[2,tid=30569,WaitingInMainSignalCatcherLoop,Thread*=0x7f73a25400,peer=0x12cd40a0,"Signal Catcher"]: reacting to signal 3
I/art: Wrote stack traces to '/data/anr/traces.txt'

(这意味着该应用已停止响应)

我不知道为什么,因为如果我按“等待”并继续玩游戏,游戏仍然可以正常运行。我有一个单独的服务类,该类运行背景声音,并且在添加类时开始时并没有遇到这些困难,所以我觉得我在整个过程中一定出错了。我也很累将所有内容都放在try / catch的backgroundsound类中以尝试查找问题,但这没有帮助。

下面是我的MainActivity(在没有声音的情况下效果很好)和带有背景声音的单独的服务类。

public class MainActivity extends AppCompatActivity implements GarbageListener {

    //global variable of FishView
    private FishView gameView;

    //handle animation task
    private final Handler handler = new Handler();

    //global variable of screen
    private RelativeLayout screen;

    //time before level update
    private int levelChangeTime = 2; //initialize small garbage in X seconds
    private int spawnBossGarbage = 3; //initialize big garbage in X seconds
    private int spawnHeart = 3; //initialize heart in X seconds

    //pause variables
    private Button pauseButton;
    private boolean pauseFlag = false;

    //left and right button
    private Button leftButton;
    private Button rightButton;

    //List of small garbage on screen
    private final List<SmallGarbage> smallGarbages = new ArrayList<>();
    //List of big garbage on screen
    private List<BigGarbage> bigGarbages = new ArrayList<>();
    //List of heart on screen
    private List<LifePoint> lifePoints = new ArrayList<>();

    //create timer for animation and level increase
    private Timer mainTimer;

    //create timer fro holding left or right
    private Timer movingLeft;
    private Timer movingRight;
    private final boolean buttonIsPressed = false; //so players can't hold both buttons down
    private final int holdMovementPeriod = 9;

    //keep track of song
    public static Intent themeSong;
    //keep track of how far we are in the song, serviceStop() deletes everything in service ThemeSong so variable must be saved elsewhere
    private static int lengthOfSong = 0;
    private static boolean backButtonPressed = false; //check if backButton was pressed in service ThemeSong oonDestroy() since that's the last thing that is run

    //save LinearLayout for buttons here and have static buttonsCorY (so FishView can reach it) which is the same as the linearLayout's y
    private LinearLayout linearLayoutMoveButtons;
    public static int buttonsCorY = 0;

    //handling events if we pressed restart on game over screen then press back button here it should go to start menu
    private static boolean haveBeenGameOver = false;

    //adjust spawns
    private static int adjustSpawns;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        themeSong=new Intent(this, ThemeSong.class);
        startService(themeSong); //OR stopService(svc);

        //can't set this above as a field as that would generate NullPointerException
        linearLayoutMoveButtons = findViewById(R.id.buttonsLinearLayout);

        leftButton = findViewById(R.id.leftArrow);
        rightButton = findViewById(R.id.rightArrow);

        screen = findViewById(R.id.gameScreen);
        gameView = new FishView(this);
        screen.addView(gameView);

        pauseButton = findViewById(R.id.pauseButton);

        mainTimer = new Timer();
        createNewAnimationTask();
        createNewLevelTask();

        //start adjustment at 0
        adjustSpawns = 0;

        //create listeners fo holding left or right button
        findViewById(R.id.leftArrow).setOnTouchListener(new View.OnTouchListener() {
            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    holdLeft();
                    rightButton.setEnabled(false);}
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    rightButton.setEnabled(true);
                    if (movingLeft!=null){
                    movingLeft.cancel();
                    }}
                return false;}
        });

        findViewById(R.id.rightArrow).setOnTouchListener(new View.OnTouchListener() {
            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    holdRight();
                    leftButton.setEnabled(false);}
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    leftButton.setEnabled(true);
                    if (movingRight!=null){
                    movingRight.cancel();}}
                return false;}
        });
        System.out.println("don with this");
    }

    //this is run after onCreate(), have to have this otherwise it won't have time to recognize Y position of LinearLayout
    @Override
    public void onWindowFocusChanged (boolean hasFocus) {
        int[] xy = new int[2];
        linearLayoutMoveButtons.getLocationOnScreen(xy);
        buttonsCorY = xy[1];
    }

    public void moveLeft(@SuppressWarnings("unused") View v){
        if (buttonIsPressed){return;}
        gameView.setLeftPressed(true);
        gameView.leftFishAnimation();//before running the animations we first set which fish animations to run (left or right)
        gameView.invalidate();
    }

    public void moveRight(@SuppressWarnings("unused") View view) {
        if (buttonIsPressed){return;}
        gameView.setRightPressed(true);
        gameView.rightFishAnimation();
        gameView.invalidate();
    }

    public void pauseGame(@SuppressWarnings("unused") View v){
        String resume = "Resume";
        String pause = "Pause";
        if (!pauseFlag){
            stopService(themeSong); //turn of music
            pauseFlag = true;
            pauseButton.setText(resume);
            pauseButton.setBackgroundResource(R.drawable.roundbuttonred);

            //disable animation and level tasks
            mainTimer.cancel();
            //disable all falling garbage on screen
            for (SmallGarbage smallGarbage : smallGarbages) {smallGarbage.disableGarbageTimer();}
            for (BigGarbage bigGarbage : bigGarbages) {bigGarbage.disableGarbageTimer();}
            for (LifePoint lifePoint : lifePoints) {lifePoint.disableGarbageTimer();}
            //disable buttons
            leftButton.setEnabled(false);
            rightButton.setEnabled(false);

        }
        else{
            startService(themeSong); //start music
            pauseFlag=false;
            pauseButton.setText(pause);
            leftButton.setEnabled(true);
            rightButton.setEnabled(true);
            pauseButton.setBackgroundResource(R.drawable.roundbuttonblue);
            //resume falling garbage
            for (SmallGarbage smallGarbage : smallGarbages) {smallGarbage.startFallingGarbage();}
            for (BigGarbage bigGarbage : bigGarbages) {bigGarbage.startFallingGarbage();}
            for (LifePoint lifePoint : lifePoints) {lifePoint.startFallingGarbage();}
            //resume animation and level increase
            mainTimer = new Timer();
            createNewAnimationTask();
            createNewLevelTask();
        }

    }

    private void createNewAnimationTask(){
        TimerTask newAnimationTask = new TimerTask() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //here we set the animation
                        int selectedFish = gameView.getSelectedFish();
                        selectedFish ++;
                        if (selectedFish==2){
                            selectedFish = 0;}

                        gameView.setSelectedFish(selectedFish);

                        //update screen
                        gameView.invalidate();
                    }
                });
            }
        };
        long animationPeriod = 600;
        mainTimer.scheduleAtFixedRate(newAnimationTask, 0, animationPeriod);
    }

    private void createNewLevelTask(){
        TimerTask levelCountDown = new TimerTask(){
            @Override
            public void run() {
                levelChangeTime--;
                spawnBossGarbage--;
                spawnHeart--;
                if (levelChangeTime==0 || spawnBossGarbage == 0 || spawnHeart == 0){
                    //move task that updates the UI onto the main thread
                    runOnUiThread(new Runnable() { //this tells the program to run this on the UI(aka main) thread, we could also call on new Thread if wanted to start new thread
                        @Override
                        public void run() {
                            if (levelChangeTime==0){generateNewGarbage("smallGarbage");}
                            if (spawnBossGarbage==0){generateNewGarbage("bigGarbage");}
                            if (spawnHeart==0){generateNewGarbage("lifePoint");}// when this is added we can't lose life?
                        }
                    });
                }
            }
        };
        mainTimer.scheduleAtFixedRate(levelCountDown,0,1000);
    }


    private void holdLeft(){
        movingLeft = new Timer();
        final View v = new View(this); //create view so moveLeft() can called
        TimerTask holdLeftTask = new TimerTask(){
            @Override
            public void run() {
                handler.post(new Runnable() {
            @Override
            public void run() {
                moveLeft(v);
            }
                });
        }};
        movingLeft.scheduleAtFixedRate(holdLeftTask,0,holdMovementPeriod);
    }

    private void holdRight(){
        movingRight = new Timer();
        final View v = new View(this);
        TimerTask holdRightTask = new TimerTask(){
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        moveRight(v);
                    }
                });
            }};
        movingRight.scheduleAtFixedRate(holdRightTask,0,holdMovementPeriod);
    }

    private void generateNewGarbage(String garbage){
        switch (garbage){
            case "bigGarbage":
                //make sound for spawning big garbage
                spawnBossGarbage =  new Random().nextInt(45-adjustSpawns)+30; //time to next spawn
                BigGarbage newBigGarbage = new BigGarbage(MainActivity.this);
                newBigGarbage.setListener(MainActivity.this);
                bigGarbages.add(newBigGarbage);
                screen.addView(newBigGarbage);
                break;
            case "smallGarbage":
                //set timer for next object
                levelChangeTime = new Random().nextInt(20)+2; //set seconds between 2 and 20 at random
                //this create SmallGarbage and initialize its task
                SmallGarbage newGarbage = new SmallGarbage(MainActivity.this);
                newGarbage.setListener(MainActivity.this); // set listener for garbage
                smallGarbages.add(newGarbage);
                screen.addView(newGarbage);
                break;
            case "lifePoint":
                spawnHeart= 5; //new Random().nextInt(30-adjustSpawns)+20; //time to next life spawn
                //this create SmallGarbage and initialize its task
                LifePoint newLifePoint = new LifePoint(MainActivity.this);
                newLifePoint.setListener(MainActivity.this); // set listener for garbage
                lifePoints.add(newLifePoint);
                screen.addView(newLifePoint);
                break;
        }
    }

    //here starts the GarbageListener
    @Override
    public void handleAvoidedGarbage(String avoidedGarbage) {
        gameView.avoidedGarbage(avoidedGarbage);
    }

    @Override
    public boolean handleHitPlayer(int x, int y, String garbageType) {
        return gameView.hitWasteChecker(x,y, garbageType);
    }

    @Override
    public void handleLoseLife() {
        gameView.loseLife();
    }


    //empty lives on screen, once they have landed or hit player
    @Override
    public void emptyLifePointList(){
        lifePoints.clear();
        lifePoints = new ArrayList<>();
    }

    //empty big garbage on screen, once they have landed or hit player
    @Override
    public void emptyBigGarbageList(){
        bigGarbages.clear();
        bigGarbages = new ArrayList<>();
    }

    //saving and setting length of played song
    public static int getLengthOfSong() {
        return lengthOfSong;
    }

    public static void setLengthOfSong(int lengthOfSong) {
        MainActivity.lengthOfSong = lengthOfSong;
    }

    //onStop runs AFTER onBackPressed(), so lengthOfSong must be reset there
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        backButtonPressed = true;
        //if we pressed back after pressed restart go back to start menu
        if (itHasBeenGameOver()){
            setHaveBeenGameOver(false);
            backToStartMenu();
        }
    }

    public static boolean isBackButtonPressed() {
        return backButtonPressed;
    }

    public static void setBackButtonPressed(boolean backButtonPressed) {
        MainActivity.backButtonPressed = backButtonPressed;
    }

    private void backToStartMenu() {
        Intent startMenuIntent = new Intent(this, MenuActivity.class);
        startActivity(startMenuIntent);
        overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
        finish(); //end this activity
    }

    //this runs whenever the app is closed
    @Override
    protected void onStop(){
        super.onStop();
        //stop music
        if(!pauseFlag){
        stopService(themeSong);
        setLengthOfSong(0);
        //pause game, this will also reset sound upon start
        final View v = new View(this);
        pauseFlag = false;
        pauseGame(v);}
    }

    private static boolean itHasBeenGameOver() {
        return haveBeenGameOver;
    }

    public static void setHaveBeenGameOver(boolean haveBeenGameOver) {
       MainActivity.haveBeenGameOver = haveBeenGameOver;
    }

    public static void setAdjustSpawns(int adjustSpawns) {
        MainActivity.adjustSpawns = adjustSpawns;
    }

}


//creating a service for playing background sound
public class ThemeSong extends Service {
    private MediaPlayer player;

    public IBinder onBind(Intent arg0) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        player = MediaPlayer.create(this, R.raw.themesong);
        player.setLooping(true); // Set looping
        player.setVolume(0.02f,0.02f);

    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        player.seekTo(MainActivity.getLengthOfSong()); //set starting position in song
        //if backButton was pressed in MainActivity then we want to restart theme song
        if (MainActivity.isBackButtonPressed()){
            player.seekTo(0);
            MainActivity.setBackButtonPressed(false); //reset backButtonPressed
        }
        player.start();
        return startId;
    }

    @Override
    public void onDestroy() {
        int lengthOfsong = player.getCurrentPosition(); //save how far we got in the song
        MainActivity.setLengthOfSong(lengthOfsong);
        player.release();
    }


}

当我遇到此错误时,还应注意,我没有玩暂停游戏或未使用电话后退按钮,等等。我只是在MainActivity上开始运行游戏,过了一会儿我总是收到该错误。

我在另一个类上也有类似的情况,在这种情况下,事实证明我有2个“ startService(sound);”。连续,一旦我删除了其中一个,它就解决了。但是,在这种情况下,我找不到任何类似的情况,这使我对问题可能是什么一无所知。我也尝试过在try / catch中设置“ startActivity(sound)”,但这也无济于事。

如果您对造成这种情况的原因有任何了解,请分享,或者在玩游戏时有一种完全忽略此错误的方法也可以解决我的问题。

0 个答案:

没有答案