Android'de Runnable iş parçacığı belirli aralıklarla nasıl çalıştırılır?


351

Android öykünücü ekranında belirli aralıklarla bazı metinleri görüntülemek için bir uygulama geliştirdim. HandlerSınıfı kullanıyorum . İşte kodumdan bir pasaj:

handler = new Handler();
Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");               
    }
};
handler.postDelayed(r, 1000);

Bu uygulamayı çalıştırdığımda metin yalnızca bir kez görüntülenir. Neden?


109
Ben bir runnable nasıl asla hatırlayamıyorum, bu yüzden her zaman nasıl yapılacağı hakkında yazınızı ziyaret :))
Adrian Sicaru

2
haha aynı burada dostum yani gerçek
NoXSaeeD

1
lambdas çoğu zaman şimdi gitmenin yoludur;)
Xerus

@AdrianSicaru: aynı
Sovandara LENG

Yanıtlar:


535

Örneğiniz için basit düzeltme:

handler = new Handler();

final Runnable r = new Runnable() {
    public void run() {
        tv.append("Hello World");
        handler.postDelayed(this, 1000);
    }
};

handler.postDelayed(r, 1000);

Veya normal ipliği kullanabiliriz (orijinal Runner ile):

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                handler.post(this);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

thread.start();

Runnable nesnenizi, yürütme için ileti kuyruğuna gönderilebilen bir komut ve işleyiciyi yalnızca bu komutu göndermek için kullanılan bir yardımcı nesne olarak düşünebilirsiniz.

Daha fazla ayrıntı burada http://developer.android.com/reference/android/os/Handler.html


Alex, küçük bir şüphe var.Şimdi iplik mükemmel çalışıyor ve metni sürekli olarak gösteriyor, eğer bunu durdurmak istiyorsam ne yapmam gerektiği anlamına mı geliyor? Lütfen bana yardım et.
Rajapandian

11
Boole değişkeni _stop'u tanımlayabilir ve durdurmak istediğinizde 'true' değerini ayarlayabilirsiniz. Ve 'while (true)' öğesini 'while (! _ Stop)' olarak değiştirin veya ilk örnek kullanılıyorsa, 'if (! _ Stop) handler.postDelayed (this, 1000)' olarak değiştirin.
alex2k8

Mesajı yeniden başlatmak istersem ne olur?
Sonhja

ve 8 farklı ImageViews birbiri ardına görünür ayarlamak için bir runnable gerekiyorsa, o zaman hepsini aynı şekilde görünmez ve böylece ("yanıp sönen" animasyon oluşturmak için), bunu nasıl yapabilirim?
Droidman

1
İşleyicinin ana iş parçacığına ekleneceğinden emin olmak istiyorsanız, bunu şu şekilde başlatmalısınız: handler = new Handler (Looper.getMainLooper ());
Yair Kukielka

47
new Handler().postDelayed(new Runnable() {
    public void run() {
        // do something...              
    }
}, 100);

2
İşleyicinin ana iş parçacığına ekleneceğinden emin olmak istiyorsanız, bunu şu şekilde başlatmalısınız: new Handler (Looper.getMainLooper ());
Yair Kukielka

1
Bu çözüm orijinal gönderiye eşdeğer değil mi? Runnable'ı yalnızca 100 milisaniyeden sonra çalıştırır.
tronman

@YairKukielka cevap çözümdür! MainLooper'ı bağlamanız gerekir. böyle bir hayat kurtarıcı!
Houssem Chlegou

40

Sanırım her saniye düzeltmek için Alex2k8'in ilk çözümünü geliştirebilir

1. orijinal kod:

public void run() {
    tv.append("Hello World");
    handler.postDelayed(this, 1000);
}

2.Analysis

  • Yukarıdaki maliyette, tv.append("Hello Word")maliyeti T milisaniye olarak kabul edin , görüntülemenin ardından 500 kez gecikmeli süre 500 * T milisaniye
  • Uzun süre çalıştığında gecikecek

3. Çözüm

Bundan kaçınmak için Sadece postDelayed () sırasını değiştirin, gecikmeyi önlemek için:

public void run() {
    handler.postDelayed(this, 1000);
    tv.append("Hello World");
}

6
-1, run () içinde gerçekleştirdiğiniz görevin her çalıştırma için sabit bir tutar olduğunu varsayarsınız, eğer bu dinamik veriler üzerinde işlem olsaydı (tipik olarak) o zaman birden fazla run () ile sonuçlanırsınız bir Zamanlar. Bu nedenle postDelayed tipik olarak sonuna yerleştirilir.
Jay

