Làm cách nào để tạm dừng / ngủ chuỗi hoặc xử lý trong Android?


294

Tôi muốn tạm dừng giữa hai dòng mã, Hãy để tôi giải thích một chút:

-> người dùng nhấp vào một nút (trên thực tế là một thẻ) và tôi hiển thị nó bằng cách thay đổi nền của nút này:

thisbutton.setBackgroundResource(R.drawable.icon);

-> sau khi nói 1 giây, tôi cần quay lại trạng thái trước đó của nút bằng cách thay đổi lại nền của nó:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Tôi đã cố tạm dừng chuỗi giữa hai dòng mã này bằng:

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Tuy nhiên, điều này không hoạt động. Có lẽ đó là quá trình và không phải là Chủ đề mà tôi cần tạm dừng?

Tôi cũng đã thử (nhưng nó không hoạt động):

new Reminder(5);

Với cái này:

public class Reminder {

Timer timer;

        public Reminder(int seconds) {
            timer = new Timer();
            timer.schedule(new RemindTask(), seconds*1000);
        }

        class RemindTask extends TimerTask {
            public void run() {
                System.out.format("Time's up!%n");
                timer.cancel(); //Terminate the timer thread
            }
        }  
    }

Làm thế nào tôi có thể tạm dừng / ngủ các chủ đề hoặc quá trình?


4
Ồ, chỉ cần sử dụng khối tạm dừng luồng cổ điển: while (true) {}
KristoferA

6
@ KristoferA-Huagati.com Tôi không chắc liệu bạn có đang mỉa mai hay thực sự có một số phép thuật Dalvik / Android để điều này được chấp nhận trên Android. Bạn có thể vui lòng làm rõ? Xin lỗi vì đã nghi ngờ nhưng tôi hỏi vì trong khi (!conditionCheck()) {}thường nản lòng.
Biến khổ sở

"Tuy nhiên, điều này không hoạt động." "Tôi cũng đã thử (nhưng nó không hoạt động)" Đây là một ví dụ kinh điển cho biết có vấn đề mà không đưa ra các triệu chứng. Bằng cách nào những nỗ lực này không đáp ứng yêu cầu của bạn? Có phải chủ đề không tạm dừng? Bạn đã nhận được một thông báo lỗi?
LarsH

Câu trả lời:


454

Một giải pháp cho vấn đề này là sử dụng phương thức Handler.postDelayed () . Một số tài liệu đào tạo của Google đề xuất giải pháp tương tự.

@Override
public void onClick(View v) {
    my_button.setBackgroundResource(R.drawable.icon);

    Handler handler = new Handler(); 
    handler.postDelayed(new Runnable() {
         @Override 
         public void run() { 
              my_button.setBackgroundResource(R.drawable.defaultcard); 
         } 
    }, 2000); 
}

Tuy nhiên, một số người đã chỉ ra rằng giải pháp trên gây ra rò rỉ bộ nhớ vì nó sử dụng một lớp ẩn danh bên trong và ẩn danh, nó ngầm giữ một tham chiếu đến lớp bên ngoài của nó, hoạt động. Đây là một vấn đề khi bối cảnh hoạt động là rác được thu thập.

Một giải pháp phức tạp hơn để tránh rò rỉ bộ nhớ các lớp con HandlerRunnablevới các lớp bên trong tĩnh bên trong hoạt động do các lớp bên trong tĩnh không giữ một tham chiếu ngầm đến lớp bên ngoài của chúng:

private static class MyHandler extends Handler {}
private final MyHandler mHandler = new MyHandler();

public static class MyRunnable implements Runnable {
    private final WeakReference<Activity> mActivity;

    public MyRunnable(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity activity = mActivity.get();
        if (activity != null) {
            Button btn = (Button) activity.findViewById(R.id.button);
            btn.setBackgroundResource(R.drawable.defaultcard);
        }
    }
}

private MyRunnable mRunnable = new MyRunnable(this);

