Android'de iş parçacığı duraklatma / uyku veya işlem nasıl yapılır?


294

İki satır kod arasında bir duraklama yapmak istiyorum, biraz açıklayayım:

-> kullanıcı bir düğmeyi tıklar (aslında bir kart) ve bu düğmenin arka planını değiştirerek gösteririm:

thisbutton.setBackgroundResource(R.drawable.icon);

-> diyelim 1 saniye sonra, arka planını değiştirerek düğmenin önceki durumuna geri dönmem gerekiyor:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Bu iki kod satırı arasındaki konuyu duraklatmaya çalıştım:

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

Ancak, bu işe yaramaz. Belki duraklatmam gereken İş Parçası değil, süreç budur?

Ben de denedim (ama çalışmıyor):

new Reminder(5);

Bununla:

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

İpliği veya işlemi nasıl duraklatabilir / uyuyabilirim?


4
Oh, sadece klasik iş parçacığı duraklatma bloğunu kullanın: while (true) {}
KristoferA

6
@ KristoferA-Huagati.com Alaycı olup olmadığınızdan emin değil misiniz veya gerçekten Android'de kabul edilebilir olması için bazı Dalvik / Android büyüsü var. Lütfen açıklar mısın? Şüphe için özür dilerim ama soruyorum çünkü süre (!conditionCheck()) {}genellikle cesaretini kırıyor.
Sefil Değişken

"Ancak, bu işe yaramıyor." "Ben de denedim (ama işe yaramaz)" Bu, belirtileri vermeden bir sorun olduğunu söylemenin klasik bir örneğidir. Bu girişimler gereksinimlerinizi ne şekilde karşılayamadı? Konu durmadı mı? Bir hata mesajı aldınız mı?
LarsH

Yanıtlar:


454

Bu sorunun bir çözümü Handler.postDelayed () yöntemini kullanmaktır. Bazı Google eğitim materyalleri aynı çözümü önerir.

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

Bununla birlikte, bazıları, yukarıdaki çözümün , dış sınıfına, etkinliğine dolaylı olarak referansta bulunan statik olmayan bir iç ve anonim sınıf kullandığından bir bellek sızıntısına neden olduğuna dikkat çekmiştir. Etkinlik bağlamı çöp toplandığında bu bir sorundur.

Bellek kaçağını önlemeye daha karmaşık bir çözelti alt sınıfları Handlerve Runnablebunların dış sınıf örtülü bir referans sahip olmayan statik iç sınıflar yana aktivitesi içindeki statik iç sınıfları ile:

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

Kullanıcı arabirimine erişmesi gereken statik bir sınıfta gerekli olan Etkinliğe WeakReferenceRunnable kullandığını unutmayın .


3
İşe yarıyor, ancak bu kod boyunca gecikmeler getirmenin oldukça sakıncalı bir yolu değil mi?
Ehtesh Choudhury

4
"Rahatsız edici" ile ne demek istediğinden emin değilim. İşleyicinin postDelayed yöntemi, Android'e belirli bir süre geçtikten sonra biraz kod yürütülmesini istediğinizi bildirmek için tasarlanmıştır.
tronman

27
2 yıl sonra bu kod bana yardımcı oldu! teşekkürler @tronman !! :)
Melvin Lai

2
Bunu başka bir (son) değişkene kopyalayabilir ve oradan İşleyici ve Runnable'da final Button mynewbutton = mybutton;kullanabilirsiniz mynewbutton.
Dzhuneyt

2
@MelvinLai 5 yıl sonra ve bu kod bana yardımcı oldu! :)
gabrieloliveira

191

Bunu deneyebilirsiniz kısa

SystemClock.sleep(7000);

UYARI : Bunu asla bir UI iş parçacığında yapmayın.

Bunu uyku için kullanın örn. arka plan iş parçacığı.


Sorununuz için tam çözüm olacak: Bu kullanılabilir 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 oluşturmadan. Ayrıca bu çözüm @tronman'dan daha iyidir çünkü Handler'ın görüşünü korumazız. Ayrıca kötü iş parçacığında oluşturulan Handler ile sorunumuz yok;)

belgeleme

genel statik boşluk uyku (uzun ms)

API seviye 1'de eklendi

Dönmeden önce belirli bir milisaniye (uptimeMillis) bekler. Uykuya benzer (uzun), ancak InterruptedException'ı atmaz ; interrupt () olayları bir sonraki kesilebilir işleme kadar ertelenir. Does döndürmez en az milisaniye belirtilen sayıda geçene kadar.

Parametreler

ms dönmeden önce uyku, milisaniye çalışma süresi içinde.

View sınıfından postDelayed kodu :

