Cara memanggil metode setelah penundaan di Android


770

Saya ingin dapat memanggil metode berikut setelah penundaan yang ditentukan. Dalam tujuan c ada sesuatu seperti:

[self performSelector:@selector(DoSomething) withObject:nil afterDelay:5];

Apakah ada yang setara dengan metode ini di android dengan java? Misalnya saya harus dapat memanggil metode setelah 5 detik.

public void DoSomething()
{
     //do something here
}

Jawaban:


1859

Kotlin

Handler().postDelayed({
  //Do something after 100ms
}, 100)


Jawa

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);



109
Solusi ini hanya bermanfaat pada utas UI. Kalau tidak, pada utas normal, Anda perlu mengimplementasikan looper yang bukan versi terbaik yang saya kira
olivier_sdg

2
@olivier_sdg mengapa Anda perlu menerapkan looper?
djechlin

37
@djechlin Handler harus selalu ditautkan ke Looper, yang sebenarnya akan memproses posting Runnable yang Anda (). Utas UI sudah dilengkapi dengan Looper, jadi Anda bisa membuat Handler baru () di utas UI dan memposting () Runnables langsung ke sana. Runnables ini dijalankan pada utas UI. Untuk menjalankan Runnables di utas lain, Anda perlu membuat utas baru, lalu Looper.prepare (), buat Handler baru () dan kemudian Looper.loop (). Setiap Runnables yang diposkan ke Handler baru ini akan dieksekusi di utas baru ini. Jika Anda tidak melakukan semua ini, postingan () akan memberikan pengecualian.
Dororo

12
Dalam kasus Anda perlu, Anda juga dapat membatalkan eksekusi selama Runnable masih dalam antrian pesan dengan menelepon removeCallbacks(Runnable r)pada Handler.
Dennis

9
harusimport android.os.handler
KaKa

322

Saya tidak dapat menggunakan jawaban lain dalam kasus saya. Saya menggunakan Timer java asli sebagai gantinya.

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // this code will be executed after 2 seconds       
    }
}, 2000);

43
ini lebih baik daripada yang menggunakan Handler, karena tidak memiliki masalah Looper ketika Handler tidak berjalan di utas UI.
Ben H

32
Anda harus menyimpan referensi ke penghitung waktu Anda untuk membatalkannya ketika itu tidak diperlukan lagi karena menurut dokumen Android: "Ketika penghitung waktu tidak lagi diperlukan, pengguna harus memanggil cancel (), yang melepaskan utas timer dan sumber daya lainnya. Pengatur waktu yang tidak dibatalkan secara eksplisit dapat menyimpan sumber daya tanpa batas. "
Pooks

14
Perhatian! Ini tidak berjalan di utas UI. Menjalankan ini pada utas ui menyebabkan Kesalahan Fatal: android.view.ViewRootImpl $ DipanggilDariWongTrukThreadException: Hanya utas asli yang membuat hierarki tampilan yang dapat menyentuh pandangannya.
vovahost

13
@vovahost itu hanya karena Anda memperbarui komponen UI di dalam blok timer
Tim

10
Perhatikan bahwa java.util.Timer (dan TimerTask) akan ditinggalkan dalam JDK 9. TimerTask membuat Thread baru untuk tugas yang tidak terlalu baik.
Varvara Kalinina

183

Catatan: Jawaban ini diberikan ketika pertanyaan tidak menentukan Android sebagai konteksnya. Untuk jawaban yang spesifik untuk utas Android UI lihat di sini.


Sepertinya Mac OS API memungkinkan utas saat ini berlanjut, dan menjadwalkan tugas untuk berjalan secara tidak sinkron. Di Java, fungsi yang setara disediakan oleh java.util.concurrentpaket. Saya tidak yakin batasan apa yang mungkin diterapkan oleh Android.

private static final ScheduledExecutorService worker = 
  Executors.newSingleThreadScheduledExecutor();

void someMethod() {
  
  Runnable task = new Runnable() {
    public void run() {
      /* Do something… */
    }
  };
  worker.schedule(task, 5, TimeUnit.SECONDS);
  
}

3
Ini tidak pernah memanggil Runnable untuk saya
Supuhstar

14
Sebagai catatan tambahan: Ini juga memungkinkan Anda untuk membatalkan tugas nanti, yang mungkin membantu dalam beberapa situasi. Cukup simpan referensi ke yang ScheduledFuture<?>dikembalikan oleh worker.schedule()dan panggil cancel(boolean)metodenya.
Dennis