public void onClick(View view) {
    my_button.setBackgroundResource(R.drawable.icon);

    // Execute the Runnable in 2 seconds
    mHandler.postDelayed(mRunnable, 2000);
}

Lưu ý rằng việc Runnablesử dụng WeakReference cho Activity, cần thiết trong một lớp tĩnh cần truy cập vào UI.


3
Nó hoạt động, nhưng đó là một cách khá bất tiện để giới thiệu sự chậm trễ trong suốt mã, phải không?
Ehtesh Choudhury

4
Tôi không chắc ý của bạn là "bất tiện". Phương thức postDelayed của Handler được thiết kế để thông báo cho Android rằng bạn muốn một chút mã được thực thi sau một khoảng thời gian nhất định đã qua.
tronman

27
Sau 2 năm và mã này chỉ giúp tôi! cảm ơn @tronman !! :)
Melvin Lai

2
Bạn có thể chỉ cần sao chép nó sang một biến (cuối cùng) khác như vậy final Button mynewbutton = mybutton;và sử dụng mynewbuttontrong Handler và Runnable từ đó trở đi.
Dzhuneyt

2
@MelvinLai sau 5 năm và mã này chỉ giúp tôi! :)
gabrieloliveira

191

Bạn có thể thử cái này nó ngắn

SystemClock.sleep(7000);

CẢNH BÁO : Không bao giờ, bao giờ, làm điều này trên một giao diện người dùng.

Sử dụng điều này để ngủ ví dụ. chủ đề nền.


Giải pháp đầy đủ cho vấn đề của bạn sẽ là: Đây là API 1 có sẵn

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View button) {
                button.setBackgroundResource(R.drawable.avatar_dead);
                final long changeTime = 1000L;
                button.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        button.setBackgroundResource(R.drawable.avatar_small);
                    }
                }, changeTime);
            }
        });

Không cần tạo tmp Handler. Ngoài ra giải pháp này tốt hơn @tronman vì chúng tôi không duy trì chế độ xem của Handler. Ngoài ra, chúng tôi không có vấn đề với Handler được tạo ra ở luồng xấu;)

Tài liệu

ngủ tĩnh công cộng (dài ms)

Đã thêm trong API cấp 1

Đợi một số mili giây nhất định (của uptimeMillis) trước khi quay lại. Tương tự như giấc ngủ (dài), nhưng không ném Interrupttedception ; các sự kiện ngắt () được hoãn lại cho đến khi hoạt động gián đoạn tiếp theo. Không quay lại cho đến khi ít nhất số mili giây đã chỉ định đã trôi qua.

Thông số

ms để ngủ trước khi trở về, tính bằng mili giây của thời gian hoạt động.

Mã cho postDelayed từ lớp View:

/**
 * <p>Causes the Runnable to be added to the message queue, to be run
 * after the specified amount of time elapses.
 * The runnable will be run on the user interface thread.</p>
 *
 * @param action The Runnable that will be executed.
 * @param delayMillis The delay (in milliseconds) until the Runnable
 *        will be executed.
 *
 * @return true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the Runnable will be processed --
 *         if the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 *
 * @see #post
 * @see #removeCallbacks
 */
public boolean postDelayed(Runnable action, long delayMillis) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.postDelayed(action, delayMillis);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
    return true;
}

22
Và để giao diện người dùng Android đóng băng? Đừng làm điều này.
shkschneider

3
Ctrl + Shift + O (Nhập tự động Eclipse)
Dawid Drozd

13
Gelldur, bạn đang thiếu quan điểm bình luận của @ RichieHH. So với câu trả lời đã được chấp nhận 3 năm trước khi bạn đăng , những gì bạn đề xuất không giúp OP giải quyết vấn đề của anh ấy. Lý do: Mã, cả như được hiển thị bởi OP và như được hiển thị trong câu trả lời được chấp nhận, đang chạy bên trong trình xử lý UI . Nói OMG of course do this in background threadlà không liên quan, trừ khi bạn hiển thị CÁCH để đặt nó trong chủ đề nền. Tại thời điểm đó, bạn sẽ phát hiện ra rằng bạn có một câu trả lời phức tạp hơn câu trả lời đã được chấp nhận. Tôi đã đề cập rằng một giải pháp tốt hơn đã được chấp nhận ba năm trước? : P
ToolmakerSteve

