Android röportajları için temel bilgi noktaları: Android'de Handler hakkında sekiz önemli sorunun bir özeti

Önsöz

İşleyici mekanizması neredeyse bir Android röportajında sorulması gereken bir sorudur. İşleyicinin kaynak kodunu birçok kez görmüş olsam da, görüşmeci tarafından sorulan bazı sorular cevaplanamayabilir. Görüşmede kapsanan işleyicinin bilgi noktalarını özetleme fırsatını kullanın.

1. Bana Handler'ın altında yatan uygulama ilkesinden bahseder misiniz?

Aşağıdaki resim tüm işleyici mekanizmasını tam olarak göstermektedir.

İşleyicinin gerçekleştirme ilkesini anlamak için, aslında en önemli şey, Looper'ın gerçekleştirme ilkesini anlamaktır.Looper, işleyici mekanizmasının gerçekleştirilmesinin özüdür. Herhangi bir işleyici sendMessage veya post kullandığında, önce bir Mesaj oluşturur, kendisini mesaja koyar ve ardından Mesajı ilgili Looper'ın MessageQueue'suna koyar ve Looper, işleyiciyi veya çalıştırılabilirliği yürütmek için mesajı almak için MessageQueue'yu kontrol eder. Geçerli iş parçacığındaki işleyicinin belirtilen işlemini gerçekleştirmek için, önce geçerli iş parçacığında bir döngüleyici olup olmadığını görmelisiniz.Bir döngüleyici varsa, işleyici önce sendMessage veya post aracılığıyla bir mesaj oluşturacak ve ardından mesajı geçerli iş parçacığının ilmek yapıcısına yerleştirecektir. Mesaj, mevcut iş parçacığındaki bir döngüde yürütülecektir.Eğer döngüleyici yoksa, mevcut iş parçacığında looper.prepare () yöntemi ile bir döngü yapıcı oluşturulmalıdır ve ardından döngüyü gerçekleştirmek için looper.loop () etkin olarak çalıştırılacaktır.

Sıralamak gerekirse, en basitleri aslında şu dördüdür:

1. Her iş parçacığında ThreadLocal tarafından kaydedilen en fazla bir Looper vardır Looper'da işleyiciyi kaydeden ve işleyici tarafından gönderilen mesajı yürüten bir Mesaj kuyruğu vardır.

2. Looper, iş parçacığında Looper.prepare () tarafından oluşturulur ve Looper, ThreadLocal tarafından kaydedilir. Looper.prepare () her iş parçacığında yalnızca bir kez çağrılabilir, bu da bir iş parçacığında yalnızca bir Looper olduğu anlamına gelir İplikte Looper'ın benzersizliği.

3. sendMessage veya post işlemi işleyicide yürütülür.Bu işlemler tarafından yürütülen iş parçacığı, işleyicideki Looper'ın bulunduğu iş parçacığıdır. İşleyicinin nerede oluşturulduğu önemli değildir, ancak İşleyicideki Looper'ın nerede oluşturulduğu ile ilgilidir.

4. Bir iş parçacığında yalnızca bir İlmek Yapıcı olabilir, ancak bir Döngüleyici birden fazla işleyiciye karşılık gelebilir ve aynı İlmek Yapıcıdaki mesajların tümü aynı iş parçacığı içinde yürütülür.

2. İşleyici mekanizması, sendMessage ve post (Runnable) arasındaki fark nedir?

SendMessage ile post arasındaki farkı görmek için, kaynak koduna bakmanız gerekir. İşleyiciyi kullanmanın birkaç yolu vardır. Önce bu yollara bakın ve ardından kaynak koddan farkı analiz edin. Örnek 1. Ana iş parçacığında işleyiciyi kullanma

// Ana konu İşleyici mHandler = new Handler (new Handler.Callback () { @Override public boolean handleMessage (@NonNull Message msg) { eğer (msg.what == 1) { // bir şeyler yapmak } yanlış dönüş; } }); Mesaj msg = Message.obtain (); msg.what = 1; mHandler.sendMessage (msg);

