Як призупинити / спати нитку або обробити в Android?


294

Я хочу зробити паузу між двома рядками коду. Дозвольте мені трохи пояснити:

-> користувач натискає кнопку (фактично карта), і я показую її, змінюючи фон цієї кнопки:

thisbutton.setBackgroundResource(R.drawable.icon);

-> після скажімо 1 секунду, мені потрібно повернутися до попереднього стану кнопки, змінивши її фон:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Я намагався призупинити нитку між цими двома рядками коду за допомогою:

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

Однак це не працює. Може бути, це процес, а не Нитка, яку мені потрібно зробити паузу?

Я також спробував (але це не працює):

new Reminder(5);

З цим:

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
            }
        }  
    }

Як я можу призупинити / заснути нитку чи обробити її?


4
О, просто використовуйте класичний блок паузи для потоку: while (true) {}
KristoferA

6
@ KristoferA-Huagati.com Я не впевнений, чи є ти саркастичним чи дійсно є якась магія Dalvik / Android, щоб це було прийнятно на Android. Ви можете, будь ласка, уточнити? Вибачте за сумніви, але я прошу, тому що, як (!conditionCheck()) {}правило, не рекомендується.
Жалюгідна змінна

"Однак це не працює". "Я також спробував (але це не працює)" Це класичний приклад сказати, що існує проблема без надання симптомів. Яким чином ці спроби не відповідали вашим вимогам? Хіба нитка не призупинилася? Ви отримали повідомлення про помилку?
LarsH

Відповіді:


454

Одним із варіантів вирішення цієї проблеми є використання методу Handler.postDelayed () . Деякі навчальні матеріали Google пропонують таке саме рішення.

@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); 
}

Однак деякі вказували на те, що рішення вище викликає витік пам'яті, оскільки він використовує нестатичний внутрішній та анонімний клас, який неявно посилається на його зовнішній клас, на активність. Це проблема, коли контекст діяльності збирається сміттям.

Більш складне рішення, яке дозволяє уникнути витоку пам'яті підкласів Handlerта Runnableз статичними внутрішніми класами всередині діяльності, оскільки статичні внутрішні класи не містять неявного посилання на їх зовнішній клас:

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);
}

Зауважте, що Runnableвикористовується слабке посилання на активність, необхідне в статичному класі, якому потрібен доступ до інтерфейсу користувача.


3
Це працює, але це досить незручний спосіб ввести затримки в коді, правда?
Ехтеш Чудхурі

4
Я не впевнений, що ви маєте на увазі під «незручним». Метод Handler's PostDelayed призначений для того, щоб повідомити Android про те, що потрібно виконати трохи коду після того, як пройде певна кількість часу.
тронман

27
через 2 роки, і цей код мені просто допоміг! дякую @tronman !! :)
Мелвін Лай

2
Ви можете просто скопіювати його в іншу (остаточну) змінну на зразок цього final Button mynewbutton = mybutton;і використовувати mynewbuttonзвідти в Handler та Runnable.
Джунейт

2
@MelvinLai через 5 років, і цей код мені просто допоміг! :)
gabrieloliveira

191

Ви можете спробувати цей короткий

SystemClock.sleep(7000);

ПОПЕРЕДЖЕННЯ . Ніколи і ніколи не робіть цього на потоці інтерфейсу користувача.

Використовуйте це для сну, наприклад. фонова нитка.


Повне рішення вашої проблеми буде: Це доступний API 1

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);
            }
        });

Без створення tmp Handler. Крім того, це рішення краще, ніж @tronman, оскільки ми не зберігаємо перегляд від Handler. Крім того, у нас немає проблем з Handler, створеним в поганій темі;)

Документація

публічний статичний недійсний сон (тривалий мс)

Додано в рівень 1 API

Зачекає задану кількість мілісекунд (часу без пробілу) перед поверненням. Схожий на сон (тривалий), але не кидає InterruptedException ; події interrupt () відкладаються до наступної операції переривання. Хіба не повернеться до тих пір , по крайней мере, задану кількість мілісекунд , поки не закінчиться.

Параметри

мс для сну перед поверненням, в мілісекундах часу безперервного часу.

Код для публікаціїЗахищено від класу 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
І нехай Android інтерфейс застигає? Не робіть цього.
shkschneider

3
Ctrl + Shift + O (автоматичний імпорт Eclipse)
Давід Дрозд