1
@Jay Maalesef yanılıyorsunuz. Bir işleyici tek bir iş parçacığı (ve o iş parçacığının çalışma yöntemi olan bir lüper) + bir MessageQueue ile ilişkilidir. Her Mesaj gönderdiğinizde, onu sıralarsınız ve ilmek yapıcı sırayı bir sonraki kontrol ettiğinde, yayınladığınız Runnable'ın çalıştırma yöntemini yürütür. Tüm bunlar tek bir Konu'da gerçekleştiği için aynı anda 1'den fazla çalıştırılamaz. Ayrıca postDelayed'i ilk önce yapmak, yürütme başına 1000 ms'ye yaklaşmanızı sağlar çünkü dahili olarak yürütme süresi olarak geçerli saati + 1000 kullanır. Kodu yazıdan önce koyarsanız ek gecikme eklersiniz.
zapl

1
@zapl işleyici hakkında ipucu için teşekkürler, ben birden çok runnable ve dolayısıyla, birden çok iş parçacığı yürütme olacağını varsaydım. Dahili olarak, eğer çalışma süreleri 1000 ms'den az veya ona eşit olduğunda if ((geçerli saat - son çalışma zamanı> 1000) gibi bir durum iyi çalışır, ancak bu aşıldığında, zamanlayıcı doğrusal olmayan aralıklarda gerçekleşir) tamamen çalıştırma yönteminin yürütme süresine bağlı (bu nedenle öngörülemeyen hesaplama gideriyle ilgili benim açım)
Jay

Sabit bir süre, çatışma olmadan, iş yapmadan önce başlangıç ​​zamanını ölçün ve gecikmenizi buna göre ayarlayın. İşlemci meşgulse yine de biraz gecikme göreceksiniz, ancak daha katı bir süreye izin verebilir ve sistemin aşırı yüklenip yüklenmediğini algılayabilir (belki de düşük öncelikli şeylerin geri çekileceğini bildirmek için).
Ajax

27

Tekrarlanan görevler için şunları kullanabilirsiniz:

new Timer().scheduleAtFixedRate(task, runAfterADelayForFirstTime, repeaingTimeInterval);

şöyle söyle

new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {

            }
        },500,1000);

Yukarıdaki kod yarım saniyeden (500) sonra ilk kez çalışır ve her saniyeden sonra (1000) tekrarlanır.

Nerede

görev , yürütülecek yöntem

ilk uygulamadan sonra

( yürütmenin tekrarlanması için zaman aralığı )

ikinci olarak

Ayrıca, bir Görev sayısını kaç kez yürütmek istiyorsanız CountDownTimer'i de kullanabilirsiniz .

    new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

     public void onTick(long millisUntilFinished) {
      }
      public void onFinish() {
     }
    }.start();

//Above codes run 40 times after each second

Ve bunu runnable ile de yapabilirsiniz. gibi çalıştırılabilir bir yöntem oluştur

Runnable runnable = new Runnable()
    {
        @Override
        public void run()
        {

        }
    };

Ve her iki şekilde de söyleyin

new Handler().postDelayed(runnable, 500 );//where 500 is delayMillis  // to work on mainThread

VEYA

new Thread(runnable).start();//to work in Background 

3. seçenek için, nasıl duraklatabilir / devam ettirebilirim ve ayrıca kalıcı olarak durdurabilirim?
Si8

Handler gibi bir Handler örneği oluşturun handler = new Handler () ve handler.removeCallbacksAndMessages (null) gibi kaldırın;
Zar E Ahmer

24

Ben sabit bir aralıkta bir şey çalıştırmak için, yani bu tipik durum için inanmak Timerdaha uygundur. İşte basit bir örnek:

myTimer = new Timer();
myTimer.schedule(new TimerTask() {          
@Override
public void run() {
    // If you want to modify a view in your Activity
    MyActivity.this.runOnUiThread(new Runnable()
        public void run(){
            tv.append("Hello World");
        });
    }
}, 1000, 1000); // initial delay 1 second, interval 1 second

Kullanmanın Timerbirkaç avantajı vardır:

  • İlk gecikme ve aralık scheduleişlev bağımsız değişkenlerinde kolayca belirtilebilir
  • Zamanlayıcı sadece arayarak durdurulabilir myTimer.cancel()
  • Yalnızca bir iş parçacığının çalışmasını istiyorsanız, yeni bir iş parçacığı zamanlamadan myTimer.cancel() önce aramayı unutmayın (myTimer boş değilse)

7
Android yaşam döngüsünü dikkate almadığı için bir zamanlayıcının daha uygun olduğuna inanmıyorum. Duraklatıp devam ettirdiğinizde, zamanlayıcının düzgün çalışacağı garantisi yoktur. Runnable'ın daha iyi bir seçim olduğunu iddia ediyorum.
Janpan

1
Bu, bir uygulama arka plana yerleştirildiğinde bir İşleyici duraklatılacağı anlamına mı gelir? ve odaklanmaya başladığında (az ya da çok) hiçbir şey olmamış gibi devam edecek mi?
Andrew Gallasch