Yukarıdakiler, ana iş parçacığındaki işleyiciyi kullanmaktır, çünkü sistem Android'deki ana iş parçacığında Looper'ı oluşturmuştur, böylece kendiniz döngüleyici oluşturmanıza gerek kalmaz. Yukarıdaki kod bir alt iş parçacığında çalıştırılırsa, rapor verecektir

"+ Thread.currentThread () iş parçacığı içinde işleyici oluşturulamıyor Looper.prepare () çağırmayan + "

İşleyicinin alt iş parçacığındaki işleyişini halletmek istiyorsanız, Looper'ı kendiniz oluşturmalısınız.

Örnek 2: Alt iş parçacıkları içinde işleyicileri kullanma

Thread thread = new Thread (yeni Runnable () { @Override public void run () { Looper.prepare (); İşleyici işleyici = yeni İşleyici (); handler.post (new Runnable () { @Override public void run () { } }); Looper.loop (); } });

Yukarıdaki İş parçacığındaki işleyiciyi kullanarak, önce geçerli iş parçacığında bir Looper nesnesi oluşturmak için Looper.prepare yöntemini yürütün ve onu geçerli iş parçacığının ThreadLocal'ına kaydedin. Looper.prepare () içindeki kaynak koduna bakın:

//Hazırlamak private static void ready (boolean quitAllowed) { eğer (sThreadLocal.get ()! = null) { yeni RuntimeException ("İş parçacığı başına yalnızca bir Looper oluşturulabilir"); } sThreadLocal.set (yeni Looper (quitAllowed)); } // Looper private Looper (boolean quitAllowed) { mQueue = new MessageQueue (quitAllowed); mThread = Thread.currentThread (); }

Hazırlama yönteminin önce sThreadLocal'dan alacağı görülebilir.Eğer Looper daha önce oluşturulmuşsa, bir hata raporlanacak, aksi takdirde yeni bir Looper üretilecek ve iş parçacığının ThreadLocal'ında saklanacak, bu da her iş parçacığında yalnızca bir benzersiz Looper.

Ek olarak: Looper'ın geçerli iş parçacığına bir başvurusu olduğundan, bazen Looper'ın bu özelliği, geçerli iş parçacığının ana iş parçacığı olup olmadığını belirlemek için kullanılabilir.

@RequiresApi (api = Build.VERSION_CODES.KITKAT) boolean isMainThread () { return Objects.requireNonNull (Looper.myLooper ()). getThread () == Looper.getMainLooper (). GetThread (); }

sendMessage vs post

SendMessage'ın kod çağrı zincirine bir göz atalım:

EnqueueMessage'ın kaynak kodu aşağıdaki gibidir:

private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, uzun uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid (); dönüş queue.enqueueMessage (msg, uptimeMillis); }

EnqueueMessage'ın kod işlemesi çok basittir, msg.target = this; mevcut işleyici nesnesini message.target'a vermektir. Ardından kuyruğa giren mesaj hakkında konuşun.

Posta kodu arama zinciri:

Gönderiyi çağırırken, sendMessage işlemiyle aynı olan bir Mesaj oluşturmak için önce getPostMessage çağrılacaktır. GetPostMessage yönteminin kaynak koduna bakalım:

özel statik Mesaj getPostMessage (Runnable r) { Mesaj m = Message.obtain (); m.callback = r; dönüş m; }

Önce getPostMessage'da bir Messgae üretildiğini ve mesajın geri çağrısına çalıştırılabilirin atandığını görebilirsiniz Mesajlar MessageQueue'ya yerleştirildikten sonra Looper'ın bunu nasıl işlediğini görelim.

