如何在 android studio 中正确使用 postDelayed()?

Posted

技术标签:

【中文标题】如何在 android studio 中正确使用 postDelayed()?【英文标题】:how to use postDelayed() correctly in android studio? 【发布时间】:2017-07-11 18:47:23 【问题描述】:

我有一个 countDownTimer,如果用户在 12 秒内没有点击 gameButton,我希望调用 gameOver 方法。问题我要么在 countDownTimer 为 12 时立即调用游戏功能,要么计时器一直在倒计时。所以我尝试使用 postDelayed() 方法给用户一整秒的时间来点击按钮并让 countDownTimer 继续,但由于我的代码现在游戏在 12 点停止。

import android.app.Activity;
import android.os.CountDownTimer;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;



public class GameScreen extends Activity 

    private TextView time;
    private Button start;
    private Button cancel;
    private Button gameButton;
    private CountDownTimer countDownTimer;
    public static int count = 0;
    public static int countFail = 0;

    final Handler handler = new Handler();
    final Runnable r = new Runnable() 
        public void run() 
            handler.postDelayed(this, 1000);
            gameOver();
        
    ;


    private View.OnClickListener btnClickListener = new View.OnClickListener()

        @Override
        public void onClick(View v) 

            switch(v.getId())
                case R.id.start_ID :
                    start();
                    break;
                case R.id.cancel :
                    cancel();
                    break;
                case R.id.gameButton_ID :
                    gameButton();
                    break;
            

        


    ;


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


        start = (Button) findViewById(R.id.start_ID);
        start.setOnClickListener(btnClickListener);
        cancel = (Button) findViewById(R.id.cancel);
        cancel.setOnClickListener(btnClickListener);
        time = (TextView) findViewById(R.id.time);
        gameButton = (Button) findViewById(R.id.gameButton_ID);
        gameButton.setOnClickListener(btnClickListener);


    

    public void start()

        time.setText("16");
        //This doesn't work and makes app crash when you hit start button

        countDownTimer = new CountDownTimer(16 * 1000, 1000) 
            @Override
            public void onTick(long millsUntilFinished)
                time.setText("" + millsUntilFinished / 1000);

                //turns textview string to int
                int foo = Integer.parseInt(time.getText().toString());

                if(time.getText().equals("12"))

                    r.run();

                

            

            public void onFinish() 
                time.setText("Done !");
            
        ;
        countDownTimer.start();
    

    private void cancel()
        if(countDownTimer != null)
            countDownTimer.cancel();
            countDownTimer = null;
        
    

    private void gameOver()
        Toast.makeText(getApplicationContext(), "You scored " + count, Toast.LENGTH_SHORT).show();
        count = 0;
        countFail = 0;
        cancel();
    

    private void gameButton()

        int foo = Integer.parseInt(time.getText().toString());

        if(foo  % 2 == 0 ) 
            Toast.makeText(getApplicationContext(), "PASS", Toast.LENGTH_SHORT).show();
            handler.removeCallbacks(r);
            ++count;
        

        else
            gameOver();
        
    


【问题讨论】:

【参考方案1】:

您几乎正确地使用了postDelayed(Runnable, long),但并不完全正确。让我们看看你的 Runnable。

final Runnable r = new Runnable() 
    public void run() 
        handler.postDelayed(this, 1000);
        gameOver();
    
;

当我们调用r.run(); 时,它要做的第一件事就是告诉您的handler 在1000 毫秒后运行相同的Runnable,然后调用gameOver()。这实际上会导致您的 gameOver() 方法被调用两次:一次是立即调用,第二次是在 Handler 完成后等待 1000 毫秒。

相反,您应该将 Runnable 更改为:

final Runnable r = new Runnable() 
    public void run() 
        gameOver();
    
;

然后这样称呼它:

handler.postDelayed(r, 1000);

希望这会有所帮助。

【讨论】:

gameOver()不是每秒无限调用,而不是被调用两次?它看起来像无限递归。【参考方案2】:

以下是我使用的代码,它与公认的答案相同,但编写和理解起来非常简单。

final Handler handler = new Handler();
            handler.postDelayed(new Runnable() 
                @Override
                public void run() 
                    //Write whatever to want to do after delay specified (1 sec)
                    Log.d("Handler", "Running Handler");
                
            , 1000);

【讨论】:

【参考方案3】:
    Thread(Runnable 
        // background work here ... 
        Handler(Looper.getMainLooper()).postDelayed(Runnable 
            // Update UI here
        , 10000) //it will wait 10 sec before upate ui
    ).start()

【讨论】:

【参考方案4】:

不推荐使用无参数的 Handler 构造函数,并且不使用 lambdas 也会使代码看起来很笨拙,所以话虽如此,下面是它看起来更现代的用法:

final Runnable _r_ = new Runnable()...;

Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() -> _r_.run(), 666);

您也可以更新它以使用方法参考

handler.postDelayed(_r_::run(), 666);

或者更简单,只需要一个限定符

handler.postDelayed(_r_, 666);

【讨论】:

以上是关于如何在 android studio 中正确使用 postDelayed()?的主要内容,如果未能解决你的问题,请参考以下文章

Android studio中正确引入so文件的方法

如何强制 Android Studio 生成具有开发人员正确名称的文档

Android studio中正确引入so文件的方法

Android studio中正确引入so文件的方法

如何在 Android Studio 中正确更改我现有的项目名称?

如何使用 Android Studio 在模块中添加风味?