android - हैंडलर, MessageQueue, लूपर, क्या वे सभी यूआई थ्रेड पर चलते हैं?




multithreading android-handler (3)

मैं अपने सिर को थ्रेडिंग के चारों ओर लपेटने की कोशिश कर रहा हूं, और मुझे पता है कि मैसेज्यूयू में संदेश / रननेबल्स पोस्ट करने के लिए मैं Handler का उपयोग कर सकता हूं, जो बदले में Looper द्वारा उठाया जाता है और प्रसंस्करण के लिए हैंडलर को वापस भेज दिया जाता है।

अगर मैं अपनी गतिविधि में हैंडलर पर पोस्ट करता हूं, तो क्या Activity , Handler , MessageQueue और Looper सभी यूआई थ्रेड पर चल रहे हैं? यदि नहीं, तो क्या कोई यह समझा सकता है कि यह सब एक साथ कैसे आता है? :)


अगर मैं अपनी गतिविधि में हैंडलर पर पोस्ट करता हूं, तो क्या गतिविधि, हैंडलर, संदेशक्यूयू और लूपर सभी यूआई थ्रेड पर चल रहे हैं? यदि नहीं, तो क्या कोई यह समझा सकता है कि यह सब एक साथ कैसे आता है? :)

यह इस बात पर निर्भर करता है कि आप Handler कैसे बनाते हैं

मामला एक:

Handler()

डिफ़ॉल्ट कन्स्ट्रक्टर वर्तमान हैंडल के लिए Looper साथ इस हैंडलर को Looper

यदि आप यूआई थ्रेड में इस तरह Handler बनाते हैं, Handler यूआई थ्रेड के Looper से जुड़ा हुआ है। यूआई थ्रेड के साथ Looper साथ MessageQueue भी जुड़ा हुआ है।

प्रकरण 2:

Handler (Looper looper)

डिफ़ॉल्ट के बजाय प्रदत्त लूपर का उपयोग करें।

यदि मैं हैंडलर थ्रेड बनाता हूं और हैंडलर थ्रेड को हैंडलर, हैंडलर और लूपर के लूपर को हैंडलर थ्रेड से जोड़ता हूं और यूआई थ्रेड नहीं करता हूं। Handler , HandlerThread और Looper Handler HandlerThread जुड़े हुए हैं।