Saya pikir jawaban ini sudah usang. .schedule sepertinya bukan metode Runnable lagi ...? : /
beetree

5
@beetree ini adalah metode aktif ScheduledExecutorService.
erickson

3
Ini tidak berfungsi jika objek ui thread terlibat, Anda harus memanggil runOnUIThread (runnable baru () {run () ....}); atau memposting runnable menggunakan objek handler dari dalam run () {}
Jayant Arora

107

Untuk mengeksekusi sesuatu di UI Thread setelah 5 detik:

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something here
    }
}, 5000);

8
Konfirmasikan, ini adalah solusi terbaik untuk mencegah panggilan ke looper.prepare dan menempatkan semuanya ke utas UI.
Tobliug

Terima kasih untuk ini, membantu saya dengan masalah Looper :)
Tia

1
Saya akan berhati-hati membuat handler di main looper, maka di utas ini tidak ada tugas lama yang harus dilakukan
Shayan_Aryan

40

Anda dapat menggunakan Handler di dalam UIThread:

runOnUiThread(new Runnable() {

    @Override
    public void run() {
         final Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
           @Override
           public void run() {
               //add your code here
           }
         }, 1000);

    }
});

36

Terima kasih atas semua jawaban hebatnya, saya menemukan solusi yang paling sesuai dengan kebutuhan saya.

Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);

class DoSomething extends Handler {
    @Override
    public void handleMessage(Message msg) {
      MyObject o = (MyObject) msg.obj;
      //do something here
    }
}

Apakah boleh jika saya menggunakan pendekatan ini untuk mendapatkan umpan balik sentuh pada klik suatu item .. view.setColor (some_color) dan kemudian hapus warna ini di Handler setelah x detik ...?
eRaisedToX

25

Kotlin& JavaBanyak Cara

1. Menggunakan Handler

Handler().postDelayed({
    TODO("Do something")
    }, 2000)

2. Menggunakan TimerTask

Timer().schedule(object : TimerTask() {
    override fun run() {
        TODO("Do something")
    }
}, 2000)

Atau bahkan lebih pendek

Timer().schedule(timerTask {
    TODO("Do something")
}, 2000)

Atau terpendek

Timer().schedule(2000) {
    TODO("Do something")
}

3. Menggunakan Executors

Executors.newSingleThreadScheduledExecutor().schedule({
    TODO("Do something")
}, 2, TimeUnit.SECONDS)

Di jawa

1. Menggunakan Handler

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something
    }
}, 2000);

2. Menggunakan Timer

new Timer().schedule(new TimerTask() {          
    @Override
    public void run() {
        // Do something
    }
}, 2000);

3. Menggunakan ScheduledExecutorService

private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();

Runnable runnable = new Runnable() {
  public void run() {
      // Do something
  }
  };
worker.schedule(runnable, 2, TimeUnit.SECONDS);

1
@ JanRabe Terima kasih atas saran Anda. Saya memperkirakannya. Namun pertanyaannya adalah How to call a method after a delay in Android. Jadi saya memfokuskan hal itu. Ke titik. Kalau tidak, kebocoran java adalah topik besar untuk dipahami secara terpisah untuk pengembang.
Khemraj

20

Lihat demo ini:

import java.util.Timer;
import java.util.TimerTask;

class Test {
     public static void main( String [] args ) {
          int delay = 5000;// in ms 

          Timer timer = new Timer();

          timer.schedule( new TimerTask(){
             public void run() { 
                 System.out.println("Wait, what..:");
              }
           }, delay);

           System.out.println("Would it run?");
     }
}

20

Jika Anda harus menggunakan Handler, tetapi Anda berada di utas lain, Anda dapat menggunakan runonuithreaduntuk menjalankan handler di utas UI. Ini akan menyelamatkan Anda dari Pengecualian yang diajukan untuk meneleponLooper.Prepare()

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do something after 1 second
            }
        }, 1000);
    }
});

Terlihat sangat berantakan, tetapi ini adalah salah satu caranya.


4
Ini berfungsi, saya tidak dapat mengedit posting Anda karena aturan SO bodoh dengan minimal 6 karakter untuk diedit, tetapi ada '()' hilang setelah 'Handler baru' itu harus 'Handler baru ()'
Jonathan Muller