2
... đã quên đề cập rằng điều khiến cho gợi ý này đặc biệt ngoài mục đích của câu hỏi, đó là OP rõ ràng muốn thực hiện hành động UI sau khi trì hoãn. Vì vậy, bạn sẽ có một giải pháp trong đó bạn đã tạo một luồng nền (đã nặng hơn mức cần thiết để giải quyết vấn đề), ngủ, và sau đó bạn phải (bằng cách nào đó) quay lại luồng UI. Handler + postRunnablehoàn thành tất cả điều này, trong một bước duy nhất. Không có chi phí hệ thống để tạo ra một luồng thứ hai.
ToolmakerSteve

2
@ToolmakerSteve hạnh phúc ngay bây giờ: D? Câu hỏi chính là: "Làm thế nào để tạm dừng / ngủ chuỗi hoặc xử lý trong Android?" vì vậy câu trả lời của tôi rất đơn giản :). Nếu ai đó google cho "làm thế nào để ngủ chủ đề" câu hỏi này sẽ hiển thị. Anh ấy / Cô ấy có lẽ đang tìm câu trả lời của tôi: P. Nhưng ok tôi đã thêm phản hồi đầy đủ;)
Dawid Drozd

28

Tôi sử dụng cái này:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});

4
Phải e.getLocalizedMessage()làm gì?
Philipp Reichart

tôi sử dụng e.getLocalizedMessage () khi tôi cần một tin nhắn ngoại lệ đơn giản, chung chung và nhanh chóng
Byt3

1
Nhưng tôi cá là bạn không làm điều này trong tình huống OP yêu cầu, bên trong phương thức nhấp vào giao diện người dùng, sẽ thực hiện cập nhật thêm cho giao diện người dùng. Handler/postDelayedGiải pháp được chấp nhận có hai ưu điểm: (1) tránh chi phí hệ thống của luồng thứ 2, (1) chạy trên luồng UI, do đó có thể thực hiện thay đổi UI mà không gây ra ngoại lệ.
ToolmakerSteve

1
Không liên quan trực tiếp đến mục tiêu chính của câu hỏi nhưng bạn không nên bắt Ngoại lệ. Thay vì bắt Interrupt. Bằng cách bắt Exception, bạn sẽ nắm bắt mọi thứ có thể sai, do đó che giấu các vấn đề mà bạn sẽ bắt gặp.
Herve Thu

16

Bạn có thể không muốn làm theo cách đó. Bằng cách đặt một phần rõ ràng sleep()trong trình xử lý sự kiện nhấp vào nút của bạn, bạn thực sự sẽ khóa toàn bộ UI trong một giây. Một cách khác là sử dụng một số loại Timer chụp một lần . Tạo TimerTask để thay đổi màu nền trở lại màu mặc định và lên lịch cho Timer.

Một khả năng khác là sử dụng Handler . Có một hướng dẫn về ai đó đã chuyển từ sử dụng Timer sang sử dụng Handler.

Ngẫu nhiên, bạn không thể tạm dừng một quá trình. Một quy trình Java (hoặc Android) có ít nhất 1 luồng và bạn chỉ có thể ngủ các luồng.


14

Tôi sử dụng CountDownTime

new CountDownTimer(5000, 1000) {

    @Override
    public void onTick(long millisUntilFinished) {
        // do something after 1s
    }

    @Override
    public void onFinish() {
        // do something end times 5s
    }

}.start(); 

Điều này rất hữu ích.
UzaySan

9

Đây là những gì tôi đã làm vào cuối ngày - hiện đang hoạt động tốt:

@Override
    public void onClick(View v) {
        my_button.setBackgroundResource(R.drawable.icon);
        // SLEEP 2 SECONDS HERE ...
        final Handler handler = new Handler(); 
        Timer t = new Timer(); 
        t.schedule(new TimerTask() { 
                public void run() { 
                        handler.post(new Runnable() { 
                                public void run() { 
                                 my_button.setBackgroundResource(R.drawable.defaultcard); 
                                } 
                        }); 
                } 
        }, 2000); 
    }

2
Tại sao không đăngDelayed? Không cần hẹn giờ.
RichieHH

8

Ngoài câu trả lời của ông Yankowsky, bạn cũng có thể sử dụng postDelayed(). Điều này có sẵn trên bất kỳ View(ví dụ, thẻ của bạn) và mất một Runnablekhoảng thời gian trì hoãn. Nó thực hiện Runnablesau sự chậm trễ đó.


5

Đây là ví dụ của tôi

Tạo một tiện ích Java

    import android.app.ProgressDialog;
    import android.content.Context;
    import android.content.Intent;

    public class Utils {

        public static void showDummyWaitingDialog(final Context context, final Intent startingIntent) {
            // ...
            final ProgressDialog progressDialog = ProgressDialog.show(context, "Please wait...", "Loading data ...", true);

            new Thread() {
                public void run() {
                    try{
                        // Do some work here
                        sleep(5000);
                    } catch (Exception e) {
                    }
                    // start next intent
                    new Thread() {
                        public void run() {
                        // Dismiss the Dialog 
                        progressDialog.dismiss();
                        // start selected activity
                        if ( startingIntent != null) context.startActivity(startingIntent);
                        }
                    }.start();
                }
            }.start();  

        }

    }    

3
Tuy nhiên, một câu trả lời khác được thêm vào nhiều năm sau khi câu hỏi đã được giải quyết tốt, điều đó không liên quan đến câu hỏi, bởi vì nó không giải quyết được mong muốn đã nêu để có thể thực hiện công việc UI. Bạn không thể làm UI ở đây, vì bạn đang chạy trên một luồng mới. Không phải trên luồng UI.
ToolmakerSteve

1
Về phía UX, hiển thị hộp thoại chặn cho người dùng tải dữ liệu là ... không tốt.
2Dee

4

Hoặc bạn có thể sử dụng:

android.os.SystemClock.sleep(checkEvery)

trong đó có lợi thế của không yêu cầu một gói try ... catch.


0

Nếu bạn sử dụng Kotlin và coroutines , bạn chỉ cần làm

GlobalScope.launch {
   delay(3000) // In ms
   //Code after sleep
}

Và nếu bạn cần cập nhật UI

GlobalScope.launch {
  delay(3000)
  GlobalScope.launch(Dispatchers.Main) {
    //Action on UI thread
  }
}

0

Tôi biết đây là một chủ đề cũ, nhưng trong tài liệu Android tôi đã tìm thấy một giải pháp hoạt động rất tốt cho tôi ...

new CountDownTimer(30000, 1000) {

    public void onTick(long millisUntilFinished) {
        mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
    }

    public void onFinish() {
        mTextField.setText("done!");
    }
}.start();

https://developer.android.com/reference/android/os/CountDownTimer.html

Hy vọng điều này sẽ giúp ai đó ...


0
  class MyActivity{
    private final Handler handler = new Handler();
    private Runnable yourRunnable;
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       // ....
       this.yourRunnable = new Runnable() {
               @Override
               public void run() {
                   //code
               }
            };

        this.handler.postDelayed(this.yourRunnable, 2000);
       }


     @Override
  protected void onDestroy() {
      // to avoid memory leaks
      this.handler.removeCallbacks(this.yourRunnable);
      }
    }

Và để chắc chắn, bạn có thể kết hợp nó với phương thức "lớp tĩnh" như được mô tả trong câu trả lời của tronman

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.