Android में Looper, Handler और MessageQueue के बीच क्या संबंध है?


95

मैं के लिए आधिकारिक Android दस्तावेज़ / गाइड जाँच कर ली है Looper, Handlerऔर MessageQueue। लेकिन मुझे नहीं मिला। मैं एंड्रॉइड के लिए नया हूं, और इन अवधारणाओं के साथ बहुत उलझन में हूं।

जवाबों:


103

A Looperएक संदेश हैंडलिंग लूप है: यह पढ़ता है और एक से आइटम संसाधित करता है MessageQueueLooperवर्ग आम तौर पर एक साथ संयोजन के रूप में प्रयोग किया जाता है HandlerThread(के एक उपवर्ग Thread)।

A Handlerएक उपयोगिता वर्ग है जो थ्रेड के लिए Looperसंदेश और Runnableऑब्जेक्ट पोस्ट करके एक- दूसरे के साथ बातचीत करने की सुविधा देता है MessageQueue। जब एक Handlerबनाया जाता है, तो यह एक विशिष्ट Looper(और संबद्ध धागा और संदेश कतार) से बंधा होता है ।

विशिष्ट उपयोग में, आप एक बनाते हैं और शुरू करते हैं HandlerThread, फिर एक Handlerऑब्जेक्ट (या ऑब्जेक्ट) बनाते हैं जिसके द्वारा अन्य थ्रेड्स HandlerThreadउदाहरण के साथ इंटरैक्ट कर सकते हैं । Handlerजबकि पर चल रहा है बनाया जाना चाहिए HandlerThread, एक बार बनाया वहाँ क्या धागे का उपयोग कर सकते पर कोई प्रतिबंध नहीं है, हालांकि Handlerके समय निर्धारण के तरीकों ( post(Runnable), आदि)

एंड्रॉइड एप्लिकेशन में मुख्य थ्रेड (उर्फ यूआई थ्रेड) को आपके एप्लिकेशन इंस्टेंस बनने से पहले हैंडलर थ्रेड के रूप में सेट किया जाता है।

क्लास डॉक्स के अलावा, यहाँ इस सब की एक अच्छी चर्चा है

PS उपरोक्त सभी वर्ग पैकेज में हैं android.os


@ टेड हॉप - क्या लूपर की संदेश कतार थ्रेड की संदेश कतार से अलग है?
CopsOnRoad

2
@ जेक - वे एक ही चीज हैं। एंड्रॉइड एपीआईMessageQueue राज्य के लिए बताता है कि एक MessageQueue" निम्न-स्तर की श्रेणी है जो संदेशों की सूची को एक के द्वारा भेजा जाना है Looper "
टेड होप

95

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

Looperवर्ग एक का कहना है MessageQueue है, जो एक सूची है संदेशों । लूपर का एक महत्वपूर्ण चरित्र यह है कि यह उस धागे से जुड़ा हुआ है जिसके भीतर Looperनिर्मित है । यह जुड़ाव हमेशा के लिए रखा जाता है और इसे न तो तोड़ा जा सकता है और न ही बदला जा सकता है। यह भी ध्यान दें कि एक धागा एक से अधिक के साथ संबद्ध नहीं किया जा सकता हैLooper । इस एसोसिएशन की गारंटी देने के लिए, Looperइसे थ्रेड-लोकल स्टोरेज में स्टोर किया जाता है, और इसे सीधे इसके कंस्ट्रक्टर के जरिए नहीं बनाया जा सकता है। इसे बनाने का एकमात्र तरीका स्थैतिक विधि को तैयार करना हैLooper । तैयार विधि पहले थ्रेडलोक की जांच करती हैयह सुनिश्चित करने के लिए कि थ्रेड से पहले से जुड़ा कोई लूपर नहीं है। परीक्षा के बाद, एक नया Looperबनाया जाता है और उसे सहेजा जाता है ThreadLocal। तैयार होने के बाद Looper, हम नए संदेशों की जांच करने और उनसे निपटने के लिए उस पर लूप विधि कह सकते Handlerहैं।

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

यहां छवि विवरण दर्ज करें


5
धन्यवाद! लेकिन क्या बात है अगर हैंडलर पहले संदेश को संदेश कतार में पोस्ट करता है और फिर उसी कतार से संदेश को संभालता है? यह सीधे संदेश को क्यों नहीं संभालता है?
ब्लेक

4
@Blake b / c आप एक थ्रेड (नॉन लूपर थ्रेड) से पोस्ट कर रहे हैं लेकिन दूसरे थ्रेड (लूपर थ्रेड) में
मैसेज को हैंडल कर रहे हैं

Developer.android.com पर जो कुछ भी प्रलेखित किया जा रहा है, उससे बेहतर है - लेकिन आपके द्वारा प्रदान किए गए आरेख के लिए कोड देखना अच्छा होगा।
tfmontague

