परिभाषित अंतराल पर एंड्रॉइड में एक रनवेबल थ्रेड कैसे चलाएं?


351

मैंने एंड्रॉइड एमुलेटर स्क्रीन में परिभाषित अंतराल पर कुछ पाठ प्रदर्शित करने के लिए एक एप्लिकेशन विकसित किया। मैं Handlerकक्षा का उपयोग कर रहा हूं । यहाँ मेरे कोड से एक स्निपेट है:

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

जब मैं इस एप्लिकेशन को चलाता हूं तो पाठ केवल एक बार प्रदर्शित होता है। क्यों?


109
मैं कभी भी याद नहीं रख सकता कि कैसे एक रन करने योग्य है, इसलिए मैं हमेशा आपके पोस्ट पर यह कैसे करना है :) :))
एड्रियन सिसरू

2
वही यहाँ दोस्त इतना सच है
NoXSaeeD

1
लैम्ब्डा अब ज्यादातर समय जाने का रास्ता है;)
ज़ेरुस

@ एड्रियनसिकारू: वही
सोवंडारा लेंग

जवाबों:


535

अपने उदाहरण के लिए सरल तय है:

handler = new Handler();

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

handler.postDelayed(r, 1000);

या हम सामान्य धागे का उपयोग कर सकते हैं (मूल धावक के साथ):

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

thread.start();

आप अपने रन करने योग्य ऑब्जेक्ट को केवल एक कमांड के रूप में मान सकते हैं जिसे निष्पादन के लिए संदेश कतार में भेजा जा सकता है, और हैंडलर के रूप में सिर्फ एक सहायक ऑब्जेक्ट उस कमांड को भेजने के लिए उपयोग किया जाता है।

अधिक विवरण यहाँ हैं http://developer.android.com/reference/android/os/Handler.html


एलेक्स, मुझे एक छोटी सी शंका है। अब धागा पूरी तरह से चल रहा है और लगातार पाठ प्रदर्शित कर रहा है, अगर मैं इसे रोकना चाहता हूं तो इसका मतलब है कि मुझे क्या करना है? कृपया मेरी मदद करें।
राजपंडियन

11
आप बूलियन वैरिएबल _stop को परिभाषित कर सकते हैं, और जब आप रोकना चाहते हैं तो इसे 'सही' सेट करें। और 'जबकि (सच्चा)' को 'जबकि (! _ रोक)' में बदल दें, या यदि पहला नमूना इस्तेमाल किया है, तो बस '' (!! बंद) हैंडलर.पोस्टडीलैड (यह, 1000) में बदल दें।
अलेक्सा 2k8

यदि मैं संदेश को पुनः आरंभ करना चाहता हूं तो क्या होगा?
सोनहजा

और अगर मुझे एक के बाद एक दिखाई देने वाले 8 अलग-अलग ImageView सेट करने के लिए एक रनवेबल की आवश्यकता है, तो उन सभी को उसी तरह से अदृश्य करें और इसी तरह ("ब्लिंकिंग" एनीमेशन बनाने के लिए), मैं यह कैसे कर सकता हूं?
Droidman 13

1
यदि आप यह सुनिश्चित करना चाहते हैं कि हैंडलर मुख्य धागे से जुड़ा होगा, तो आपको इसे इस तरह से शुरू करना चाहिए: हैंडलर = नया हैंडलर (Looper.getMainLooper ());
यैर कुकिल्का

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

2
यदि आप यह सुनिश्चित करना चाहते हैं कि हैंडलर मुख्य धागे से जुड़ा होगा, तो आपको इसे इस तरह से शुरू करना चाहिए: नया हैंडलर (Looper.getMainLooper ());
यैर कुकिल्का

1
क्या यह समाधान मूल पद के समतुल्य नहीं है? यह केवल 100 मिलीसेकंड के बाद एक बार Runnable चलाएगा।
ट्रॉनमैन

@YairKukielka उत्तर समाधान है! आपको MainLooper अटैच करना होगा। ऐसा जीवन रक्षक!
हाउससेम चेलीगू

40

मुझे लगता है कि प्रत्येक सेकंड सही अपडेट के लिए एलेक्स 2k8 के पहले समाधान में सुधार कर सकते हैं

1. मूल कोड:

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

2.Analysis

  • ऊपर की लागत में, tv.append("Hello Word")लागत टी मिलीसेकंड मान लें , प्रदर्शन के बाद 500 बार विलंबित समय 500 * टी मिलीसेकंड है
  • लंबे समय तक चलने पर यह देरी से बढ़ेगा

3. समाधान

देरी से बचने के लिए, PostDelayed () के बस परिवर्तन क्रम से बचने के लिए:

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