için (;;) { Mesaj msg = queue.next (); // engelleyebilir eğer (msg == null) { dönüş; } msg.target.dispatchMessage (msg); }

Looper, mesaj listesinden geçecek ve mesaj boş olmadığında msg.target.dispatchMessage (msg) yöntemini çağıracaktır. Mesaj yapısına bakın:

Diğer bir deyişle, msg.target.dispatchMessage yöntemi aslında çağrılan İşleyicideki dispatchMessage yöntemidir. Şimdi dispatchMessage yönteminin kaynak koduna bakalım:

public void dispatchMessage (@NonNull Message msg) { eğer (msg.callback! = null) { handleCallback (msg); } Başka { eğer (mCallback! = null) { eğer (mCallback.handleMessage (msg)) { dönüş; } } handleMessage (msg); } } // private static void handleCallback (Mesaj mesajı) { message.callback.run (); }

Post yöntemi çağrıldığında oluşturulan message.callback = runnable olduğundan, dispatchMessage yöntemi doğrudan message.callback.run () öğesini çağırır; yani postadaki runnable yöntemi doğrudan çalıştırılır. SendMessage'da, mCallback boş değilse, mCallback.handleMessage (msg) yöntemi çağrılır, aksi takdirde handleMessage yöntemi doğrudan çağrılır.

sonuç olarak Post yöntemi ile handleMessage yöntemi arasındaki fark, gönderinin çalıştırılabilirliğinin geri aramada çalıştırma yöntemini doğrudan çağırmasıdır; sendMessage yöntemi ise kullanıcının bunu işlemek için mCallback veya handleMessage yöntemini etkin bir şekilde geçersiz kılmasını gerektirir.

3. Looper, sistem kaynaklarını her zaman tüketecek mi?

İlk olarak, Looper'ın sistem kaynaklarını her zaman tüketmeyeceği sonucuna varın Looper's MessageQueue'da mesaj olmadığında veya zamanlama mesajı yürütme süresine ulaşmadığında, Looper'ı tutan iş parçacığı bloke durumuna girecektir.

Looper'ın engelleme durumunda olduğu iş parçacığının nasıl olduğuna bakalım. Looper engellemesi, mesajın sırasının kaldırılmasıyla ilgili olmalıdır, bu nedenle mesajın sırasını çözme koduna bakın.

Mesaj deque

Sonraki mesaj () { // Mesaj döngüsü zaten çıkmışsa ve atılmışsa buraya geri dönün. // Bu, uygulama çıktıktan sonra bir döngü yapıcıyı yeniden başlatmaya çalışırsa olabilir // desteklenmeyen. son uzun ptr = mPtr; eğer (ptr == 0) { boş döndür; } int nextPollTimeoutMillis = 0; için (;;) { eğer (nextPollTimeoutMillis! = 0) { Binder.flushPendingCommands (); } nativePollOnce (ptr, nextPollTimeoutMillis); // Boşta bir işleyici çağırılırken yeni bir mesaj teslim edilmiş olabilirdi // öyleyse geri dönün ve beklemeden bekleyen mesajı tekrar arayın. eğer (hasNoMessage) { nextPollTimeoutMillis = -1; } } }

Yukarıdaki mesaj kuyruğunu giderme yöntemi kısaltılmıştır, esas olarak aşağıdaki paragrafa bakın, mesaj olmadığında nextPollTimeoutMillis = -1;

eğer (hasNoMessage) { nextPollTimeoutMillis = -1; }

Bu alanın for döngüsündeki rolüne bakın:

eğer (nextPollTimeoutMillis! = 0) { Binder.flushPendingCommands (); } nativePollOnce (ptr, nextPollTimeoutMillis);

Binder.flushPendingCommands (); Bu yöntemin işlevi, kaynak kodda verilen açıklamada görülebilir:

/ ** * Geçerli iş parçacığında bekleyen tüm Bağlayıcı komutlarını çekirdeğe boşalt * sürücü. Bu olabilir * uzun süre engelleyebilecek bir işlemi gerçekleştirmeden önce aramak yararlıdır * süre, bekleyen nesne referanslarının serbest bırakıldığından emin olmak için * sürecin nesnelere daha uzun süre tutunmasını önlemek için * ihtiyacı var. * /

Yani, kullanıcı iş parçacığının bir nesneyi uzun süre tutmasını önlemek için kullanıcı iş parçacığı engellemeye girmeden önce çekirdek iş parçacığına mesajlar gönderin. Aşağıdaki yönteme bir göz atın: nativePollOnce (ptr, nextPollTimeoutMillis); nextPollingTimeOutMillis = -1 olduğunda, bu yerel yöntem mevcut iş parçacığını engeller. İş parçacığı engellendikten sonra, bir ileti kuyruğa bir dahaki sefer girdiğinde çalıştırılabilir duruma yeniden girer, yani Looper Çalışan bellek sonsuz bir döngüde tüketilmeyecek ve kuyruktaki renkli mesaj zamana kadar olmadığında mevcut iş parçacığı engellenecek, ancak nextPollingTimeOutMillis olan bir engelleme süresi olacaktır. > 0 kez.

Mesaj kuyruğunda mesaj olmadığında, lüper mesaj kuyruğu tarafından uyandırılmalıdır.

Mesaj sıraya koyma

boolean enqueueMessage (Message msg, long when) { eğer (msg.target == null) { yeni IllegalArgumentException ("Mesajın bir hedefi olmalıdır."); } eğer (msg.isInUse ()) { yeni IllegalStateException (msg + "Bu ileti zaten kullanımda."); } senkronize edildi (bu) { if (mQuitting) { IllegalStateException e = new IllegalStateException ( msg.target + "çalışmayan bir iş parçacığında bir İşleyiciye mesaj gönderme"); Log.w (TAG, e.getMessage (), e); msg.recycle (); yanlış dönüş; } msg.markInUse (); msg.when = ne zaman; Mesaj p = mMessages; boolean needWake; eğer (p == null || ne zaman == 0 || ne zaman < p.when) { // Yeni kafa, bloke edilmişse olay kuyruğunu uyandır. msg.next = p; mMessages = msg; needWake = mBlocked; } Başka { // Sıranın ortasına eklenir. Genellikle uyanmamıza gerek yoktur // kuyruğun başında bir engel yoksa olay kuyruğunu yükseltin // ve mesaj, kuyruktaki en eski eşzamansız mesajdır. needWake = mBlocked p.target == null msg.isAsynchronous (); Mesaj önceki; için (;;) { prev = p; p = p.sonraki; eğer (p == null || ne zaman < p.when) { kırmak; } if (needWake p.isAsynchronous ()) { needWake = yanlış; } } msg.next = p; // değişmez: p == prev.next prev.next = msg; } // mPtr! = 0 kabul edebiliriz çünkü mQuitting yanlıştır. eğer (needWake) { nativeWake (mPtr); } } doğruya dön; }

Ekibe katıldıktan sonra bir mesajın olacağını yukarıda görebilirsiniz.

eğer (needWake) { nativeWake (mPtr); }

Yöntem, iş parçacığını uyandırmak için bu yöntemi çağırın. Ayrıca mesajlar kuyruğa alındığında, arkada uzun gecikme süresi önde ve kısa sürede mesajın gecikme süresine göre bağlantılı listede sıralanır. Zaman aynıysa, yerleştirme zamanına göre sıralanır, yerleştirme zamanı önde ve yerleştirme zamanı daha sonra arkada olur.

4. Android'in Tanıtıcı mekanizması, Döngüsel ilişki, ana iş parçacığının İşleyicisi alınan iletinin hangi İşleyiciden olduğunu nasıl belirler?

Looper, Mesajın hangi işleyiciden geldiğini nasıl belirler? Aslında çok basittir 1'de analiz edildiği gibi, işleyici Mesaj gönderirken bir Mesaj nesnesi oluşturur ve kendisini Mesajın hedefine koyar, böylece Looper mevcut mesajın hangi işleyicinin Mesajdaki hedefe bağlı olduğuna karar verebilir. Gel.

5. İşleyici mekanizması süreci, Looper'ı Looper'daki gecikmiş mesajlarla kim uyandıracak?

3'ten itibaren aşağıdaki yöntemin mesajın kuyruğundan çıkarıldığı for döngüsü kuyruğunda çağrılacağını bilin.

nativePollOnce (ptr, nextPollTimeoutMillis);

Gecikmiş bir mesaj ise, bloke edilen nextPollTimeoutMillis saatinden sonra uyandırılacaktır NextPollTimeoutMillis, mesajın yürütüleceği saat ile mevcut saat arasındaki farktır.

6. İşleyici bellek sızıntılarına nasıl neden olur? Nasıl çözülür?

Alt iş parçacığında, Looper bunun için manuel olarak yaratılmışsa, her şey tamamlandıktan sonra mesaj döngüsünü sonlandırmak için çıkma yöntemi çağrılmalıdır, aksi takdirde çocuk iş parçacığı her zaman bekleme durumunda olacaktır ve Looper çıkarsa iş parçacığı Hemen sona erer, bu nedenle gerekmediğinde Looper'ı sonlandırmanız önerilir.

Looper.myLooper (). Quit ()

Ardından, mesaj İşleyicinin handleMessage yönteminde (veya çalıştırma yönteminde) işlenirse, bu gecikmiş bir mesajsa, her zaman ana iş parçacığının ileti kuyruğunda depolanır ve sistemin Activity kurtarmasını etkileyerek bellek sızıntılarına neden olur.

Ayrıntılar için lütfen İşleyici bellek sızıntısı analizi ve çözümüne bakın

Özetlemek gerekirse, İşleyici bellek sızıntısını çözmek için iki ana nokta vardır

1. Geciken mesajlar var ve Aktivite yok edildiğinde Mesajlar kaldırılmalıdır

2. Anonim iç sınıfların neden olduğu sızıntılar anonim statik iç sınıflara değiştirilir ve bağlama veya Aktiviteye zayıf başvurular kullanılır.

7. İşleyici mekanizmasında Looper'ın benzersizliği nasıl sağlanır?

Looper, iş parçacığının ThreadLocal'ında saklanır İşleyiciyi kullanırken, Looper.prepare () bir Looper yaratmak için çağrılır ve mevcut iş parçacığının ThreadLocal'ına yerleştirilir.

private static void ready (boolean quitAllowed) { eğer (sThreadLocal.get ()! = null) { yeni RuntimeException ("İş parçacığı başına yalnızca bir Looper oluşturulabilir"); } sThreadLocal.set (yeni Looper (quitAllowed)); }

Gördüğünüz gibi, hazırlamayı birden çok kez çağırırsanız, iş parçacığı başına yalnızca bir Döngüleyici oluşturulabileceğini bildirir, böylece bir iş parçacığında yalnızca bir Looper olduğundan emin olabilirsiniz.

8. İşleyici konuları nasıl değiştirebilir ve mesaj gönderebilir?

İşleyicinin yürütülmesinin, işleyiciyi oluşturan iş parçacığı ile hiçbir ilgisi yoktur, bu, alt iş parçacığına bir İşleyici ekleyerek döngü yapıcıyı oluşturan iş parçacığı ile ilgilidir, ancak İşleyiciyle ilgili Döngüleyici ana iş parçacığıdır, böylece işleyici bir çalıştırılabilir veya sendMessage gönderimi yürütürse, son Tanıtıcı Mesaj ana iş parçacığında yürütülür.

Thread thread = new Thread (yeni Runnable () { @Override public void run () { Looper.prepare (); İşleyici işleyici = yeni İşleyici (getMainLooper ()); handler.post (new Runnable () { @Override public void run () { Toast.makeText (MainActivity.this, "merhaba, dünya", Toast.LENGTH_LONG) .show (); } }); Looper.loop (); } }); thread.start ();

Sonunda

Hepsi bugünün röportaj paylaşımı için. Aynı cümle. Sadece anlamanız gereken değil, aynı zamanda onları iyi ifade etmeniz gereken bazı şeyler var, böylece görüşmecinin Anlayışınızı tanıyabilmesi için İşleyici mekanizması gibi. Bu, röportajda mutlaka sorulması gereken bir sorudur. . Bazı belirsiz noktalar var, belki sadece röportajda yaşıyor, gerçek işte hiç kullanmayacaksın ama ne olduğunu bilmelisin.

Son olarak, buradaki editör, yukarıdaki teknik sistem şemalarıyla ilgili düzinelerce set koleksiyonunu paylaşıyor. Tencent, Toutiao, Ali, Meituan ve diğer şirketlerden 19 yıl içinde mülakat soruları , Teknik noktaları video ve PDF olarak düzenledi (aslında beklenenden çok daha fazla enerji harcadı), Bilgi bağlamı + birçok ayrıntı , Sınırlı alan nedeniyle, işte resim şeklinde bir parçası.

ve ayrıca Gelişmiş mimari teknolojisi gelişmiş zihin haritası, Android geliştirme röportajı özel materyalleri , Herkesin ileri seviyeyi geliştirmeyi öğrenmesine yardımcı olmak için gelişmiş gelişmiş çerçeve materyalleri ve ayrıca internette öğrenmek için materyal aramak için herkesin zamanından tasarruf sağlar ve ayrıca birlikte öğrenmek için arkadaşlarla paylaşılabilir.

[Android temel ileri teknoloji PDF belgesi, BAT görüşmesindeki gerçek soruların analizi]

[Algoritma Koleksiyonu]

[Android temel bilgi noktalarını genişletin]

[Gelişmiş mimari video öğrenme kaynaklarının Android bölümü]

Android Tanıtım Videosu alıp öğrendikten sonra, daha da güçlü hale geliyor! BATJ fabrikasına girin ve benzeri (hazırlık)! Günümüzde İnternetin soğuk olduğu söyleniyor. Aslında, yanlış arabaya bindiğinizden ve daha az (beceri) giydiğinizden başka bir şey değil. Doğru arabaya binerseniz ve kendi teknik yeteneğiniz yeterince güçlüyse, şirketin değişim maliyeti yüksek olacaktır. Sadece son iş Curd'u ortadan kaldırmak için! Günümüzde piyasada genç programcılar akınına uğramaktadır. Bu eğitim seti 1-6 yaş arası Android geliştirme mühendislerine yöneliktir. Darboğaz dönemindedirler. Gelecek yıl maaş artışlarını aşmak isteyenler, ileri düzey Android orta ve kıdemli, mimarlar daha da önemlidir Sudaki bir balık gibi, çabuk alın!

[Android gelişmiş öğrenme videosu], [Android mülakat hilelerinin tam seti PDF], [Android geliştirme temel bilgi notları] özel mesajla [Android] ücretsiz olarak elde edilebilir!

Görüşme sırasında hangi konulara dikkat edilmelidir? İşte programcı iş görüşmelerinin 66 detayı
önceki
Yıl ortasında Ali'ye tekrar katılmak için iyi bir zaman, Ali'ye katılmak istersem ne yapmalıyım?
Sonraki
Android programcıları: İyi bir iş bulmak ve salgın krizi kırmak için mülakata nasıl hazırlanılır?
"Bir günde röportaj için nasıl hazırlandım ve Tencent teklifini nasıl aldım?"
Muhabir: Temel java nasıl? Çok iş parçacıklı okuma mutlaka çok iş parçacıklı güvenlik sorunlarına neden olur mu?
Yılbaşı tarım ürünleri siparişleri 200 milyonu aştı, Pinduoduo markalı tarım ürünlerini şehirlere ve kırsal alanlara tanıtıyor
Didi, 62 tren istasyonunda çevrimiçi araç çağırma için özel toplama noktaları ve kanallar oluşturuyor
Marka yükseltme ve geliştirme Wanrun Group, 2020'de yeni bir sayfa açıyor
Yeni Land Rover Discovery Sport neden Discovery ailesinin spor şampiyonu oldu?
Ağır! Chengdu: İlkokul 13 Nisan'da başlıyor! Lise giriş sınavı orta derecede gecikti
Uzman ve malzeme gönderen Çin, bunun gibi Asyalı kardeşleri destekliyor
"Herhangi bir zorlukla karşılaşırsanız, kırmızı yeleği bulun!" - Wanjia'nın ışıklarını kalpleriyle yakarlar
İlk Gözlem | Genel Sekreter Zhejiang, maske takıp takmayacağını denetledi
Chongqing kızları ve Wuhan kızlarının uzun bir ilişkisi var: "Babamı sana bağışlıyorum ve dayanmalısın!"
To Top