致命异常:Timer-0?

时间:2018-04-04 14:45:44

标签: java android exception timertask

我正在创建一个计算反应时间的游戏。我正在使用计时器来获取用户按下按钮所需的时间。出于某种原因,当我尝试运行应用程序时,我收到了Timer-0异常。

的logcat

E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.abz.layouts, PID: 16015
                  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
                      at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
                      at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
                      at android.view.View.invalidateInternal(View.java:12713)
                      at android.view.View.invalidate(View.java:12649)
                      at android.view.View.invalidateDrawable(View.java:16788)
                      at android.widget.TextView.invalidateDrawable(TextView.java:5408)
                      at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
                      at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:705)
                      at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:701)
                      at android.graphics.drawable.LayerDrawable.invalidateDrawable(LayerDrawable.java:896)
                      at android.graphics.drawable.DrawableWrapper.invalidateDrawable(DrawableWrapper.java:153)
                      at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
                      at android.graphics.drawable.GradientDrawable.setColorFilter(GradientDrawable.java:837)
                      at android.graphics.drawable.DrawableWrapper.setColorFilter(DrawableWrapper.java:243)
                      at android.graphics.drawable.LayerDrawable.setColorFilter(LayerDrawable.java:1285)
                      at android.graphics.drawable.Drawable.clearColorFilter(Drawable.java:600)
                      at com.example.abz.layouts.HighLight.run(HighLight.java:33)
                      at java.util.Timer$TimerImpl.run(Timer.java:284)
D/EGL_emulation: eglMakeCurrent: 0xae424780: ver 3 0 (tinfo 0xae412ba0)
E/Surface: getSlotFromBufferLocked: unknown buffer: 0xaaac9820
Application terminated.

E/AndroidRuntime: FATAL EXCEPTION: Timer-0 Process: com.example.abz.layouts, PID: 16015 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556) at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942) at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081) at android.view.View.invalidateInternal(View.java:12713) at android.view.View.invalidate(View.java:12649) at android.view.View.invalidateDrawable(View.java:16788) at android.widget.TextView.invalidateDrawable(TextView.java:5408) at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385) at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:705) at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:701) at android.graphics.drawable.LayerDrawable.invalidateDrawable(LayerDrawable.java:896) at android.graphics.drawable.DrawableWrapper.invalidateDrawable(DrawableWrapper.java:153) at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385) at android.graphics.drawable.GradientDrawable.setColorFilter(GradientDrawable.java:837) at android.graphics.drawable.DrawableWrapper.setColorFilter(DrawableWrapper.java:243) at android.graphics.drawable.LayerDrawable.setColorFilter(LayerDrawable.java:1285) at android.graphics.drawable.Drawable.clearColorFilter(Drawable.java:600) at com.example.abz.layouts.HighLight.run(HighLight.java:33) at java.util.Timer$TimerImpl.run(Timer.java:284) D/EGL_emulation: eglMakeCurrent: 0xae424780: ver 3 0 (tinfo 0xae412ba0) E/Surface: getSlotFromBufferLocked: unknown buffer: 0xaaac9820 Application terminated.

这是我的java类

MainActivty.java

package com.example.abz.layouts;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;



public class MainActivity extends AppCompatActivity {

    private long start_time;
    private long end_time;
    private Button finalButton;





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



    @Override
    protected void onStart() {
        super.onStart();



        // Create list of buttons
        ArrayList<Button> buttons = createButtonsArrayList();

        // Generates sequence of 5 random buttons
        RandomSequence randomSequence = new RandomSequence(buttons, 5, 9);

        // Add OnClickListener for last button
        finalButton = randomSequence.sequence.get(randomSequence.sequence.size() - 1);
        finalButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finalButtonClicked();
            }
        });

        // Start Game
        start_time = System.nanoTime();
        randomSequence.startSequence();

    }

    private void finalButtonClicked() {
        end_time = System.nanoTime();
        double diffInMillis = (end_time - start_time) / 1e6;
        finalButton.getBackground().clearColorFilter();
        Toast toast = Toast.makeText(this, "Reaction time: " + diffInMillis + " milliseconds.", Toast.LENGTH_LONG);
        toast.show();
    }

    private ArrayList<Button> createButtonsArrayList() {
        ArrayList<Button> buttons = new ArrayList<>();
        final Button button1 = (Button) findViewById(R.id.button1);
        final Button button2 = (Button)findViewById(R.id.button2);
        final Button button3 = (Button) findViewById(R.id.button3);
        final Button button4 = (Button) findViewById(R.id.button4);
        final Button button5 = (Button) findViewById(R.id.button5);
        final Button button6 = (Button) findViewById(R.id.button6);
        final Button button7 = (Button) findViewById(R.id.button7);
        final Button button8 = (Button) findViewById(R.id.button8);
        final Button button9 = (Button) findViewById(R.id.button9);


        buttons.add(button1);
        buttons.add(button2);
        buttons.add(button3);
        buttons.add(button4);
        buttons.add(button5);
        buttons.add(button6);
        buttons.add(button7);
        buttons.add(button8);
        buttons.add(button9);

        return buttons;
    }
}