6
-1 आप रन में किए गए कार्य को मान रहे हैं () प्रत्येक रन के लिए एक स्थिर राशि है, अगर यह डायनेमिक डेटा (जो आमतौर पर यह है) पर संचालन होता था, तो आप एक से अधिक रन () के साथ समाप्त हो जाएंगे एक बार। यही कारण है कि पोस्टडेलिड को आम तौर पर अंत में रखा जाता है।
जय

1
@ जय दुर्भाग्य से आप गलत हैं। एक हैंडलर एक एकल थ्रेड (और एक लूपर जो कि थ्रेड की रन विधि है) + एक मैसेजक्यू के साथ जुड़ा हुआ है। हर बार जब आप कोई संदेश पोस्ट करते हैं, तो आप उसे एनक्यू करते हैं और अगली बार जब लूपर उस कतार की जांच करता है, जो आपके द्वारा पोस्ट किए गए रननेबल के रन विधि को निष्पादित करता है। चूँकि यह सब केवल एक थ्रेड में होता है, आप एक ही समय में 1 से अधिक निष्पादित नहीं कर सकते हैं। इसके अलावा पोस्टडायलेड करने से पहले आपको प्रति निष्पादन 1000ms के करीब मिलेगा क्योंकि आंतरिक रूप से यह निष्पादन समय के रूप में वर्तमान समय + 1000 का उपयोग करता है। यदि आप पोस्ट से पहले कोड डालते हैं तो आप अतिरिक्त देरी करते हैं।
zapl

1
हैंडलर के बारे में टिप के लिए @zapl धन्यवाद, मुझे लगा कि यह कई रनवे और इसलिए, कई थ्रेड्स निष्पादित करेगा। आंतरिक रूप से, हालांकि, एक शर्त जैसे कि ((वर्तमान समय - अंतिम समय)> 1000) ठीक काम करेगा जब रन अवधि 1000ms से कम या इसके बराबर हो, हालांकि, जब यह पार हो जाता है, तो निश्चित रूप से टाइमर गैर रेखीय अंतराल के कारण उत्पन्न होने वाला है रन विधि के निष्पादन समय पर पूरी तरह से निर्भर (इसलिए अप्रत्याशित कम्प्यूटेशनल व्यय पर मेरी बात)
जे

यदि आप एक निश्चित अवधि, डब्ल्यू / आउट संघर्ष चाहते हैं, तो काम करने से पहले प्रारंभ समय को मापें, और तदनुसार अपनी देरी को समायोजित करें। यदि सीपीयू व्यस्त है, तो आप अभी भी थोड़ा विलंबता देखेंगे, लेकिन यह आपको एक सख्त अवधि की अनुमति दे सकता है, और यह पता लगाने के लिए कि क्या सिस्टम अतिभारित है (शायद वापस जाने के लिए कम प्राथमिकता वाले सामान को इंगित करने के लिए)।
अजाक्स

27

दोहराए जाने वाले कार्य के लिए आप उपयोग कर सकते हैं

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

इसे कॉल करें

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

            }
        },500,1000);

उपरोक्त कोड पहली बार आधे सेकंड (500) के बाद चलेगा और प्रत्येक सेकंड (1000) के बाद खुद को दोहराएगा

कहाँ पे

कार्य निष्पादित होने की विधि है

प्रारंभिक निष्पादन के समय के बाद

( निष्पादन को दोहराने के लिए समय अंतराल )

दूसरे

और यदि आप किसी टास्क संख्या को निष्पादित करना चाहते हैं, तो आप CountDownTimer का उपयोग कर सकते हैं ।

    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

और आप इसे रननेबल के साथ भी कर सकते हैं। एक रन करने योग्य विधि बनाएं

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

        }
    };

और इन दोनों तरीकों से इसे कॉल करें

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

या

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

विकल्प # 3 के लिए मैं कैसे रोक / फिर से शुरू कर सकता हूं और स्थायी रूप से बंद भी कर सकता हूं?
Si8

हैंडलर जैसे हैंडलर का एक उदाहरण बनाएं = नया हैंडलर () और इसे हैंडलर की तरह निकालें ।removeCallbacksAndMessages (नल);
ज़ार ई अहमर

24

मैं इस विशिष्ट मामले के लिए विश्वास करता हूं, यानी एक निश्चित अंतराल के साथ कुछ चलाने के लिए, Timerअधिक उपयुक्त है। ये रहा एक सरल उदाहरण:

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