2
Alih-alih menempatkan semuanya ke utas UI, Anda dapat melakukannya: new Handler (Looper.getMainLooper ())
Tobliug

17

Saya lebih suka menggunakan View.postDelayed()metode, kode sederhana di bawah ini:

mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do something after 1000 ms
    }
}, 1000);

1
Bukankah itu membekukan elemen ui itu sendiri, karena akan dijadwalkan pada penangan view?
JacksOnF1re

1
Tidak, tugas yang diposting akan dieksekusi dalam 1 detik, tetapi selama utas UI kedua ini melakukan pekerjaan bermanfaat lainnya
demaksee

14

Inilah solusi terpendek saya:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Do something after 100ms
    }
}, 100);

10
final Handler handler = new Handler(); 
Timer t = new Timer(); 
t.schedule(new TimerTask() { 
    public void run() { 
        handler.post(new Runnable() { 
            public void run() { 
                //DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
            }
        }); 
    } 
}, 5000); 

10

Jika Anda menggunakan Android Studio 3.0 dan di atasnya, Anda dapat menggunakan ekspresi lambda. Metode callMyMethod()ini dipanggil setelah 2 detik:

new Handler().postDelayed(() -> callMyMethod(), 2000);

Jika Anda perlu membatalkan runnable yang tertunda gunakan ini:

Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);

// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);

Bagaimana kita bisa membatalkan ini?
Damia Fuentes

Saya terkejut berapa banyak orang di sini yang akan dengan senang hati pindah ke Kotlin, namun sepenuhnya mengabaikan Ekspresi Lambda yang merupakan Java standar.
TomDK

6

Saya menyarankan Timer , ini memungkinkan Anda untuk menjadwalkan metode yang akan dipanggil pada interval yang sangat spesifik. Ini tidak akan memblokir UI Anda, dan menjaga aplikasi Anda tetap beresonansi saat metode ini dijalankan.

Opsi lainnya, adalah wait (); metode, ini akan memblokir utas saat ini untuk jangka waktu yang ditentukan. Ini akan menyebabkan UI Anda berhenti merespons jika Anda melakukan ini pada utas UI.


2
Thread.sleep () lebih baik dari Object.wait (). Tunggu menyiratkan bahwa Anda akan diberi tahu dan sedang melakukan sinkronisasi di sekitar beberapa aktivitas. Tidur menunjukkan bahwa Anda hanya ingin melakukan apa pun untuk jangka waktu tertentu. Timer adalah cara untuk pergi jika Anda ingin tindakan terjadi secara serempak pada suatu saat nanti.
Tim Bender

1
Itu benar. Itulah mengapa saya mendaftarkannya sebagai pilihan lain ;-)
Nate

6

Untuk tunda Post Menangani garis Sederhana, Anda dapat melakukan hal berikut:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Do someting
    }
}, 3000);

saya harap ini membantu


5

Anda dapat menggunakan ini untuk Solusi Sederhana:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        //Write your code here
    }
}, 5000); //Timer is in ms here.

Lain, Di bawah ini bisa menjadi solusi bersih yang bermanfaat:

new Handler().postDelayed(() -> 
{/*Do something here*/}, 
5000); //time in ms

5

Anda dapat membuatnya lebih bersih dengan menggunakan ekspresi lambda yang baru diperkenalkan:

new Handler().postDelayed(() -> {/*your code here*/}, time);

5

Jadi ada beberapa hal yang perlu dipertimbangkan di sini karena ada begitu banyak cara untuk menguliti kucing ini. Meskipun semua jawaban sudah diberikan dipilih dan dipilih. Saya pikir ini penting bahwa ini akan ditinjau kembali dengan pedoman pengkodean yang tepat untuk menghindari orang salah arah hanya karena "jawaban sederhana yang dipilih mayoritas".

Jadi pertama-tama mari kita bahas jawaban sederhana Post Delayed yaitu jawaban yang dipilih secara keseluruhan di utas ini.

Beberapa hal yang perlu dipertimbangkan. Setelah jeda posting, Anda dapat menemukan kebocoran memori, benda mati, siklus hidup yang telah hilang, dan banyak lagi. Jadi penanganannya dengan benar juga penting. Anda dapat melakukan ini dalam beberapa cara.

Demi perkembangan modern, saya akan memasok di KOTLIN