केस का प्रयोग करें: आप नेटवर्क या आईओ ऑपरेशन निष्पादित करना चाहते हैं। आप इसे यूआई थ्रेड पर निष्पादित नहीं कर सकते हैं और इसलिए HandlerThread थ्रेड आपके लिए आसान है।

 HandlerThread handlerThread = new HandlerThread("NetworkOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());

यदि आप हैंडलर थ्रेड से यूआई थ्रेड तक डेटा वापस पास करना चाहते हैं, तो आप यूआई थ्रेड से Looper साथ एक और हैंडलर (प्रतिक्रिया हैंडलर कहें) और कॉल sendMessage । यूआई थ्रेड responseHandler handleMessage ओवरराइड करना चाहिए

अधिक जानकारी के लिए इन पदों का संदर्भ लें।

लूपर का उद्देश्य क्या है और इसका उपयोग कैसे करें? (अवधारणाओं के लिए)

एंड्रॉइड: थ्रेड में टोस्ट (उदाहरण के लिए इन सभी अवधारणाओं को जोड़कर कोड)


प्रश्न के भाग "यह सब कैसे एक साथ आता है" के बाद। उपयोगकर्ता 634618 के रूप में लिखा गया है, लूपर एक थ्रेड लेता है, एप्लिकेशन के मुख्य Looper के मामले में मुख्य यूआई थ्रेड।

  • Looper.loop() संदेशों को अपने संदेश कतार से बाहर खींचता है। प्रत्येक संदेश में एक संबंधित हैंडलर का संदर्भ होता है जिसे इसे (लक्षित सदस्य) को दिया जाना है।
  • कतार से प्राप्त प्रत्येक संदेश के लिए Looper.loop() अंदर:
    • loop() संदेश को अपने लक्षित सदस्य के रूप में संग्रहीत हैंडलर का उपयोग करके public void Handler.dispatchMessage(Message msg) loop() कॉल public void Handler.dispatchMessage(Message msg)
    • अगर संदेश में एक रननेबल कॉलबैक सदस्य है, तो वह चलाया जाता है।
    • अन्यथा, यदि हैंडलर के पास साझा कॉलबैक सेट है, तो वह चलाया जाता है।
    • अन्यथा, हैंडलर handleMessage() को संदेश के साथ तर्क के रूप में बुलाया जाता है। (ध्यान दें, यदि आप हैंडलर को AsyncTask के रूप में उपclass करते हैं, तो आप handleMessage() को ओवरराइड कर सकते हैं।)

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

जब हम किसी अन्य थ्रेड पर कुछ कोड चला रहे हैं और UI थ्रेड पर निष्पादित करने के लिए एक रननेबल भेजना चाहते हैं, तो हम इसे इस तरह से कर सकते हैं:

// h is a Handler that we constructed on the UI thread.
public void run_on_ui_thread(final Handler h, final Runnable r)
{
   // Associate a Message with our Handler and set the Message's
   // callback member to our Runnable:
   final Message message = Message.obtain(h, r);

   // The target is the Handler, so this asks our Handler to put
   // the Message in its message queue, which is the exact same
   // message queue associated with the Looper on the thread on
   // which the Handler was created:
   message.sendToTarget();
}

संक्षिप्त उत्तर: वे सभी एक ही धागे पर चलते हैं। यदि किसी Activity लाइफसाइक्ल कॉलबैक से तत्काल किया जाता है, तो वे सभी मुख्य UI थ्रेड पर चलते हैं।

लंबा जवाब:

एक थ्रेड में Looper हो सकता है, जिसमें एक MessageQueue होता है। इस सुविधा का उपयोग करने के लिए, आपको वर्तमान थ्रेड पर Looper (स्थैतिक) Looper.prepare() को कॉल करके Looper बनाना होगा, और उसके बाद लूप को (स्थिर भी) Looper.loop() कॉल करके प्रारंभ करना होगा। ये स्थैतिक हैं क्योंकि केवल प्रति थ्रेड एक Looper होना चाहिए।

loop() पर कॉल आमतौर पर कुछ समय के लिए वापस नहीं आता है , लेकिन संदेश क्यूई से संदेश ("कार्य", "आदेश" या जिन्हें आप उन्हें कॉल करना चाहते हैं) लेते रहते हैं और उन्हें व्यक्तिगत रूप से संभालते हैं (उदाहरण के लिए एक Runnable वापस बुलाकर संदेश में)। जब कतार में कोई संदेश नहीं छोड़ा जाता है, तब तक थ्रेड ब्लॉक होते हैं जब तक कि नए संदेश न हों। Looper को रोकने के लिए, आपको उस पर quit() को कॉल करना होगा (जो संभवतः लूप को तुरंत नहीं रोकता है, बल्कि लूप से समय-समय पर चेक किया गया एक निजी ध्वज सेट करता है, इसे रोकने के लिए संकेत देता है)।

हालांकि, आप सीधे कतार में संदेश नहीं जोड़ सकते हैं। इसके बजाय, आप एक queueIdle() कॉलबैक के लिए प्रतीक्षा करने के लिए MessageQueue.IdleHandler पंजीकृत करते हैं, जिसमें आप यह तय कर सकते हैं कि आप कुछ करना चाहते हैं या नहीं। सभी हैंडलर बदले में बुलाए जाते हैं। (इसलिए "कतार" वास्तव में एक कतार नहीं है, बल्कि इसके बजाय कॉलबैक का संग्रह नियमित रूप से कहा जाता है ।)

पिछले अनुच्छेद के बारे में नोट: यह वास्तव में अनुमान लगाया गया है। मुझे उस पर कोई दस्तावेज नहीं मिला, लेकिन यह समझ में आता है।

अपडेट करें: एहकोक्स की टिप्पणी और उसका जवाब देखें

क्योंकि यह बहुत काम है, ढांचा चीजों को सरल बनाने के लिए Handler क्लास प्रदान करता है। जब आप एक Handler उदाहरण बनाते हैं, तो यह डिफ़ॉल्ट रूप से (वर्तमान में) Looper से जुड़ा हुआ है जो पहले से ही मौजूदा थ्रेड से जुड़ा हुआ है। ( Handler जानता है कि Looper जुड़ा हुआ है क्योंकि हमने पहले prepare() बुलाया था, जो शायद ThreadLocal में Looper संदर्भ को संग्रहीत करता था।)

एक Handler साथ , आप केवल post() को "थ्रेड के संदेश कतार में संदेश डालने" के लिए कॉल कर सकते हैं (इसलिए बोलने के लिए)। Handler सभी IdleHandler कॉलबैक सामग्री का ख्याल IdleHandler और सुनिश्चित करेगा कि आपके पोस्ट Runnable को निष्पादित किया गया है। (यदि आप देरी के साथ पोस्ट करते हैं तो यह भी जांच सकता है कि समय सही है या नहीं।)

बस स्पष्ट होने के लिए: वास्तव में लूपिंग थ्रेड बनाने का एकमात्र तरीका कुछ लूप को संदेश पोस्ट करना है। यह तब तक मान्य है जब तक आप लूपर पर छोड़ें () को कॉल नहीं करते हैं।

एंड्रॉइड यूआई थ्रेड के बारे में: किसी बिंदु पर (शायद किसी भी गतिविधि से पहले और जैसे ही बनाए जाते हैं) ढांचे ने एक Looper स्थापित किया है (एक MessageQueue युक्त) और इसे शुरू किया। इस बिंदु से, यूआई थ्रेड पर जो कुछ भी होता है वह उस लूप के माध्यम से होता है। इसमें गतिविधि जीवन चक्र प्रबंधन आदि शामिल हैं। आपके द्वारा ओवरराइड किए जाने वाले सभी कॉलबैक ( onCreate() , onDestroy() ...) उस लूप से कम से कम अप्रत्यक्ष प्रेषित हैं। आप देख सकते हैं कि उदाहरण के लिए अपवाद के स्टैक ट्रेस में। (आप इसे आजमा सकते हैं, बस int a = 1 / 0; onCreate() int a = 1 / 0; कहीं भी onCreate() ...)

मुझे लगता है कि इसका मतलब बनता है। पहले अस्पष्ट होने के लिए खेद है।





android-looper