उपयोग करने Timerके कुछ फायदे हैं:

  • प्रारंभिक देरी और अंतराल को आसानी से scheduleफ़ंक्शन तर्कों में निर्दिष्ट किया जा सकता है
  • टाइमर को केवल कॉल करके रोका जा सकता है myTimer.cancel()
  • यदि आप केवल एक धागा चलाना चाहते हैं, तो नया शेड्यूल करने myTimer.cancel() से पहले कॉल करना याद रखें (यदि myTimer शून्य नहीं है)

7
मैं इस बात से इत्तफाक नहीं रखता कि एक टाइमर अधिक उपयुक्त है क्योंकि यह एंड्रॉइड जीवन चक्र पर विचार नहीं करता है। जब आप विराम देते हैं और फिर से शुरू करते हैं, तो कोई ग्वारेंटी नहीं है कि टाइमर सही ढंग से चलेगा। मेरा तर्क है कि एक बेहतर विकल्प एक बेहतर विकल्प है।
जनपद

1
क्या इसका मतलब यह है कि जब एक ऐप को पृष्ठभूमि में रखा जाता है तो एक हैंडलर को रोक दिया जाएगा? और जब यह ध्यान केंद्रित करता है तो यह (कम या ज्यादा) जारी रहेगा जैसे कि कुछ भी नहीं हुआ था?
एंड्रयू गैलास्च

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

5
यह एक त्रुटि देना चाहिए। आपकी दूसरी पंक्ति में आप चर कह रहे हैं rजो अभी तक परिभाषित है।
सेउल

यदि आप यह सुनिश्चित करना चाहते हैं कि हैंडलर मुख्य धागे से जुड़ा होगा, तो आपको इसे इस तरह से शुरू करना चाहिए: हैंडलर = नया हैंडलर (Looper.getMainLooper ());
यैर कुकिल्का

बस दोहराव जवाब!
हामिद

मैं इमेजव्यू क्लिक के साथ रननेबल को कैसे रोक / फिर से शुरू कर सकता हूं?
Si8

4

अगर मैं सही ढंग से Handler.post () विधि के प्रलेखन को समझता हूं:

संदेश पंक्ति में Runnable r जोड़ने का कारण बनता है। रननेबल को उस थ्रेड पर चलाया जाएगा, जिससे यह हैंडलर जुड़ा हुआ है।

तो @ alex2k8 द्वारा प्रदान किए गए उदाहरण, भले ही सही ढंग से काम कर रहे हों, समान नहीं हैं। मामले में, जहां Handler.post()उपयोग किया जाता है, कोई नया धागा नहीं बनाया जाता है । आप केवल EDT द्वारा निष्पादित होने Runnableवाले थ्रेड पर पोस्ट करते हैं । उसके बाद, ईडीटी केवल निष्पादित करता है , और कुछ नहीं।HandlerRunnable.run()

याद रखें: Runnable != Thread


1
सच है। हर बार एक नया धागा न बनाएं। हैंडलर और अन्य निष्पादन पूलों के पूरे बिंदु में एक या दो थ्रेड्स हैं जो एक कतार से कार्य खींचते हैं, थ्रेड निर्माण और जीसी से बचने के लिए। यदि आपके पास वास्तव में टपका हुआ ऐप है, तो अतिरिक्त GC आउटऑफमैमोरी स्थितियों को कवर करने में मदद कर सकता है, लेकिन दोनों मामलों में बेहतर समाधान यह है कि आप जरूरत से ज्यादा काम करने से बचें।
अजाक्स

अलेक्सा 2k8 के उत्तर के आधार पर सामान्य थ्रेड का उपयोग करके ऐसा करने का बेहतर तरीका है?
कॉम्पैक 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)
}

जावा

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

एक दिलचस्प उदाहरण यह है कि आप लगातार एक काउंटर / स्टॉप-वॉच को अलग-अलग थ्रेड में देख सकते हैं। जीपीएस-लोकेशन भी दिखा रहा है। जबकि मुख्य गतिविधि उपयोगकर्ता इंटरफ़ेस थ्रेड पहले से ही है।

अंश:

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

कोड देखने के लिए यहां देखें:

मुख्य गतिविधि के उपयोगकर्ता इंटरफ़ेस थ्रेड के साथ जीपीएस स्थान और वर्तमान समय में प्रदर्शित थ्रेड उदाहरण


1
संकेत: यदि आप अपने उत्तर को उपयोगी बनाना चाहते हैं - यहाँ इनपुट प्रारूपित करना सीखें। वह पूर्वावलोकन विंडो एक कारण से मौजूद है।
भूतकाल

0

अब कोटलिन में आप इस तरह से धागे चला सकते हैं:

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

कोटलीन के साथ कोटलिन

कोटलिन में, आप निम्नलिखित कार्य कर सकते हैं:

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

इसे यहाँ आज़माएं !

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.