जवाबों:
A Looper
एक संदेश हैंडलिंग लूप है: यह पढ़ता है और एक से आइटम संसाधित करता है MessageQueue
। Looper
वर्ग आम तौर पर एक साथ संयोजन के रूप में प्रयोग किया जाता है HandlerThread
(के एक उपवर्ग Thread
)।
A Handler
एक उपयोगिता वर्ग है जो थ्रेड के लिए Looper
संदेश और Runnable
ऑब्जेक्ट पोस्ट करके एक- दूसरे के साथ बातचीत करने की सुविधा देता है MessageQueue
। जब एक Handler
बनाया जाता है, तो यह एक विशिष्ट Looper
(और संबद्ध धागा और संदेश कतार) से बंधा होता है ।
विशिष्ट उपयोग में, आप एक बनाते हैं और शुरू करते हैं HandlerThread
, फिर एक Handler
ऑब्जेक्ट (या ऑब्जेक्ट) बनाते हैं जिसके द्वारा अन्य थ्रेड्स HandlerThread
उदाहरण के साथ इंटरैक्ट कर सकते हैं । Handler
जबकि पर चल रहा है बनाया जाना चाहिए HandlerThread
, एक बार बनाया वहाँ क्या धागे का उपयोग कर सकते पर कोई प्रतिबंध नहीं है, हालांकि Handler
के समय निर्धारण के तरीकों ( post(Runnable)
, आदि)
एंड्रॉइड एप्लिकेशन में मुख्य थ्रेड (उर्फ यूआई थ्रेड) को आपके एप्लिकेशन इंस्टेंस बनने से पहले हैंडलर थ्रेड के रूप में सेट किया जाता है।
क्लास डॉक्स के अलावा, यहाँ इस सब की एक अच्छी चर्चा है ।
PS उपरोक्त सभी वर्ग पैकेज में हैं android.os
।
MessageQueue
राज्य के लिए बताता है कि एक MessageQueue
" निम्न-स्तर की श्रेणी है जो संदेशों की सूची को एक के द्वारा भेजा जाना है Looper
। "
यह व्यापक रूप से ज्ञात है कि एंड्रॉइड में मुख्य धागे के अलावा थ्रेड्स से सीधे यूआई घटकों को अपडेट करना अवैध है । यह एंड्रॉइड डॉक्यूमेंट ( 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
नीचे दिखाए गए हैं:
चलो लूपर के साथ शुरू करते हैं। आप 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 "वर्कर थ्रेड्स" उपयोगी होगा) द्वारा "जावा में समवर्ती प्रोग्रामिंग" में आप इसके बारे में अधिक जान सकते हैं। इसके अलावा, आप कल्पना कर सकते हैं कि इस तरह का तंत्र एंड्रॉइड फ्रेमवर्क में अद्वितीय नहीं है, लेकिन सभी जीयूआई ढांचे को इसके समान कुछ की आवश्यकता हो सकती है। आप जावा स्विंग ढांचे में लगभग एक ही तंत्र पा सकते हैं।
MessageQueue
: यह निम्न स्तर का वर्ग है जो संदेशों की सूची को एक के द्वारा प्रेषित करता है Looper
। संदेश सीधे नहीं जोड़े जाते हैं MessageQueue
, बल्कि Handler
वस्तुओं से जुड़े होते हैं Looper
। [ ३ ]
Looper
: यह उन पर लूप करता है MessageQueue
जिनमें संदेश भेजे जाने हैं। कतार को प्रबंधित करने का वास्तविक कार्य उसके द्वारा किया जाता है Handler
जो संदेश कतार में संदेशों को संभालने (जोड़ने, हटाने, भेजने) के लिए जिम्मेदार है। [ २ ]
Handler
: यह आप भेजने के लिए और प्रक्रिया की अनुमति देता है Message
और Runnable
एक धागे के साथ जुड़े वस्तुओं MessageQueue
। प्रत्येक हैंडलर का उदाहरण एक धागे से जुड़ा होता है और उस धागे की संदेश कतार होती है। [ ४ ]
जब आप एक नया बनाते हैं Handler
, तो यह उस थ्रेड के थ्रेड / मैसेज कतार के लिए बाध्य होता है, जो इसे बना रहा है - उस बिंदु से, यह मैसेज और रनवेबल्स को उस संदेश कतार में वितरित करेगा और उन्हें निष्पादित करेगा क्योंकि वे मैसेज कतार से बाहर आते हैं ।
बेहतर समझ के लिए कृपया नीचे दी गई छवि [ 2 ] पर जाएं।
एक उदाहरण के साथ, @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() ;