Android ile ilgili temel bilgiler: UI iş parçacığında kod çalıştırma


450

UI iş parçacığında çalışan kod bakış açısından aşağıdakiler arasında herhangi bir fark var:

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

veya

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

ve

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}

Sorumu açıklığa kavuşturmak için: Bu kod bir hizmet iş parçacığı, genellikle bir dinleyici denirdi. Ayrıca AsynkTask'ın doInBackground () işlevinde ya da ilk iki snippet'ten önce çağrılan yeni bir Görev (...) içinde gerçekleştirilecek ağır bir çalışma olduğunu varsayalım. Her neyse, AsyncTask öğesinin onPostExecute () yöntemi olay kuyruğunun sonuna konuyor, değil mi?
Luky

Yanıtlar:


288

Bunların hiçbiri tam olarak aynı değildir, ancak hepsinin aynı net etkisi olacaktır.

Birinci ve ikinci arasındaki fark, olmak için ne olduğunu üzerinde kod çalıştırıldığında ana uygulama iş, ilki ( runOnUiThread()) çalıştırır Runnablehemen. İkincisi ( post()) Runnablezaten ana uygulama iş parçacığında olsanız bile, olay kuyruğunun sonuna her zaman girer .

Üçüncüsü, bir örneğini oluşturup yürüttüğünüzü varsayarsak, varsayılan olarak BackgroundTaskbir işlem doInBackground()yapmadan önce bir iş parçacığını iş parçacığı havuzundan almak için çok zaman harcayacaktır post(). Bu, üçünden en az verimlidir. AsyncTaskYalnızca kullanımı için değil, bir arka plan iş parçacığında yapacak işiniz varsa kullanın onPostExecute().


27
Ayrıca AsyncTask.execute(), tüm arka plan çalışmalarınızı içine taşımazsanız doInBackground()ve AsyncTaskdüzgün kullanmazsanız , bu seçeneği yine de UI iş parçacığında kodun arka plan iş parçacığından çalıştırılması için işe yaramaz hale getiren UI iş parçacığından aramanızı gerektirdiğini unutmayın .
kabuko

@kabuko AsyncTaskUI iş parçacığından aradığımı nasıl kontrol edebilirim ?
Neil Galiaskarov

@NeilGaliaskarov Bu sağlam bir seçenek gibi görünüyor: stackoverflow.com/a/7897562/1839500
Dick Lucas

1
@NeilGaliaskarovboolean isUiThread = (Looper.getMainLooper().getThread() == Thread.currentThread());
ban-

1
@NeilGaliaskarov, M kullanımından büyük veya M kullanıma eşit sürümler içinLooper.getMainLooper().isCurrentThread
Balu Sangem

252

Ben HPP yorum bir gibi, herhangi bir parametre olmadan herhangi bir yerde kullanılabilir:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

2
Bu ne kadar verimli? Diğer seçeneklerle aynı mı?
EmmanuelMess

59

Kullanmanın dördüncü yolu var Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

56
Buna dikkat etmelisin. Çünkü UI olmayan bir iş parçacığında bir işleyici oluşturursanız, UI olmayan iş parçacığına ileti gönderirsiniz. Bir işleyici, oluşturulduğu iş parçacığına varsayılan olarak mesaj gönderir.
lujop

108
ana UI iş parçacığında yürütmek için, statik bir çağrı yapmak new Handler(Looper.getMainLooper()).post(r)için tercih edilen bir yöntemdir Looper.getMainLooper(), oysa kapsamda postOnUiThread()bir örneği olmalıdır MainActivity.
HPP

1
@HPP Bu yöntemi bilmiyordum, ne bir Görüşünüz ne de Aktiviteniz yoksa harika bir yol olacaktır. Harika çalışıyor! çok teşekkür ederim çok çok!
Sulfkain

@lujop AsyncTask'ın onPreExecute geri arama yöntemi için de geçerlidir.
Sreekanth Karumanaghat

18

