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

时间:2016-10-13 18:50:29

标签: java android

我花了一段时间试图找出为什么我的模拟器会一直冻结,结果是因为我引入了一个循环。单击按钮后,输出随机数(LoadG1),然后在4秒后消失。用户然后输入一个数字,如果它等于loadg1,他们得到一个点,程序通过让另一个loadg1可用4秒然后消失等来循环...

我不能拥有循环,除非它显然是它自己的线程。我为它做了一个线程然后尝试了。按下按钮后整个应用程序冻结没有发生,但当loadg1启动4秒时,应用程序关闭。好像第一行循环工作,然后在4秒结束后失败。我收到以下错误:

  

android.view.ViewRootImpl $ CalledFromWrongThreadException:只有   创建视图层次结构的原始线程可以触及其视图。

在查询之后我看到我需要把它放在runOnUiThread中,所以我做了,只是遇到了同样的问题。这是我的代码:

import android.os.CountDownTimer;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Random;


public class game1 extends AppCompatActivity {

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


final Button loseStarter1;

    loseStarter1 = (Button) findViewById(R.id.Starter1);
    loseStarter1.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            loseStarter1.setVisibility(View.GONE);

            final int[] score = {0};
            Random generateG1 = new Random();
            final int loadG1 = generateG1.nextInt(1000000)+10000;
            final TextView number = (TextView) findViewById(R.id.number);
            number.setText(" "+loadG1);

            new CountDownTimer(18000, 1000) {
                @Override
                public void onTick (long millisUntilFinished) {
                }
                public void onFinish() {
                    TextView result = (TextView) findViewById(R.id.outcome);
                    result.setText("Score: "+ score[0]);
                    TextView prompt = (TextView) findViewById(R.id.prompt);
                    prompt.setVisibility(View.GONE);
                }
            }.start();

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Runnable myRunnable = new Runnable() {
                        @Override
                        public void run() {
                            while (true) {
                                SystemClock.sleep(4000);
                                number.setVisibility(View.GONE);
                                final TextView prompt = (TextView) findViewById(R.id.prompt);
                                prompt.setText(" Enter the number");
                                final EditText input = (EditText) findViewById(R.id.enterAnswer);
                                input.setVisibility(View.VISIBLE);
                                input.setOnKeyListener(new View.OnKeyListener() {
                                    @Override
                                    public boolean onKey(View v, int keyCode, KeyEvent event) {
                                        if (event.getAction() == KeyEvent.ACTION_DOWN) {
                                            switch (keyCode) {
                                                case KeyEvent.KEYCODE_ENTER:
                                                    Editable answer = input.getText();
                                                    int finalAnswer = Integer.parseInt(String.valueOf(answer));
                                                    int finalLoadG1 = Integer.parseInt(String.valueOf(loadG1));
                                                    input.setVisibility(View.GONE);
                                                    prompt.setVisibility(View.GONE);
                                                    if (finalAnswer == finalLoadG1) {
                                                        score[0]++;
                                                    }

                                                    return true;
                                                default:
                                            }
                                        }
                                        return false;
                                    }
                                });
                            }
                        }
                    };

                    Thread myThread = new Thread(myRunnable);
                    myThread.start();

                }
            });

            }
        });
    }

}

我觉得我已经如此接近解决这个问题,非常感谢所有帮助。

编辑:我尝试了一种没有循环的替代解决方案,代码在这里:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Timer timer = new Timer();
                    timer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    SystemClock.sleep(4000);
                                    number.setVisibility(View.GONE);
                                    final TextView prompt = (TextView) findViewById(R.id.prompt);
                                    prompt.setText(" Enter the number");
                                    final EditText input = (EditText) findViewById(R.id.enterAnswer);
                                    input.setVisibility(View.VISIBLE);
                                    input.setOnKeyListener(new View.OnKeyListener() {
                                        @Override
                                        public boolean onKey(View v, int keyCode, KeyEvent event) {
                                            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                                                switch (keyCode) {
                                                    case KeyEvent.KEYCODE_ENTER:
                                                        Editable answer = input.getText();
                                                        int finalAnswer = Integer.parseInt(String.valueOf(answer));
                                                        int finalLoadG1 = Integer.parseInt(String.valueOf(loadG1));
                                                        input.setVisibility(View.GONE);
                                                        prompt.setVisibility(View.GONE);
                                                        if (finalAnswer == finalLoadG1) {
                                                            score[0]++;
                                                        }

                                                        return true;
                                                    default:
                                                }
                                            }
                                            return false;
                                        }
                                    });

                                }
                            });

                        }
                    }, 0, 4000);
                }
            });

现在生成随机数并显示4秒钟,但是当用户输入可用时,应用程序会冻结。显然跳过了238帧(我相信这会显示每四秒钟),并且“主线程”正在做太多工作。注意:即使没有RUNONUITHREAD封装我的代码,也会发生同样的结果。我没有找到任何不同的方法。

老老实实地感谢能够帮助我解决这个问题的人。

1 个答案:

答案 0 :(得分:1)

不要使用UI操作进行while(true),这就是为什么你会卡住,甚至更多的时候不使用(真的) - 这是不好的做法。

如果你需要运行一些重复操作,可以使用计时器,你可以启动/停止计时器,你可以启动另一个计时器,甚至几次一次)

不要在线程中进行UI操作,只能在UI线程上进行。

用于硬操作使用异步任务 - 当完成所有硬操作后,再更新UI。 (在异步任务中,您可以通过发布进度更新UI),在stackoverflow吨样本上执行异步任务。

OnKeyListener - 在非UI线程上运行,但请在上面阅读并避免使用while(true)

更新回答:

好的,如果你想这样跑并且不想有滞后,那么请继续:

创建计时器 - 无需在runOnUIThread中启动它。所有带计算的代码移动ti异步任务,你将在计时器中启动,在异步任务中运行 - 停止计时器以避免再次运行,在异步任务中进行所有计算,结束异步任务 - 更新ui和(或者你可以在发布时更新)进度)并在async任务结束时,您可以再次启动计时器。如果您在下次运行之前需要延迟,请使用HandlerpostDelayed