Berikut adalah contoh sederhana menggunakan utas UI pada panggilan balik dan mengonfirmasi bahwa aktivitas Anda masih hidup dan baik ketika Anda menekan panggilan balik Anda.

  Handler(Looper.getMainLooper()).postDelayed({
            if(activity != null && activity?.isFinishing == false){
                txtNewInfo.visibility = View.GONE
            }
        }, NEW_INFO_SHOW_TIMEOUT_MS)

Namun, ini masih belum sempurna karena tidak ada alasan untuk menelepon balik Anda jika aktivitas telah hilang. jadi cara yang lebih baik adalah menyimpan referensi dan menghapus callbacknya seperti ini.

    private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
        A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
        if(activity != null && activity?.isFinishing == false){
            txtNewInfo.visibility = View.VISIBLE
            mHandler.postDelayed({
                if(activity != null && activity?.isFinishing == false){
                    txtNewInfo.visibility = View.GONE
                }
            }, NEW_INFO_SHOW_TIMEOUT_MS)
        }
    }

dan tentu saja menangani pembersihan pada onPause sehingga tidak menekan callback.

    override fun onPause() {
        super.onPause()
        mHandler.removeCallbacks(null)
    }

Sekarang kita telah membahas yang jelas, mari kita bicara tentang opsi yang lebih bersih dengan coroutine dan kotlin modern :). Jika Anda belum menggunakannya, Anda benar-benar ketinggalan.

   fun doActionAfterDelay() 
        launch(UI) {
            delay(MS_TO_DELAY)           
            actionToTake()
        }
    }

atau jika Anda ingin selalu melakukan peluncuran UI pada metode itu, Anda cukup melakukannya:

  fun doActionAfterDelay() = launch(UI){ 
      delay(MS_TO_DELAY)           
      actionToTake()
  }

Tentu saja sama seperti PostDelayed Anda harus memastikan Anda menangani pembatalan sehingga Anda dapat melakukan pemeriksaan aktivitas setelah panggilan tunda atau Anda dapat membatalkannya di onPause sama seperti rute lainnya.

var mDelayedJob: Job? = null
fun doActionAfterDelay() 
   mDelayedJob = launch(UI) {
            try {
               delay(MS_TO_DELAY)           
               actionToTake()
            }catch(ex: JobCancellationException){
                showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
            }
        }
   }
}

// menangani pembersihan

override fun onPause() {
   super.onPause()
   if(mDelayedJob != null && mDelayedJob!!.isActive) {
      A35Log.v(mClassTag, "canceling delayed job")
      mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
   }
}

Jika Anda memasukkan peluncuran (UI) ke dalam metode tanda tangan, pekerjaan itu dapat ditetapkan dalam baris kode panggilan.

jadi moral dari cerita ini adalah untuk aman dengan tindakan Anda yang tertunda, pastikan Anda menghapus panggilan balik Anda, atau membatalkan pekerjaan Anda dan tentu saja mengkonfirmasi Anda memiliki siklus hidup yang tepat untuk menyentuh item pada penundaan panggilan balik Anda selesai. Coroutine juga menawarkan tindakan yang dapat dibatalkan.

Juga patut dicatat bahwa Anda biasanya harus menangani berbagai pengecualian yang dapat terjadi pada coroutine. Misalnya, pembatalan, pengecualian, batas waktu, apa pun yang Anda putuskan untuk digunakan. Ini adalah contoh yang lebih maju jika Anda memutuskan untuk benar-benar mulai menggunakan coroutine.

   mLoadJob = launch(UI){
            try {
                //Applies timeout
                withTimeout(4000) {
                    //Moves to background thread
                    withContext(DefaultDispatcher) {
                        mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
                    }
                }

                //Continues after async with context above
                showFancyToast("Loading complete", true, FancyToast.SUCCESS)
            }catch(ex: JobCancellationException){
                showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
            }catch (ex: TimeoutCancellationException) {
                showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
            }catch(ex: Exception){
                showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
            }
        }

1
Tidak masalah Rajiv, saya akan mengambil satu langkah lebih jauh dan menyebutkan bahwa dengan menggunakan Data Langsung, coroutine dapat menyadari siklus hidup dan membatalkan sendiri untuk menghindari panggilan pembersihan, tetapi tidak ingin membuang terlalu banyak kurva belajar ke dalam satu jawaban;)
Sam