Pomber'in cevabı kabul edilebilir, ancak tekrar tekrar yeni nesneler yaratmanın büyük bir hayranı değilim. En iyi çözümler her zaman bellek domuzunu azaltmaya çalışanlardır. Evet, otomatik çöp toplama var ama bir mobil cihazda bellek tasarrufu en iyi uygulama sınırları içinde. Aşağıdaki kod, bir hizmetteki TextView öğesini güncelleştirir.

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

Bunun gibi herhangi bir yerden kullanılabilir:

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);

7
GC ve nesne oluşturmayı belirtmek için +1. ANCAK, kesinlikle aynı fikirde değilim The best solutions are always the ones that try to mitigate memory hog. Bunun için başka birçok kriter var bestve bunun hafif bir kokusu var premature optimization. Yani, yarattığınız nesne sayısının bir sorun olduğunu yeterince bilmediğinizi bilmiyorsanız (uygulamanızın muhtemelen çöp oluşturduğu on bin farklı yolla karşılaştırıldığında), besten basit olanı (anlaması en kolay olanı) yazmak olabilir. ) kodu girin ve başka bir göreve geçin.
ToolmakerSteve

2
BTW, ana UI iş parçacığına herhangi bir yazı için genellikle yararlı olduğundan veya textViewUpdaterHandlergibi bir şey daha iyi adlandırılır ; hiç TextViewUpdater sınıfınıza bağlı değildir. Onu bu kodun geri kalanından uzaklaştırır ve başka bir yerde kullanılabileceğini açıkça belirtirdim ... Kodun geri kalanı şüpheli, çünkü dinamik olarak bir nesne oluşturmaktan kaçınmak için içine tek bir çağrı olabilir iki adım ve bu geçici olarak kullandığınız uzun ömürlü bir nesneye dayanır. Gereksiz karmaşıklık ve iş parçacığı için güvenli değil. Bakımı kolay değil. uiHandlermainHandlersetTextpost
ToolmakerSteve

Eğer varsa gerçekten bu değer önbelleğe olduğunu defalarca denilen bir durum var uiHandlerve textViewUpdaterdaha sonra hiç değiştirerek sınıfını iyileştirmek public void setText(String txt, Handler uiHandler)ve yöntem satırı ekleyerek uiHandler.post(this); tek adımda yapabilirsiniz Sonra arayan: textViewUpdater.setText("Hello", uiHandler);. Daha sonra gelecekte, iş parçacığı güvenli olması gerekiyorsa, yöntem ifadelerini bir kilit içine sarabilir uiHandlerve arayan değişmeden kalır.
ToolmakerSteve

Runnable'ı yalnızca bir kez çalıştırabileceğinizden eminim. Hangi kesinlikle kırmak fikir, hangi genel olarak güzel.
Nativ

@Nativ Nope, Runnable birçok kez çalıştırılabilir. Bir Konu yapamaz.
Zbyszek

8

Android P'den itibaren şunları kullanabilirsiniz getMainExecutor():

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

Gönderen Android geliştirici docs :

Bu bağlamla ilişkili ana iş parçacığında enqueued görevleri çalıştıracak bir Executor döndürün. Bu, uygulama bileşenlerine (etkinlikler, hizmetler vb.) Çağrı göndermek için kullanılan iş parçacığıdır.

Gönderen CommonsBlog :

Ana uygulama iş parçacığında işlerini yürütecek bir Executor almak için Context üzerinde getMainExecutor () 'i çağırabilirsiniz. Looper ve özel bir Executor uygulaması kullanarak bunu gerçekleştirmenin başka yolları da vardır, ancak bu daha basittir.


7

Fragment'ta kullanmanız gerekiyorsa,

private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }


    ((MainActivity)context).runOnUiThread(new Runnable() {
        public void run() {
            Log.d("UI thread", "I am the UI thread");
        }
    });

onun yerine

getActivity().runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

Çağrı cihazı parçası gibi bazı durumlarda boş işaretçi istisnası olacaktır


1

merhaba millet bu herhangi bir uzakta temel bir soru söyle

Kullanım Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});

Genel amaçlı Handler'ı runOnUiThread üzerinde kullanmayı seçmenizin bir nedeni var mı?
ThePartyTurtle

Bu java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(), UI iş parçacığından çağrılmazsa bir verir .
Roc Boronat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.