13
Гелдур, ти не вистачаєш точки коментаря @ RichieHH. Порівняно з відповіддю, яке було прийнято за 3 роки до публікації , те, що ви пропонуєте, не допомагає ОП вирішити його проблему. Причина: Код, як показано в ОП, так і як показано у прийнятій відповіді, працює всередині обробника інтерфейсу користувача . Казання OMG of course do this in background threadне має значення, якщо ви не покажете, ЯК розмістити його у фоновому потоці. У цей час ви виявите, що у вас є відповідь складніша, ніж вже прийнята відповідь. Чи згадував я, що краще рішення було прийнято три роки тому? : P
ToolmakerSteve

2
... забув згадати, що те, що робить цю пропозицію особливо нецільовою для запитання, полягає в тому, що ОП явно хоче зробити дії інтерфейсу після затримки. Отже, у вас було б рішення, в якому ви створили фонову нитку (вже більш важку вагу, ніж потрібно для вирішення проблеми), спали, і тоді вам доведеться (якось) повернутися до потоку інтерфейсу користувача. Handler + postRunnableздійснює все це за один крок. Без накладних витрат на створення другої нитки.
ToolmakerSteve

2
@ToolmakerSteve щасливий зараз: D? Головне питання: "Як призупинити / вимкнути нитку або обробити в Android?" тому моя відповідь була простою :). Якщо хтось google за "як спати нитку", це питання покаже. Він, мабуть, шукає моєї відповіді: P. Але добре, я додав повну відповідь;)
Давід Дрозд

28

Я використовую це:

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

4
Що e.getLocalizedMessage()слід робити?
Філіп Рейхарт

я використовую e.getLocalizedMessage (), коли мені потрібно просте, загальне та швидке повідомлення про виключення
Byt3

1
Але я маю надію, що ви цього не робите в ситуації, про яку задається ОП, всередині методу клацання інтерфейсу, який збирається зробити нові оновлення інтерфейсу користувача. Прийняте Handler/postDelayedрішення має дві переваги: ​​(1) уникає накладних витрат 2-го потоку системи, (1) працює на потоці інтерфейсу користувача, тому може вносити зміни в інтерфейс користувача, не викликаючи винятку.
ToolmakerSteve

1
Не має прямого відношення до головної мети питання, але ви не повинні сприймати Виняток. Швидше зловити InterruptedException. Зловивши «Виняток», ви схопите все, що могло піти не так, приховуючи таким чином проблеми, які ви інакше спіймали б.
Герве Чт

16

Напевно, ви не хочете робити це так. Помістивши явне sleep()в обробник подій, який натиснув кнопку, ви фактично заблокували весь інтерфейс на секунду. Однією з альтернатив є використання якогось таймера для одиночного зйомки . Створіть задачу TimerTask, щоб змінити колір тла до кольору за замовчуванням, і заплануйте його на Таймері.

Інша можливість - використовувати обробник . Там є підручник про того, хто перейшов з таймера на використання обробника.

До речі, ви не можете призупинити процес. Процес Java (або Android) має щонайменше 1 потік, і ви можете спати лише теми.


14

Я використовую 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(); 

це корисно.
UzaySan

9

Це те, що я зробив наприкінці дня - зараз добре працює:

@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
Чому б не PostDelayed? Не потрібен таймер.
RichieHH

8

Окрім відповідей пана Янковського, ви можете також скористатися postDelayed(). Це доступно на будь-якій View(наприклад, на вашій картці) і потребує Runnableперіоду та затримки. Він виконує Runnableпісля цього затримку.


5

Це мій приклад

Створіть утиліти 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
Ще одна відповідь була додана через роки після того, як питання було вже добре вирішене, що не стосується питання, оскільки воно не стосується заявленого бажання мати можливість виконувати роботу з інтерфейсом користувача. Тут ви не можете виконувати роботу інтерфейсу, оскільки ви працюєте на новій темі. Не на потоці інтерфейсу користувача.
ToolmakerSteve

1
Що стосується речей UX, показ діалогу блокування користувача для завантаження даних - це не добре.
2Dee

4

Або ви можете використовувати:

android.os.SystemClock.sleep(checkEvery)

що має перевагу в тому, що не вимагає обгортання try ... catch.


0

Якщо ви використовуєте Котлін та супроти , ви можете просто зробити

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

І якщо вам потрібно оновити інтерфейс користувача

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

0

Я знаю, що це стара тема, але в документації на Android я знайшов рішення, яке дуже добре працювало для мене ...

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

Сподіваюся, це допоможе комусь ...


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);
      }
    }

І щоб бути подвійним впевненим, ви можете поєднати його з методом "статичного класу", як описано у відповіді tronman

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.