【问题标题】:CountdownTimer crashing app. Trying to update UI thread on tickCountdownTimer 崩溃的应用程序。尝试在滴答声上更新 UI 线程
【发布时间】:2017-06-25 16:49:57
【问题描述】:

我希望倒数计时器位于单独的线程上,并让它在每次滴答时更新 UI。每次单击启动时,应用程序都会关闭,并收到“应用程序已停止”错误消息。

public class Activity_MultiplayerGame extends AppCompatActivity {

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

public void btnStart_onClick(View view){
    CountDownTimer timer = new CountDownTimer(15000, 1000){

        @Override
        public void onTick(final long millisUntilFinished) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    TextView countdownText = (TextView) findViewById(R.id.countdown);
                    Integer timeUntilFinished = (int) millisUntilFinished/1000;
                    countdownText.setText(timeUntilFinished);
                }
            });
        }

        @Override
        public void onFinish() {
            TextView countdownText = (TextView) findViewById(R.id.countdown);
            countdownText.setText("Finished");
        }
    };

    timer.start();
}
}

我假设创建一个 CountDownTimer 会给它自己的线程?

【问题讨论】:

  • 提供您的堆栈跟踪。
  • 错误是什么?

标签: java android multithreading countdown


【解决方案1】:

您可以使用 Handler。我为你写了一个示例

这段代码每秒增加一个计数器显示和更新textView上的计数器值。

public class MainActivity extends Activity {


    private Handler handler = new Handler();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        startTimer();
    }


    int i = 0;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            i++;
            textView.setText("counter:" + i);
            startTimer();
        }
    };

    public void startTimer() {
        handler.postDelayed(runnable, 1000);
    }

    public void cancelTimer() {
        handler.removeCallbacks(runnable);
    }

    @Override
    protected void onStop() {
        super.onStop();
        cancelTimer();
    }
}

【讨论】:

  • 但是处理程序不是绑定到 UI 线程的吗?所以当它调用 setText 时,这不会导致 UI 的滞后或冻结吗?我希望用户能够在倒计时生效时输入文本框并与按钮进行交互。
  • 这是工作!我在手机上测试过。而且我之前看到 ExoPlayer 使用此代码更新视频播放器中的进度条
  • @Jamanfi 绑定到 UI 线程,没关系。您不能从 Android 上的主/ui 线程以外的其他 UI 操作。您不必担心冻结 UI,因为它阻塞线程的唯一时间是在 Runnable 内的代码执行期间,它不是长时间运行的代码。当处理程序为另一个倒计时步骤等待 1000 毫秒时,没有任何内容被阻塞或冻结。
  • 如果对你有帮助,请标记为正确答案并投票
【解决方案2】:

您不能从 android 上的主 (ui) 线程以外的其他线程更新 UI。有很多方法可以解决这个问题,但如果你真的需要另一个线程,我想你应该从AsyncTask 开始。检查 doInBackround 和 onProgressUpdate 方法。

如果您不需要其他线程(如果这只是关于您不需要的计数器),您可以检查 Handler 类和 postAtTime(Runnable, long), postDelayed(Runnable, long) 方法。或者您可以创建 Handler 类的子类,并使用 sendMessageDelayed(android.os.Message, long) 和覆盖的 handleMessage() 的组合。

【讨论】:

    【解决方案3】:

    这是你的问题:

    countdownText.setText(timeUntilFinished);
    

    您已将整数传递给setText(int) 方法,该方法实际上采用字符串资源ID 而不是要显示的整数。你应该使用setText(String)的方法。

    试试这个:

    countdownText.setText(String.valueOf(timeUntilFinished));
    

    【讨论】:

      【解决方案4】:

      此代码还可以减少计数器(或增加您想要的任何值)并在文本视图中显示您也可以通过单击相同的按钮来停止或重置计数器。

      public class MainActivity extends AppCompatActivity {
      
      
      TextView textView;
      Button count;
      
      boolean timelapseRunning = true;
      int NUM_OF_COUNT=15;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          textView=(TextView)findViewById(R.id.textview);
          count=(Button)findViewById(R.id.count);
          runTimer();
      
      }
      public void onclick(View view){
          if (!timelapseRunning) {
             timelapseRunning=true;
          } else {
              timelapseRunning = false;
      
          }
          if(NUM_OF_COUNT==0){
              NUM_OF_COUNT=15;
          }
      }
      private void runTimer() {
                 final Handler handler = new Handler();
          handler.post(new Runnable() {
              @Override
              public void run() {
                  textView.setText(""+NUM_OF_COUNT);
                  if (!timelapseRunning && NUM_OF_COUNT>0) {
                      NUM_OF_COUNT--;
                  }
                  handler.postDelayed(this, 1000);
              }
          });
      }
      }
      

      【讨论】:

        【解决方案5】:

        CountDownTimer 在创建它的同一线程上发出回调。因此,如果在 UI 线程上调用了 CountDownTimer 构造函数,则绝对不需要 onTick 中的 runOnUiThread

        还有,

        TextView countdownText = (TextView) findViewById(R.id.countdown);
        

        在 onTick 方法中调用是糟糕的设计,因为findViewById 方法会消耗大量的处理能力。

        最后,正如 Nabin Bhandari 所指出的,实际问题是传递给 setText 方法的整数值。您需要将其包装成字符串。 这段代码就够了

        final TextView countdownText = (TextView) findViewById(R.id.countdown);
            CountDownTimer timer = new CountDownTimer(15000, 1000) {
                @Override
                public void onTick(long millisUntilFinished) {
                    countdownText.setText(String.valueOf(millisUntilFinished/1000));
                }
        
                @Override
                public void onFinish() {
                    countdownText.setText("Finished");
                }
            };
            timer.start();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多