Ben resmi Android dokümantasyon / rehber kontrol ettikten Looper
, Handler
ve MessageQueue
. Ama anlayamadım. Android'de yeniyim ve bu kavramlarla kafam çok karıştı.
Yanıtlar:
A Looper
, bir mesaj işleme döngüsüdür: bir MessageQueue
. Looper
Sınıf genellikle ile bağlantılı olarak kullanılır HandlerThread
(bir alt sınıfı Thread
).
A Handler
, a ile etkileşimi kolaylaştıran bir yardımcı sınıftır - esas olarak iş parçacığına Looper
mesajlar ve Runnable
nesneler göndererek MessageQueue
. Bir Handler
oluşturulduğunda, belirli bir Looper
(ve ilişkili iş parçacığı ve mesaj kuyruğu) bağlanır .
Tipik kullanımda, HandlerThread
bir Handler
nesneyi yaratır ve başlatırsınız , ardından diğer iş parçacıklarının HandlerThread
örnekle etkileşime girebileceği bir nesne (veya nesneler) oluşturursunuz . Bir kez oluşturulduktan sonra hangi iş parçacığının zamanlama yöntemlerini ( vb.) Kullanabileceği konusunda herhangi bir kısıtlama olmamasına rağmen, Handler
üzerinde çalıştırılırken HandlerThread
oluşturulmalıdır .Handler
post(Runnable)
Bir Android uygulamasındaki ana iş parçacığı (UI iş parçacığı olarak da bilinir), uygulama örneğiniz oluşturulmadan önce bir işleyici iş parçacığı olarak ayarlanır.
Sınıf dokümanlarının yanı sıra, burada tüm bunların güzel bir tartışması var .
Not: Yukarıda belirtilen tüm sınıflar paketin içindedir android.os
.
Android'deki ana iş parçacığı dışındaki iş parçacıklarından doğrudan UI bileşenlerini güncellemenin yasa dışı olduğu yaygın olarak bilinmektedir . Bu android belgesi ( UI İş Parçacığında Pahalı İşlemleri İşleme ), bazı pahalı işler yapmak ve bittikten sonra kullanıcı arabirimini güncellemek için ayrı bir iş parçacığı başlatmamız gerekirse izlenecek adımları önerir . Buradaki fikir, ana iş parçacığı ile ilişkilendirilmiş bir İşleyici nesnesi oluşturmak ve uygun zamanda ona bir Çalıştırılabilir öğe göndermektir. Bu , ana iş parçacığında çağrılacaktır . Bu mekanizma, Looper ve Handler sınıflarıyla gerçekleştirilir.Runnable
Looper
Sınıf bir tutar MessageQueue bir liste içerir mesajlar . Looper'ın önemli bir karakteri, içinde oluşturulduğu iş parçacığı ile ilişkili olmasıdır . Bu ilişki sonsuza kadar korunur ve bozulamaz veya değiştirilemez. Ayrıca iplik dikkat aşkın ile ilişkili olamaz biri . Bu ilişkiyi garanti etmek için , iş parçacığı yerel depolamada saklanır ve doğrudan kurucusu aracılığıyla oluşturulamaz. Bunu oluşturmanın tek yolu, hazır statik yöntemi çağırmaktır . yöntemi hazırlamak önce ThreadLocal'ı incelerLooper
Looper
Looper
Looper
mevcut iş parçacığının, iş parçacığı ile ilişkili bir Looper olmadığından emin olmak için. İncelemeden sonra yeni Looper
bir tane oluşturulur ve kaydedilir ThreadLocal
. Hazırladıktan sonra Looper
, yeni mesajları kontrol etmek ve onlarla ilgilenmek için üzerinde döngü yöntemini çağırabiliriz Handler
.
Adından da anlaşılacağı gibi, Handler
sınıf esas olarak mevcut iş parçacığının mesajlarını işlemekten (eklemek, kaldırmak, göndermek) sorumludur MessageQueue
. Bir Handler
örnek ayrıca bir iş parçacığına da bağlıdır. Handler ve iplik arasındaki bağlanma ile elde edilir Looper
ve MessageQueue
. Bir Handler
olduğu zaman bağlı bir Looper
ve daha sonra bağlanmış ilişkili iplik ile Looper
. Bunun tersine Looper
, birden çok İşleyici örneği aynı iş parçacığına bağlanabilir. Post veya benzeri herhangi bir yöntemi çağırdığımızda Handler
, ilişkili olana yeni bir mesaj eklenir MessageQueue
. Mesajın hedef alanı, mevcut Handler
örneğe ayarlanır . Ne zamanLooper
bu mesajı aldığında , mesajın hedef alanında dispatchMessage'ı çağırır , böylece mesaj işlenecek İşleyici örneğine, ancak doğru iş parçacığına geri döner. Arasındaki ilişkiler Looper
, Handler
ve MessageQueue
aşağıda gösterilmiştir:
Looper ile başlayalım. Looper'ın ne olduğunu anladığınızda Looper, Handler ve MessageQueue arasındaki ilişkiyi daha kolay anlayabilirsiniz. Ayrıca, GUI çerçevesi bağlamında Looper'ın ne olduğunu daha iyi anlayabilirsiniz. Looper 2 şey yapmak için yapılmıştır.
1) Looper , yöntemi geri döndüğünde sona eren normal bir iş parçacığını , GUI çerçevesinde ihtiyaç duyulan Android uygulaması çalışana kadar sürekli çalışan bir şeye dönüştürür (Teknik olarak, yöntem döndüğünde hala sona erer . Ama ne demek istediğimi açıklığa kavuşturmama izin verin, altında).run()
run()
2) Looper , GUI çerçevesinde de gerekli olan, yapılacak işlerin sıraya alındığı bir kuyruk sağlar .
Bildiğiniz gibi, bir uygulama başlatıldığında, sistem uygulama için "ana" adı verilen bir yürütme dizisi oluşturur ve Android uygulamaları normalde tamamen tek bir iş parçacığı üzerinde varsayılan olarak "ana iş parçacığı" üzerinde çalışır. Ancak ana konu gizli, özel bir konu değil . Bu sadece new Thread()
kodla oluşturabileceğiniz normal bir iş parçacığıdır , yani run()
metodu döndüğünde sona erer ! Aşağıdaki örneği düşünün.
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();
}
}
Şimdi bu basit prensibi Android uygulamasına uygulayalım. Bir Android uygulaması normal bir iş parçacığında çalıştırılırsa ne olur? "Ana" veya "UI" adı verilen bir iş parçacığı veya uygulamayı ne başlatırsa başlatır ve tüm UI'yi çizer. Böylece kullanıcılara ilk ekran gösterilir. Peki şimdi ne olacak? Ana iş parçacığı sona mı eriyor? Hayır, olmamalı. Kullanıcılar bir şey yapana kadar beklemeli, değil mi? Ama bu davranışı nasıl başarabiliriz? Peki, deneyebiliriz Object.wait()
veyaThread.sleep()
. Örneğin, ana iş parçacığı ilk ekranı görüntülemek için ilk işini bitirir ve uyur. Yapılacak yeni bir iş getirildiğinde uyanır, yani kesintiye uğrar. Şimdiye kadar her şey yolunda, ancak şu anda birden fazla işi tutmak için kuyruk benzeri bir veri yapısına ihtiyacımız var. Bir kullanıcının ekrana seri olarak dokunduğu ve bir görevin tamamlanması daha uzun sürdüğü bir durumu düşünün. Bu nedenle, ilk giren ilk çıkar tarzında yapılacak işleri tutmak için bir veri yapısına ihtiyacımız var. Ayrıca, kesme kullanarak iş parçacığını sürekli çalıştıran ve işleyen işi uygulamanın kolay olmadığını ve karmaşık ve çoğu zaman sürdürülemez bir koda yol açtığını hayal edebilirsiniz. Böyle bir amaç için yeni bir mekanizma yaratmayı tercih ederiz ve Looper da bundan ibarettir . Looper sınıfının resmi belgeder, "İş parçacığı varsayılan olarak kendileriyle ilişkili bir ileti döngüsüne sahip değildir" ve Looper, "iş parçacığı için bir ileti döngüsü çalıştırmak için kullanılan bir sınıftır". Şimdi ne anlama geldiğini anlayabilirsiniz.
Handler ve MessageQueue'ya geçelim. Öncelikle, MessageQueue yukarıda bahsettiğim kuyruktur. Bir Looper içinde bulunur ve işte bu kadar. Looper sınıfının kaynak koduyla kontrol edebilirsiniz . Looper sınıfı, MessageQueue'nun bir üye değişkenine sahiptir.
O halde Handler nedir? Bir kuyruk varsa, kuyruğa yeni bir görevi sıraya koymamızı sağlayacak bir yöntem olmalı, değil mi? Handler'ın yaptığı budur. Yeni bir görevi çeşitli post(Runnable r)
yöntemler kullanarak bir kuyruğa (MessageQueue) sıralayabiliriz . Bu kadar. Bu tamamen Looper, Handler ve MessageQueue ile ilgili.
Son sözüm, temelde Looper, GUI çerçevesinde meydana gelen bir sorunu çözmek için yapılmış bir sınıftır. Ancak bu tür ihtiyaçlar başka durumlarda da olabilir. Aslında, çok iş parçacıklı uygulama için oldukça ünlü bir kalıptır ve Doug Lea'nın "Java'da Eşzamanlı Programlama" bölümünde daha fazla bilgi edinebilirsiniz (Özellikle, bölüm 4.1.4 "İş Parçacıkları" yardımcı olacaktır). Ayrıca, bu tür bir mekanizmanın Android çerçevesinde benzersiz olmadığını düşünebilirsiniz, ancak tüm GUI çerçevelerinin buna biraz benzer olması gerekebilir. Java Swing çerçevesinde de hemen hemen aynı mekanizmayı bulabilirsiniz.
MessageQueue
: A. Tarafından gönderilecek mesajların listesini tutan düşük seviyeli bir sınıftır Looper
. Mesajlar doğrudan a'ya değil MessageQueue
, bunun yerine . [ 3 ] Handler
ile ilişkili nesneler aracılığıyla eklenir Looper
.
Looper
: MessageQueue
Gönderilecek mesajları içeren a üzerinde döngü oluşturur. Kuyruğun yönetilmesinin asıl görevi Handler
, mesaj kuyruğundaki mesajların işlenmesinden (ekleme, çıkarma, gönderme) sorumlu olan tarafından yapılır . [ 2 ]
Handler
: Bu gönderip süreç sağlar Message
ve Runnable
bir iş parçacığı en ilişkili nesnelerin MessageQueue
. Her İşleyici örneği, tek bir iş parçacığı ve bu iş parçacığının ileti kuyruğu ile ilişkilidir. [ 4 ]
Yeni bir tane oluşturduğunuzda Handler
, onu oluşturan iş parçacığının iş parçacığı / ileti kuyruğuna bağlıdır - bu noktadan sonra, iletileri ve çalıştırılabilirleri bu ileti kuyruğuna gönderir ve ileti kuyruğundan çıktıkça bunları yürütür. .
Daha iyi anlamak için lütfen aşağıdaki görüntüye [ 2 ] bakın.
Cevabı @K_Anas ile bir örnekle genişletmek, belirtildiği gibi
Android'deki ana iş parçacığı dışındaki iş parçacıklarından doğrudan UI bileşenlerini güncellemenin yasa dışı olduğu yaygın olarak bilinmektedir.
örneğin, UI'yi Thread kullanarak güncellemeye çalışırsanız.
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();
uygulamanız istisnai olarak kilitlenecek.
android.view.ViewRoot $ CalledFromWrongThreadException: Yalnızca bir görünüm hiyerarşisi oluşturan orijinal iş parçacığı görünümlerine dokunabilir.
başka bir deyişle ie veya 'ye Handler
atıfta bulunan ve görevi olarak ileten' i kullanmanız gerekir .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() ;