/**
 * <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 kullanıcı arayüzünün donmasına izin verilsin mi? Bunu yapma.
shkschneider

3
Ctrl + Üst Karakter + O (Tutulma otomatik içe aktarma)
Dawid Drozd

13
Gelldur, @ RichieHH'nin yorumunun noktasını kaçırıyorsunuz. Göndermeden 3 yıl önce kabul edilen cevapla karşılaştırıldığında , önerdiğiniz OP'nin sorununu çözmesine yardımcı olmaz. Sebep: Hem OP tarafından gösterilen hem de kabul edilen yanıtta gösterildiği gibi kod bir UI işleyicisinin içinde çalışıyor . Söyleyen OMG of course do this in background threadarka plan iş parçacığı koymak NASIL göstermedikçe, önemsiz. Hangi zamanda, daha önce kabul edilmiş cevaptan daha karmaşık bir cevabınız olduğunu keşfedeceksiniz. Üç yıl önce daha iyi bir çözümün kabul edildiğinden bahsetmiş miydim? : P
Takım YapıcıSteve

2
... bu öneriyi özellikle soru dışı kılan şeyin OP'nin gecikmeden sonra açıkça UI eylemi yapmak istediğini belirtmeyi unutmuşum. Yani, bir arka plan iş parçacığı (sorunu çözmek için zaten gerekli olandan daha ağır) oluşturduğunuz, uyuduğunuz ve daha sonra (bir şekilde) UI iş parçacığına geri dönmeniz gereken bir çözümünüz olacaktır. Handler + postRunnabletüm bunları tek bir adımda gerçekleştirir. Sistem ikinci bir iş parçacığı oluşturma yükü olmadan.
ToolmakerSteve

2
Happyimdi mutlu: D? Ana soru: "Android'de iş parçacığını duraklatmak / uykuya geçirmek nasıl?" bu yüzden cevabım basitti :). Birisi "nasıl uyku iplik" için google bu soru gösterecektir. Muhtemelen cevabımı arıyor: P. Ama tamam tam yanıt ekledim;)
Dawid Drozd

28

Bunu kullanıyorum:

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

4
Ne e.getLocalizedMessage()yapmalı?
Philipp Reichart

basit, genel ve hızlı bir özel durum mesajı gerektiğinde e.getLocalizedMessage () kullanın
Byt3

1
Ancak bahse girerim, OP'nin istediği durumda, bir kullanıcı arayüzü tıklama yöntemi içinde, kullanıcı arayüzünde daha fazla güncelleme yapacak olan bunu yapmazsınız. Kabul edilen Handler/postDelayedçözümün iki avantajı vardır: (1) 2. iş parçacığının sistem ek yükünü önler, (1) UI iş parçacığı üzerinde çalışır, bu nedenle bir istisna yaratmadan UI değişiklikleri yapabilir.
ToolmakerSteve

1
Sorunun ana hedefi ile doğrudan ilgili değil, ancak İstisna yakalamamalısınız. Aksine InterruptedException yakalayın. İstisna yakalayarak, yanlış gidebilecek her şeyi yakalayacak ve böylece yakalayacağınız sorunları gizleyeceksiniz.
Herve Per

16

Muhtemelen böyle yapmak istemezsiniz. sleep()Düğmeye tıklanmış olay işleyicinize açık bir şekilde koyarak , tüm kullanıcı arayüzünü bir saniyeliğine kilitlersiniz. Bir alternatif, bir çeşit tek-çekim Zamanlayıcı kullanmaktır . Arka plan rengini varsayılan renge geri döndürmek için bir TimerTask oluşturun ve Timer'da zamanlayın.

Başka bir olasılık bir Handler kullanmaktır . Orada bir öğretici Eylemciler kullanarak bir Timer kullanarak anahtarlamalı biri hakkında.

Bu arada, bir işlemi duraklatamazsınız. Bir Java (veya Android) işleminde en az 1 iş parçacığı vardır ve yalnızca iş parçacıklarını uyku moduna alabilirsiniz.


14

CountDownTime kullanıyorum

new CountDownTimer(5000, 1000) {

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

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

}.start(); 

bu kullanışlı.
UzaySan

9

Günün sonunda bunu yaptım - şimdi iyi çalışıyor:

@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
Neden postDelayed? Zamanlayıcı gerekmez.
RichieHH

8

Bay Yankowsky'nin cevaplarına ek olarak kullanabilirsiniz postDelayed(). Bu, herhangi bir View(örneğin kartınız) için geçerlidir ve a Runnableve gecikme süresi alır. Bu Runnablegecikmeden sonra yürütür .


5

Bu benim örneğim

Java Yardımcı Programları Oluşturma

    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
Sorunun zaten iyi çözülmesinden yıllar sonra eklenen başka bir cevap, bu soru ile ilgili değildir, çünkü UI çalışması yapabilmek için belirtilen arzuyla ilgilenmemektedir. Burada UI çalışması yapamazsınız, çünkü yeni bir iş parçacığı üzerinde çalışıyorsunuz. UI iş parçacığında değil.
ToolmakerSteve

1
İşlerin UX tarafında, kullanıcıya veri yüklemek için bir engelleme iletişim kutusu göstermek iyi değil.
2Dee

4

Veya şunları kullanabilirsiniz:

android.os.SystemClock.sleep(checkEvery)

bir sargı gerektirmeme avantajına sahiptir try ... catch.


0

Kotlin ve coroutines kullanıyorsanız ,

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

Kullanıcı arayüzünü güncellemeniz gerekiyorsa

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

0

Bunun eski bir iş parçacığı olduğunu biliyorum, ancak Android belgelerinde benim için çok iyi çalışan bir çözüm buldum ...

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

Umarım bu birine yardımcı olur ...


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

Ve iki kat olmak için, tronman cevabında açıklandığı gibi "statik sınıf" yöntemiyle birleştirilebileceğinden emin olun.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.