HighLight.java

package com.example.abz.layouts; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; import java.util.ArrayList; import java.util.Random; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends AppCompatActivity { private long start_time; private long end_time; private Button finalButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onStart() { super.onStart(); // Create list of buttons ArrayList<Button> buttons = createButtonsArrayList(); // Generates sequence of 5 random buttons RandomSequence randomSequence = new RandomSequence(buttons, 5, 9); // Add OnClickListener for last button finalButton = randomSequence.sequence.get(randomSequence.sequence.size() - 1); finalButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finalButtonClicked(); } }); // Start Game start_time = System.nanoTime(); randomSequence.startSequence(); } private void finalButtonClicked() { end_time = System.nanoTime(); double diffInMillis = (end_time - start_time) / 1e6; finalButton.getBackground().clearColorFilter(); Toast toast = Toast.makeText(this, "Reaction time: " + diffInMillis + " milliseconds.", Toast.LENGTH_LONG); toast.show(); } private ArrayList<Button> createButtonsArrayList() { ArrayList<Button> buttons = new ArrayList<>(); final Button button1 = (Button) findViewById(R.id.button1); final Button button2 = (Button)findViewById(R.id.button2); final Button button3 = (Button) findViewById(R.id.button3); final Button button4 = (Button) findViewById(R.id.button4); final Button button5 = (Button) findViewById(R.id.button5); final Button button6 = (Button) findViewById(R.id.button6); final Button button7 = (Button) findViewById(R.id.button7); final Button button8 = (Button) findViewById(R.id.button8); final Button button9 = (Button) findViewById(R.id.button9); buttons.add(button1); buttons.add(button2); buttons.add(button3); buttons.add(button4); buttons.add(button5); buttons.add(button6); buttons.add(button7); buttons.add(button8); buttons.add(button9); return buttons; } }

RandomSequence.java

package com.example.abz.layouts;

import android.graphics.Color;
import android.graphics.PorterDuff;
import android.widget.Button;

import java.util.ArrayList;
import java.util.TimerTask;


public class HighLight extends TimerTask {

    private ArrayList<Button> sequence;
    private int sequencePosition;

   public HighLight(ArrayList<Button> sequence) {
        this.sequence = sequence;
        sequencePosition = 0;
    }

    public void run() {
        Button activeButton = sequence.get(sequencePosition);
        if (sequencePosition == 0) {
            activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
        } else if (sequencePosition == sequence.size() - 1) {
            Button previousActiveButton = sequence.get(sequencePosition - 1);
            previousActiveButton.getBackground().clearColorFilter();
            activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
            // TODO: PLAY SOUND FOR FINAL BUTTON
            this.cancel();
        } else {
            Button previousActiveButton = sequence.get(sequencePosition - 1);
            previousActiveButton.getBackground().clearColorFilter();
            activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
        }
        sequencePosition++;
    }
}

package com.example.abz.layouts; import android.graphics.Color; import android.graphics.PorterDuff; import android.widget.Button; import java.util.ArrayList; import java.util.TimerTask; public class HighLight extends TimerTask { private ArrayList<Button> sequence; private int sequencePosition; public HighLight(ArrayList<Button> sequence) { this.sequence = sequence; sequencePosition = 0; } public void run() { Button activeButton = sequence.get(sequencePosition); if (sequencePosition == 0) { activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY); } else if (sequencePosition == sequence.size() - 1) { Button previousActiveButton = sequence.get(sequencePosition - 1); previousActiveButton.getBackground().clearColorFilter(); activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY); // TODO: PLAY SOUND FOR FINAL BUTTON this.cancel(); } else { Button previousActiveButton = sequence.get(sequencePosition - 1); previousActiveButton.getBackground().clearColorFilter(); activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY); } sequencePosition++; } }

我一直在寻找解决方案,但没有成功。

1 个答案:

答案 0 :(得分:1)

logcat中的关键行是:

  

只有创建视图层次结构的原始线程才能触及其视图

您正在尝试更改主线程之外的UI元素,在这种情况下,调用previousActiveButton.getBackground().clearColorFilter()中的HighLight.run()

您可以在主线程上创建的run()上发布Handler方法,也可以在runOnUiThread()中包含与UI相关的调用。因为您正在使用扩展TimerTask的自包含类,所以我推荐前者:

public void run() {

    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {

        @Override
        public void run() {

            Button activeButton = sequence.get(sequencePosition);
            if (sequencePosition == 0) {
                activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
            } else if (sequencePosition == sequence.size() - 1) {
                Button previousActiveButton = sequence.get(sequencePosition - 1);
                previousActiveButton.getBackground().clearColorFilter();
                activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
                // TODO: PLAY SOUND FOR FINAL BUTTON
                HighLight.this.cancel();
            } else {
                Button previousActiveButton = sequence.get(sequencePosition - 1);
                previousActiveButton.getBackground().clearColorFilter();
                activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
            }

        }
    });

    sequencePosition++;
}

虽然我个人考虑使用递归Handler::postDelayed()而不是Timer