จะหยุดชั่วคราว / พักด้ายหรือกระบวนการใน 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
โอ้เพียงใช้บล็อกหยุดด้ายแบบคลาสสิค: ในขณะที่ (จริง) {}
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); 
}

อย่างไรก็ตามบางคนได้ชี้ให้เห็นว่าการแก้ปัญหาข้างต้นทำให้เกิดการรั่วไหลของหน่วยความจำเพราะมันใช้ชั้นภายในแบบไม่คงที่และไม่ระบุชื่อซึ่งมีการอ้างอิงถึงกิจกรรมชั้นนอกของมันโดยปริยาย นี่เป็นปัญหาเมื่อบริบทของกิจกรรมเป็นที่รวบรวมขยะ

วิธีแก้ปัญหาที่ซับซ้อนมากขึ้นที่จะหลีกเลี่ยงการรั่วไหลของหน่วยความจำ subclasses 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ใช้WeakReferenceกับกิจกรรมซึ่งจำเป็นในคลาสสแตติกที่ต้องการเข้าถึง UI


3
มันใช้งานได้ แต่เป็นวิธีที่ไม่สะดวกในการแนะนำความล่าช้าในโค้ดใช่ไหม?
Ehtesh Choudhury

4
ฉันไม่แน่ใจว่าสิ่งที่คุณหมายถึงโดย "ไม่สะดวก" เมธอด postDelayed ของ Handler ออกแบบมาเพื่อบอก Android ว่าคุณต้องการให้โค้ดทำงานสักครู่หลังจากเวลาผ่านไประยะหนึ่ง
tronman

27
หลังจาก 2 ปีและรหัสนี้ก็ช่วยฉันได้! ขอบคุณ @tronman !! :)
Melvin Lai

2
คุณสามารถคัดลอกไปยังตัวแปรอื่น (สุดท้าย) เช่นนั้นfinal Button mynewbutton = mybutton;และใช้mynewbuttonในตัวจัดการและ Runnable จากที่นั่นไป
Dzhuneyt

2
@ MelvinLai หลังจาก 5 ปีและรหัสนี้ก็ช่วยฉัน! :)
gabrieloliveira

191

คุณสามารถลองอันนี้สั้น

SystemClock.sleep(7000);

คำเตือน : ไม่เคยทำเช่นนี้ในหัวข้อ UI

ใช้สิ่งนี้เพื่อนอนหลับเช่น ด้ายพื้นหลัง


วิธีแก้ไขปัญหาสำหรับปัญหาของคุณคือ: นี่คือ 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 นอกจากนี้เราไม่มีปัญหากับตัวจัดการที่สร้างขึ้นในเธรดที่ไม่ดี;)

เอกสาร

โมฆะคงที่สาธารณะสลีป (ยาว ms)

เพิ่มในระดับ API 1

รอจำนวนมิลลิวินาทีที่กำหนด (จาก uptimeMillis) ก่อนส่งคืน คล้ายกับการนอนหลับ (ยาว) แต่ไม่มีการขัดจังหวะด้วยข้อยกเว้น ; เหตุการณ์ขัดจังหวะ () จะถูกเลื่อนออกไปจนกว่าการดำเนินการขัดจังหวะครั้งต่อไป ไม่ส่งคืนจนกว่าอย่างน้อยจำนวนมิลลิวินาทีที่ระบุจะผ่านไป

พารามิเตอร์

msเข้าสู่โหมดสลีก่อนกลับมาเป็นมิลลิวินาที

รหัสสำหรับการโพสต์ล่าช้าจากดูคลาส:

/**
 * <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 UI ค้างหรือไม่ อย่าทำอย่างนี้
shkschneider

3
Ctrl + Shift + O (การนำเข้าอัตโนมัติของ Eclipse)
Dawid Drozd

13
Gelldur คุณหายไปจากความคิดเห็นของ @ RichieHH เปรียบเทียบกับคำตอบที่ตอบรับก่อน 3 ปีก่อนที่คุณโพสต์สิ่งที่คุณแนะนำไม่ช่วย OP ในการแก้ปัญหาของเขา เหตุผล: รหัสทั้งสองเป็นที่แสดงโดย OP และตามที่ปรากฏในคำตอบที่ได้รับการยอมรับ, กำลังทำงานอยู่ภายในตัวจัดการ UI การพูดOMG of course do this in background threadไม่เกี่ยวข้องยกเว้นว่าคุณแสดงวิธีการใส่ไว้ในเธรดพื้นหลัง ในเวลานั้นคุณจะพบว่าคุณมีคำตอบที่ซับซ้อนกว่าคำตอบที่ยอมรับแล้ว ฉันไม่ได้พูดถึงว่าวิธีการแก้ปัญหาที่ดีกว่าได้รับการยอมรับเมื่อสามปีที่แล้ว? : P
ToolmakerSteve

2
... ลืมที่จะพูดถึงว่าสิ่งที่ทำให้ข้อเสนอแนะนี้โดยเฉพาะอย่างยิ่งผิดวัตถุประสงค์คำถามคือ OP ต้องการดำเนินการ UI หลังจากล่าช้า ดังนั้นคุณจะมีวิธีแก้ปัญหาที่คุณสร้างเธรดพื้นหลัง (น้ำหนักที่หนักกว่าที่จำเป็นในการแก้ปัญหาแล้ว) นอนหลับแล้วคุณต้องกลับไปที่เธรด UI อย่างใด Handler + postRunnableสำเร็จทั้งหมดนี้ในขั้นตอนเดียว ไม่มีค่าใช้จ่ายของระบบในการสร้างเธรดที่สอง
ToolmakerSteve

2
@ToolmakerSteve มีความสุขตอนนี้: D? คำถามหลักคือ: "จะหยุดชั่วคราว / นอนด้ายหรือกระบวนการใน Android ได้อย่างไร" ดังนั้นคำตอบของฉันง่ายมาก :) หากมีคน google สำหรับ "วิธีการนอนด้าย" คำถามนี้จะแสดง เขา / เธออาจกำลังมองหาคำตอบของฉัน: P แต่ตกลงฉันเพิ่มการตอบกลับเต็มรูปแบบ;)
389 Dawid Drozd

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()ควรทำคืออะไร?
Philipp Reichart

ฉันใช้ e.getLocalizedMessage () เมื่อฉันต้องการข้อความยกเว้นที่ง่ายทั่วไปและรวดเร็ว
Byt3

1
แต่ฉันคิดว่าคุณไม่ได้ทำในสถานการณ์ที่ OP ถามภายในวิธีการคลิก UI ซึ่งจะทำการอัปเดตเพิ่มเติมไปยัง UI Handler/postDelayedโซลูชันที่ยอมรับนั้นมีข้อดีสองประการ: (1) หลีกเลี่ยงโอเวอร์เฮดของระบบของเธรดที่ 2 (1) รันบนเธรด UI ดังนั้นสามารถเปลี่ยนแปลง UI ได้โดยไม่ทำให้เกิดข้อยกเว้น
ToolmakerSteve

1
ไม่เกี่ยวข้องโดยตรงกับเป้าหมายหลักของคำถาม แต่คุณไม่ควรรับข้อยกเว้น ค่อนข้างจับ InterruptedException โดยการจับยกเว้นคุณจะจับทุกอย่างที่ผิดพลาดซึ่งจะซ่อนปัญหาที่คุณจะจับได้
Herve พฤ

16

คุณอาจไม่ต้องการทำอย่างนั้น โดยใส่ความชัดเจนsleep()ในตัวจัดการเหตุการณ์ที่คลิกปุ่มของคุณคุณจะต้องล็อค UI ทั้งหมดเป็นครั้งที่สอง อีกทางเลือกหนึ่งคือการใช้การจัดเรียงของเดี่ยวยิงบางจับเวลา สร้างTimerTaskเพื่อเปลี่ยนสีพื้นหลังกลับเป็นสีเริ่มต้นและกำหนดเวลาไว้ในตัวจับเวลา

เป็นไปได้ก็คือการใช้Handler มีบทช่วยสอนเกี่ยวกับคนที่เปลี่ยนจากการใช้ตัวจับเวลาเป็นการใช้ตัวจัดการ

อนึ่งคุณไม่สามารถหยุดกระบวนการชั่วคราวได้ กระบวนการ 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
ทำไมไม่โพสต์ล่าช้า ไม่ต้องใช้ตัวจับเวลา
RichieHH

8

นอกจากนี้ในการตอบนาย Yankowsky postDelayed()ของคุณยังสามารถใช้ นี้สามารถใช้ได้กับทุก ๆView(เช่นบัตรของคุณ) และใช้เวลาRunnableและระยะเวลาที่ล่าช้า มันจะดำเนินการRunnableหลังจากที่ล่าช้า


5

นี่คือตัวอย่างของฉัน

สร้าง Java Utils

    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
ยังมีคำตอบอีกหลายปีที่เพิ่มขึ้นหลังจากคำถามได้รับการแก้ไขอย่างดีแล้วนั่นไม่เกี่ยวข้องกับคำถามเพราะมันไม่ได้จัดการกับความปรารถนาที่ระบุไว้เพื่อให้สามารถใช้งาน UI ได้ คุณไม่สามารถใช้งาน UI ได้ที่นี่เนื่องจากคุณกำลังทำงานบนเธรดใหม่ ไม่ได้อยู่ในเธรด UI
ToolmakerSteve

1
ที่ด้าน UX ของสิ่งต่าง ๆ การแสดงกล่องโต้ตอบบล็อกให้ผู้ใช้โหลดข้อมูลนั้น ... ไม่ดี
2Dee

4

หรือคุณสามารถใช้:

android.os.SystemClock.sleep(checkEvery)

try ... catchซึ่งได้ประโยชน์จากการไม่ต้องตัดที่


0

หากคุณใช้ Kotlin และcoroutinesคุณสามารถทำได้

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

และหากคุณต้องการอัพเดท UI

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.