3

Saya membuat metode yang lebih sederhana untuk menyebutnya.

public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
    {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                try {
                    Method method =  activity.getClass().getMethod(methodName);
                    method.invoke(activity);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }, miliseconds);
    }

Untuk menggunakannya, cukup hubungi: .CallWithDelay(5000, this, "DoSomething");


3
Refleksi untuk tugas dasar seperti itu?
Max Ch

Karena pertanyaan untuk memanggil metode mirip dengan iOS performSelector. ini adalah cara terbaik untuk melakukannya.
HelmiB

3

Di bawah satu berfungsi saat Anda mendapatkan,

java.lang.RuntimeException: Tidak dapat membuat handler di dalam utas yang belum disebut Looper.prepare ()

final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

3

Menggunakan Kotlin, kita dapat mencapai dengan melakukan hal berikut

Handler().postDelayed({
    // do something after 1000ms 
}, 1000)

2

Sangat mudah menggunakan CountDownTimer. Untuk detail lebih lanjut https://developer.android.com/reference/android/os/CountDownTimer.html

import android.os.CountDownTimer;

// calls onTick every second, finishes after 3 seconds
new CountDownTimer(3000, 1000) { 

   public void onTick(long millisUntilFinished) {
      Log.d("log", millisUntilFinished / 1000);
   }

   public void onFinish() {
      // called after count down is finished
   } 
}.start();

2

Jika Anda menggunakan RxAndroid maka penanganan thread dan kesalahan menjadi jauh lebih mudah. Kode berikut dijalankan setelah penundaan

   Observable.timer(delay, TimeUnit.SECONDS)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(aLong -> {
           // Execute code here
        }, Throwable::printStackTrace);

1

semua orang tampaknya lupa untuk membersihkan Handler sebelum memposting runnable baru atau pesan di atasnya. Selain itu mereka berpotensi menumpuk dan menyebabkan perilaku buruk.

handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.

handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.

1

Berikut ini cara rumit lainnya: itu tidak akan membuang pengecualian ketika runnable mengubah elemen UI.

public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {

    Runnable callBack;

    public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
        setDuration(delayTimeMilli);
        callBack = runnable;
        setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

    @Override
    public void onAnimationEnd(Animation animation) {
        callBack.run();
    }

    @Override
    public void onAnimationRepeat(Animation animation) {

    }
}

Anda dapat memanggil animasi seperti ini:

view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));

Animasi dapat dilampirkan ke tampilan apa pun.



1

Saya suka hal-hal yang lebih bersih: Ini adalah implementasi saya, kode inline untuk digunakan di dalam metode Anda

new Handler().postDelayed(new Runnable() {
  @Override
  public void run() {
    //Do something after 100ms
  }
}, 100);

0

Solusi yang cocok di android:

private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
            launcher.start();
.
.
private class MyLauncher extends Thread {
        @Override
        /**
         * Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any. 
         */
        public void run() {
            try {
                // Sleeping
                Thread.sleep(SLEEP_TIME * 1000);
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
            }
            //do something you want to do
           //And your code will be executed after 2 second
        }
    }

0

Solusi serupa tetapi lebih bersih untuk digunakan

Tulis fungsi ini di luar kelas

fun delay(duration: Long, `do`: () -> Unit) {

    Handler().postDelayed(`do`, duration)

}

Pemakaian:

delay(5000) {
    //Do your work here
}

Apa yang dilakukan `lakukan`?
Skizo-ozᴉʞS

hanya nama, simpan apa saja di sana. doadalah metode inbuilt jadi kita harus menggunakan `untuk menggunakannya sebagai nama variabel
Manohar Reddy

Terima kasih, tetapi mengapa menggunakan nama variabel ini? Maksud saya apa fungsinya.
Skizo-ozᴉʞS

1
Saya berpikir seperti doini setelah penundaan 3 detik
Manohar Reddy

0

Di Android, kita dapat menulis kode kotlin di bawah ini untuk menunda eksekusi fungsi apa pun

class MainActivity : AppCompatActivity() {

private lateinit var handler: Handler

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    handler= Handler()
    handler.postDelayed({
        doSomething()
    },2000)
}

private fun doSomething() {
    Toast.makeText(this,"Hi! I am Toast Message",Toast.LENGTH_SHORT).show()
}
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.