@numansalati - हैंडलर लूपर थ्रेड से संदेश पोस्ट नहीं कर सकता है?
कोप्सऑनरॉड

78

चलो लूपर के साथ शुरू करते हैं। आप Looper, Handler और MessageQueue के बीच संबंधों को और आसानी से समझ सकते हैं जब आप समझ पाते हैं कि Looper क्या है। इसके अलावा आप बेहतर समझ सकते हैं कि Looper GUI ढांचे के संदर्भ में क्या है। 2 चीजों को करने के लिए लूपर बनाया जाता है।

1) Looper एक सामान्य थ्रेड को रूपांतरित करता है , जो तब समाप्त होता है जब इसकी run()विधि वापस आती है, कुछ में जो एंड्रॉइड ऐप चलने तक लगातार चलता है , जिसे GUI फ्रेमवर्क में आवश्यक है (तकनीकी रूप से, यह तब भी समाप्त होता है जब run()विधि वापस आती है। लेकिन मुझे स्पष्ट करें कि मेरा क्या मतलब है। नीचे)।

2) लूपर एक कतार प्रदान करता है जहां नौकरी करने के लिए गणना की जाती है, जो कि GUI ढांचे में भी आवश्यक है।

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

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

अब, इस सरल सिद्धांत को एंड्रॉइड ऐप पर लागू करते हैं। यदि एंड्रॉइड ऐप सामान्य धागे पर चलाया जाता है तो क्या होगा? एक धागा जिसे "मुख्य" या "यूआई" कहा जाता है या जो भी आवेदन शुरू होता है, और सभी यूआई खींचता है। तो, पहली स्क्रीन उपयोगकर्ताओं के लिए प्रदर्शित की जाती है। तो अब क्या? मुख्य धागा समाप्त हो गया? नहीं, ऐसा नहीं होना चाहिए। यह तब तक इंतजार करना चाहिए जब तक उपयोगकर्ता कुछ न करें, ठीक है? लेकिन हम इस व्यवहार को कैसे प्राप्त कर सकते हैं? खैर, हम Object.wait()या के साथ कोशिश कर सकते हैंThread.sleep()। उदाहरण के लिए, मुख्य धागा पहली स्क्रीन, और नींद प्रदर्शित करने के लिए अपना प्रारंभिक काम पूरा करता है। यह जागता है, जिसका अर्थ है बाधित, जब एक नया काम करना है। अब तक बहुत अच्छा है, लेकिन इस समय हमें कई नौकरियों को रखने के लिए एक कतार जैसी डेटा संरचना की आवश्यकता है। उस मामले के बारे में सोचें जब कोई उपयोगकर्ता क्रमिक रूप से स्क्रीन को छूता है, और एक कार्य को समाप्त होने में अधिक समय लगता है। इसलिए, हमें पहले-पहले-आउट तरीके से कार्य करने के लिए डेटा संरचना रखने की आवश्यकता है। इसके अलावा, आप कल्पना कर सकते हैं कि कभी-कभी चलने-फिरने-और-प्रक्रिया-नौकरी-के-आने-जाने वाले धागे को बाधित करने के लिए लागू करना आसान नहीं है, और जटिल और अक्सर अस्वीकार्य कोड की ओर जाता है। हम इस तरह के उद्देश्य के लिए एक नया तंत्र बनाना चाहते हैं, और यही लूपर के बारे में हैलूपर वर्ग का आधिकारिक दस्तावेजकहते हैं, "डिफ़ॉल्ट रूप से थ्रेड्स में उनके साथ एक संदेश लूप नहीं होता है", और लूपर एक वर्ग है "जिसका उपयोग थ्रेड के लिए एक संदेश लूप चलाने के लिए किया जाता है"। अब आप समझ सकते हैं कि इसका क्या मतलब है।

चलो हैंडलर और मैसेजक्यू पर जाएं। सबसे पहले, MessageQueue वह कतार है जिसका मैंने ऊपर उल्लेख किया था। यह लूपर के अंदर रहता है, और यही है। आप इसे लूपर वर्ग के स्रोत कोड के साथ देख सकते हैं । लूपर वर्ग में MessageQueue का एक सदस्य चर है।

फिर, हैंडलर क्या है? यदि एक कतार है, तो एक ऐसी विधि होनी चाहिए जो हमें एक नया कार्य कतार में लगाने के लिए सक्षम करे, है ना? यही हैंडलर करता है। हम विभिन्न post(Runnable r)तरीकों का उपयोग करके एक नए कार्य को एक कतार (मैसेजक्यू) में शामिल कर सकते हैं । बस। यह सब लूपर, हैंडलर और मैसेजक्यू के बारे में है।

