Bagaimana cara menghentikan / memproses utas atau proses di Android?

294

Saya ingin membuat jeda di antara dua baris kode, Biarkan saya jelaskan sedikit:

-> pengguna mengklik tombol (kartu sebenarnya) dan saya menunjukkannya dengan mengubah latar belakang tombol ini:

thisbutton.setBackgroundResource(R.drawable.icon);

-> setelah misalkan 1 detik, saya harus kembali ke keadaan tombol sebelumnya dengan mengubah latar belakangnya:

thisbutton.setBackgroundResource(R.drawable.defaultcard);

-> Saya sudah mencoba menjeda utas antara dua baris kode ini dengan:

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

Namun, ini tidak berhasil. Mungkin prosesnya dan bukan Thread yang perlu saya hentikan?

Saya juga sudah mencoba (tetapi tidak berhasil):

new Reminder(5);

Dengan ini:

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

Bagaimana saya bisa menjeda / tidur utas atau proses?

Hubert
sumber
4
Oh, cukup gunakan blok jeda utas klasik: while (true) {}
KristoferA
6
@ KristoferA-Huagati.com Saya tidak yakin apakah Anda sedang menyindir atau memang ada beberapa sihir Dalvik / Android sehingga ini dapat diterima di Android. Bisakah Anda mengklarifikasi? Maaf karena ragu tapi saya bertanya karena sementara (!conditionCheck()) {}biasanya berkecil hati.
Variabel sengsara
"Namun, ini tidak berhasil." "Saya juga sudah mencoba (tetapi tidak berhasil)" Ini adalah contoh klasik dari mengatakan ada masalah tanpa memberikan gejala. Dalam hal apa upaya ini gagal memenuhi persyaratan Anda? Apakah utasnya tidak berhenti? Apakah Anda mendapatkan pesan kesalahan?
LarsH

Jawaban:

454

Salah satu solusi untuk masalah ini adalah dengan menggunakan metode Handler.postDelayed () . Beberapa materi pelatihan Google menyarankan solusi yang sama.

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

Namun, beberapa telah menunjukkan bahwa solusi di atas menyebabkan kebocoran memori karena menggunakan kelas non-statis dalam dan anonim yang secara implisit memegang referensi ke kelas luarnya, aktivitas. Ini adalah masalah ketika konteks aktivitasnya adalah pengumpulan sampah.

Solusi yang lebih kompleks yang menghindari kebocoran memori mengelompokkan Handlerdan Runnabledengan kelas dalam statis di dalam aktivitas karena kelas statis dalam tidak memegang referensi implisit ke kelas luar mereka:

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

Perhatikan bahwa Runnablemenggunakan WeakReference to Activity, yang diperlukan di kelas statis yang membutuhkan akses ke UI.

tukang trem
sumber
3
Ini berfungsi, tapi itu cara yang cukup merepotkan untuk memperkenalkan penundaan di seluruh kode, kan?
Ehtesh Choudhury
4
Saya tidak yakin apa yang Anda maksud dengan "tidak nyaman". Metode postDelay Handler dirancang untuk memberi tahu Android bahwa Anda ingin sedikit kode dieksekusi setelah sejumlah waktu berlalu.
tronman
27
setelah 2 tahun dan kode ini hanya membantu saya! terima kasih @tronman !! :)
Melvin Lai
2
Anda cukup menyalinnya ke variabel (final) lain seperti itu final Button mynewbutton = mybutton;dan gunakan mynewbuttondi Handler dan Runnable dari sana.
Dzhuneyt
2
@MelvinLai setelah 5 tahun dan kode ini hanya membantu saya! :)
gabrieloliveira
191

Anda dapat mencoba yang ini pendek

SystemClock.sleep(7000);

PERINGATAN : Tidak pernah, lakukan ini di utas UI.

Gunakan ini untuk tidur misalnya. utas latar belakang.


Solusi lengkap untuk masalah Anda adalah: Ini tersedia 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);
            }
        });

Tanpa membuat tmp Handler. Juga solusi ini lebih baik daripada @tronman karena kami tidak mempertahankan pandangan oleh Handler. Juga kami tidak memiliki masalah dengan Handler dibuat di utas buruk;)

Dokumentasi

public static void sleep (panjang ms)

Ditambahkan di API level 1

Menunggu sejumlah milidetik (dari uptimeMillis) sebelum kembali. Mirip dengan tidur (lama), tetapi tidak membuang InterruptedException ; interrupt () peristiwa ditunda hingga operasi interruptible berikutnya. Tidak kembali sampai setidaknya jumlah milidetik yang ditentukan telah berlalu.

Parameter

ms untuk tidur sebelum kembali, dalam waktu milidetik uptime.

Kode untuk postDelayed dari View class:

/**
 * <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;
}
Dawid Drozd
sumber
22
Dan biarkan UI Android membeku? Jangan lakukan ini.
shkschneider
3
Ctrl + Shift + O (Eclipse auto import)
Dawid Drozd
13
Gelldur, Anda kehilangan inti dari komentar @ RichieHH. Dibandingkan dengan jawaban yang diterima 3 tahun sebelum Anda memposting , apa yang Anda sarankan tidak membantu OP memecahkan masalahnya. Alasan: Kode, baik seperti yang ditunjukkan oleh OP, dan seperti yang ditunjukkan dalam jawaban yang diterima, berjalan di dalam handler UI . Mengatakan OMG of course do this in background threadtidak relevan, kecuali jika Anda menunjukkan BAGAIMANA memasukkannya ke utas latar. Pada saat itu, Anda akan menemukan bahwa Anda memiliki jawaban yang lebih rumit daripada jawaban yang sudah diterima. Apakah saya menyebutkan bahwa solusi yang lebih baik diterima tiga tahun lalu? : P
ToolmakerSteve
2
... lupa menyebutkan bahwa apa yang membuat saran ini sangat tidak sesuai dengan pertanyaan, adalah bahwa OP secara eksplisit ingin melakukan tindakan UI setelah penundaan. Jadi, Anda akan memiliki solusi di mana Anda telah membuat utas latar (sudah lebih berat daripada yang dibutuhkan untuk menyelesaikan masalah), tidur, dan kemudian Anda harus (entah bagaimana) kembali ke utas UI. Handler + postRunnablemenyelesaikan semua ini, dalam satu langkah. Tanpa overhead sistem untuk membuat utas kedua.
ToolmakerSteve
2
@ToolmakerSteve senang sekarang: D? Pertanyaan utama adalah: "Bagaimana cara menghentikan / memproses thread atau proses di Android?" jadi jawaban saya sederhana :). Jika seseorang mencari "bagaimana cara tidur", pertanyaan ini akan muncul. Dia mungkin sedang mencari jawaban saya: P. Tapi ok saya menambahkan respons penuh;)
Dawid Drozd
28

Saya menggunakan ini:

Thread closeActivity = new Thread(new Runnable() {
  @Override
  public void run() {
    try {
      Thread.sleep(3000);
      // Do some stuff
    } catch (Exception e) {
      e.getLocalizedMessage();
    }
  }
});
Byt3
sumber
4
Apa yang e.getLocalizedMessage()seharusnya dilakukan?
Philipp Reichart
saya menggunakan e.getLocalizedMessage () ketika saya membutuhkan pesan pengecualian sederhana, umum, dan cepat
Byt3
1
Tapi saya yakin Anda tidak melakukan ini dalam situasi yang ditanyakan OP, di dalam metode klik UI, yang akan membuat pembaruan lebih lanjut ke UI. Handler/postDelayedSolusi yang diterima memiliki dua keunggulan: (1) menghindari overhead sistem ke-2 utas, (1) berjalan pada utas UI, sehingga dapat membuat perubahan UI tanpa menyebabkan pengecualian.
ToolmakerSteve
1
Tidak terkait langsung dengan tujuan utama dari pertanyaan tetapi Anda tidak harus menangkap Pengecualian. Alih-alih menangkap InterruptedException. Dengan menangkap Exception, Anda akan menangkap semua yang salah, sehingga menyembunyikan masalah yang seharusnya Anda tangkap.
Herve Thu
16

Anda mungkin tidak ingin melakukannya dengan cara itu. Dengan menempatkan eksplisit sleep()di pengendali event yang diklik tombol Anda, Anda benar-benar akan mengunci seluruh UI selama satu detik. Salah satu alternatif adalah menggunakan semacam Timer sekali pakai . Buat TimerTask untuk mengubah warna latar belakang kembali ke warna default, dan jadwalkan pada Timer.

Kemungkinan lain adalah menggunakan Handler . Ada tutorial tentang seseorang yang beralih dari menggunakan Timer ke menggunakan Handler.

Secara kebetulan, Anda tidak dapat menjeda proses. Proses Java (atau Android) memiliki setidaknya 1 utas, dan Anda hanya dapat tidur utas.

Daniel Yankowsky
sumber
14

Saya menggunakan 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(); 
vudandroid
sumber
ini berguna.
UzaySan
9

Inilah yang saya lakukan pada akhir hari - bekerja dengan baik sekarang:

@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); 
    }
Hubert
sumber
2
Mengapa tidak ditunda? Tidak diperlukan timer.
RichieHH
8

Selain jawaban Mr. Yankowsky, Anda juga bisa menggunakan postDelayed(). Ini tersedia pada semua View(mis. Kartu Anda) dan membutuhkan waktu Runnabledan penundaan. Itu mengeksekusi Runnablesetelah penundaan itu.

CommonsWare
sumber
5

Ini adalah contoh saya

Buat Utils 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();  

        }

    }    
Stefano
sumber
3
Namun jawaban lain ditambahkan bertahun-tahun setelah pertanyaan sudah diselesaikan dengan baik, yang tidak relevan dengan pertanyaan itu, karena tidak berhubungan dengan keinginan yang dinyatakan untuk dapat melakukan pekerjaan UI. Anda tidak dapat melakukan UI berfungsi di sini, karena Anda menjalankan utas baru. Bukan pada utas UI.
ToolmakerSteve
1
Di sisi UX, menunjukkan dialog pemblokiran kepada pengguna untuk memuat data ... tidak baik.
2Dee
4

Atau Anda bisa menggunakan:

android.os.SystemClock.sleep(checkEvery)

yang memiliki keuntungan tidak memerlukan pembungkus try ... catch.

aaaa
sumber
0

Jika Anda menggunakan Kotlin dan coroutine , Anda bisa melakukannya

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

Dan jika Anda perlu memperbarui UI

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

Saya tahu ini adalah utas lama, tetapi dalam dokumentasi Android saya menemukan solusi yang bekerja sangat baik untuk saya ...

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

Semoga ini bisa membantu seseorang ...

John Byrne
sumber
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);
      }
    }

Dan untuk menjadi dobel yakin Anda dapat dikombinasikan dengan metode "kelas statis" seperti yang dijelaskan dalam jawaban tronman

pemula
sumber