17
Handler handler=new Handler();
Runnable r = new Runnable(){
    public void run() {
        tv.append("Hello World");                       
        handler.postDelayed(r, 1000);
    }
}; 
handler.post(r);

5
Bu bir hata vermelidir. İkinci satırınızda r, henüz tanımlanmış olan değişkeni çağırıyorsunuz .
seul

İşleyicinin ana iş parçacığına ekleneceğinden emin olmak istiyorsanız, bunu şu şekilde başlatmalısınız: handler = new Handler (Looper.getMainLooper ());
Yair Kukielka

sadece tekrarlayan cevap!
Hamid

Bir görüntü görüntüleme tıklamasıyla çalıştırılabilir dosyayı nasıl duraklatabilir / devam ettirebilirim?
Si8

4

Handler.post () yönteminin belgelerini doğru anlarsam:

Çalıştırılabilir r'nin ileti kuyruğuna eklenmesine neden olur. Çalıştırılabilir bu işleyicinin bağlı olduğu iş parçacığında çalıştırılır.

Bu yüzden @ alex2k8 tarafından sağlanan örnekler, doğru çalışıyor olsalar da, aynı değildir. Nerede Handler.post()kullanılırsa, yeni bir iş parçacığı oluşturulmaz . Sadece EDT tarafından yürütülecek Runnablekonuya mesaj gönderirsiniz . Bundan sonra, EDT sadece başka hiçbir şey yürütmez.HandlerRunnable.run()

Unutmayın: Runnable != Thread.


1
Bu doğru. Her zaman yeni bir ileti dizisi oluşturmayın. İşleyici ve diğer yürütme havuzlarının amacı, iş parçacığı oluşturma ve GC'yi önlemek için bir veya iki iş parçacığının görevleri kuyruktan almasını sağlamaktır. Gerçekten sızdıran bir uygulamanız varsa, ekstra GC OutOfMemory durumlarını örtmeye yardımcı olabilir, ancak her iki durumda da daha iyi çözüm, ihtiyacınız olandan daha fazla iş oluşturmaktan kaçınmaktır.
Ajax

Bunu yapmanın daha iyi yolu, alex2k8'in cevabına dayanan normal iş parçacığını kullanmaktır?
Compaq LE2202x

4

Kotlin

private lateinit var runnable: Runnable
override fun onCreate(savedInstanceState: Bundle?) {
    val handler = Handler()
    runnable = Runnable {
        // do your work
        handler.postDelayed(runnable, 2000)
    }
    handler.postDelayed(runnable, 2000)
}

Java

Runnable runnable;
Handler handler;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    handler = new Handler();
    runnable = new Runnable() {
        @Override
        public void run() {
            // do your work
            handler.postDelayed(this, 1000);
        }
    };
    handler.postDelayed(runnable, 1000);
}

1

İlginç bir örnek, sürekli olarak ayrı bir iş parçacığında çalışan bir sayaç / kronometre görebilirsiniz. Ayrıca GPS-Konum gösteriliyor. Ana faaliyet Kullanıcı Arabirim Konu zaten oradayken.

Alıntı:

try {    
    cnt++; scnt++;
    now=System.currentTimeMillis();
    r=rand.nextInt(6); r++;    
    loc=lm.getLastKnownLocation(best);    

    if(loc!=null) { 
        lat=loc.getLatitude();
        lng=loc.getLongitude(); 
    }    

    Thread.sleep(100); 
    handler.sendMessage(handler.obtainMessage());
} catch (InterruptedException e) {   
    Toast.makeText(this, "Error="+e.toString(), Toast.LENGTH_LONG).show();
}

Koda bakmak için buraya bakın:

Ana etkinliğin Kullanıcı Arabirimi İş Parçasının yanında GPS Konumunu ve Geçerli Saati çalıştırabilecek iş parçacığı örneği


1
İpucu: Cevabınızı yararlı hale getirmek istiyorsanız - girişi nasıl biçimlendireceğinizi buradan öğrenin. Bu önizleme penceresi bir nedenden dolayı var.
GhostCat

0

şimdi Kotlin'de konuları şu şekilde çalıştırabilirsiniz:

class SimpleRunnable: Runnable {
    public override fun run() {
        println("${Thread.currentThread()} has run.")
    }
}
fun main(args: Array<String>) {
    val thread = SimpleThread()
    thread.start() // Will output: Thread[Thread-0,5,main] has run.
    val runnable = SimpleRunnable()
    val thread1 = Thread(runnable)
    thread1.start() // Will output: Thread[Thread-1,5,main] has run
}

0

Kotlin ile Coroutines

Kotlin'de, coroutines kullanarak aşağıdakileri yapabilirsiniz:

CoroutineScope(Dispatchers.Main).launch { // Main, because UI is changed
    ticker(delayMillis = 1000, initialDelayMillis = 1000).consumeEach {
        tv.append("Hello World")
    }
}

Burada deneyin !

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.