मेरा अंतिम शब्द है, इसलिए मूल रूप से Looper एक वर्ग है जो GUI ढांचे में होने वाली समस्या को हल करने के लिए बनाया गया है। लेकिन इस तरह की जरूरतें अन्य स्थितियों में भी हो सकती हैं। वास्तव में यह मल्टी थ्रेड्स एप्लिकेशन के लिए एक बहुत प्रसिद्ध पैटर्न है, और डॉग ली (विशेषकर, अध्याय 4.1.4 "वर्कर थ्रेड्स" उपयोगी होगा) द्वारा "जावा में समवर्ती प्रोग्रामिंग" में आप इसके बारे में अधिक जान सकते हैं। इसके अलावा, आप कल्पना कर सकते हैं कि इस तरह का तंत्र एंड्रॉइड फ्रेमवर्क में अद्वितीय नहीं है, लेकिन सभी जीयूआई ढांचे को इसके समान कुछ की आवश्यकता हो सकती है। आप जावा स्विंग ढांचे में लगभग एक ही तंत्र पा सकते हैं।


4
सबसे बढ़िया उत्तर। इस विस्तृत विवरण से और अधिक जानें। मुझे आश्चर्य है कि अगर कुछ ब्लॉग पोस्ट है जो अधिक विस्तार से जाती है।
21.40 बजे capt.swag

क्या संदेश हैंडलर का उपयोग किए बिना संदेश संदेश में जोड़ा जा सकता है?
CopsOnRoad

@CopsOnRoad नहीं उन्हें सीधे नहीं जोड़ा जा सकता है।
फैसल नसीर

मेरा दिन बना दिया ... आप को बहुत सारा प्यार :)
राहुल मट्ट

26

MessageQueue: यह निम्न स्तर का वर्ग है जो संदेशों की सूची को एक के द्वारा प्रेषित करता है Looper। संदेश सीधे नहीं जोड़े जाते हैं MessageQueue, बल्कि Handlerवस्तुओं से जुड़े होते हैं Looper। [ ]

Looper: यह उन पर लूप करता है MessageQueueजिनमें संदेश भेजे जाने हैं। कतार को प्रबंधित करने का वास्तविक कार्य उसके द्वारा किया जाता है Handlerजो संदेश कतार में संदेशों को संभालने (जोड़ने, हटाने, भेजने) के लिए जिम्मेदार है। [ ]

Handler: यह आप भेजने के लिए और प्रक्रिया की अनुमति देता है Messageऔर Runnableएक धागे के साथ जुड़े वस्तुओं MessageQueue। प्रत्येक हैंडलर का उदाहरण एक धागे से जुड़ा होता है और उस धागे की संदेश कतार होती है। [ ]

जब आप एक नया बनाते हैं Handler, तो यह उस थ्रेड के थ्रेड / मैसेज कतार के लिए बाध्य होता है, जो इसे बना रहा है - उस बिंदु से, यह मैसेज और रनवेबल्स को उस संदेश कतार में वितरित करेगा और उन्हें निष्पादित करेगा क्योंकि वे मैसेज कतार से बाहर आते हैं

बेहतर समझ के लिए कृपया नीचे दी गई छवि [ 2 ] पर जाएं।

यहां छवि विवरण दर्ज करें


0

एक उदाहरण के साथ, @K_Anas द्वारा उत्तर का विस्तार, जैसा कि यह कहा गया है

यह व्यापक रूप से ज्ञात है कि एंड्रॉइड में मुख्य धागे के अलावा थ्रेड्स से सीधे यूआई घटकों को अपडेट करना अवैध है।

उदाहरण के लिए यदि आप थ्रेड का उपयोग करके यूआई को अपडेट करने का प्रयास करते हैं।

    int count = 0;
    new Thread(new Runnable(){
        @Override
        public void run() {
            try {
                while(true) {
                    sleep(1000);
                    count++;
                    textView.setText(String.valueOf(count));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


   ).start();

आपका ऐप अपवाद के साथ क्रैश हो जाएगा।

android.view.ViewRoot $ CalledFromWrongThreadException: केवल मूल धागा जिसने दृश्य पदानुक्रम बनाया है, वह अपने विचारों को छू सकता है।

दूसरे शब्दों में आप उपयोग करने की आवश्यकता Handlerहै जो करने के लिए संदर्भ रहता है MainLooper यानी Main Threadया UI Threadऔर के रूप में कार्य पारित Runnable

  Handler handler = new Handler(getApplicationContext().getMainLooper);
        int count = 0;
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    while(true) {
                        sleep(1000);
                        count++;
                        handler.post(new Runnable() {
                           @Override
                           public void run() {
                                 textView.setText(String.valueOf(count));
                           }
                         });

                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

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