18 -Looper, MessageQueue Handler,
HandlerThread, Thread, Runnable,Message
Overview
(http://stackoverflow.com/questions/12877944/what-is-the-relationship-between-looper-handler-and-messagequeue-in-android)
It's widely known
that it's illegal to update UI components directly from threads other than main thread in android. if we need to
start a separate thread to do some expensive work and update UI after it's done. The idea is to create a Handler object associated with main thread, and post a Runnable to it at appropriate time. This
Runnable
will be invoked on the main thread. This mechanism is implemented with Looper and Handler classes. ( Main thread'den başka bir thread'de
UI component'leri update etmek illegaldir,yasaktır. Eğer ayrı bir thread'de
ağır bir iş yapmak ve buiş bittikten sorna UI'ı update etmek istiyorsak şunları
yapmalıyız:Main thread ile ilişkili bir Handler object yaratmalıyız. Ona(o
kim?) uygun bir zaman bir Runnable object göndermeliyiz. Runnable object main
thread'de invoke edilir. Bu mekanizma Looper ve Handler kullanılarak implement
edilir. Bu paragrafı ben de tam anlamadım buraya daha sonra geri dönüp incele
tekrar. )
The
Looper
class maintains a MessageQueue, which contains a list of messages. An important character of Looper is that it's associated with the thread within which the Looper
is created. This
association is kept forever and can't be broken nor
changed. ( Looper class'ı, bir MessageQueue
içerir. MessageQueue ise Message object içeren bir LinkedList içerir. Looper,
hangi thread içerisinde yaratıldıysa o thread ile ilişkilendirilir, bu özellik
sonsuza kadar sürer asla bozulmaz ve değiştirilemez. )
Also note that a
thread can't be associated with more than one
Looper
. In order to guarantee this association, Looper
is stored in thread-local storage, and it can't be created via
its constructor directly. The only way to create it is to call prepare static method on Looper
. prepare method first examines ThreadLocal of current thread to make sure that there isn't already a Looper
associated with the thread. After the examination, a new Looper
is created and saved in ThreadLocal
. Having prepared the Looper
, we can call loop method on it to check for new messages and have Handler
to deal with them.
( Ayrıca, 1 thread sadece 1 Looper ile ilişkilendirilebilir. Bu
ilişkiyi garantilemek için, Looper thread'in scope'u içerisinde yaşar, yani
Looper'ın doğrudan contructor'ını çağırarak bir Looper object yaratılamaz.
Looper object yaratmanın yolu, Looper class'ının static bir method'u olan
prepare() method'unu çağırmak gerekir. prepare() method'u, current thread ile
ilişkili bir Looper var mı diye bakar, yoksa yeni bir Looper object yaratır, ve
bu object'i ThreadLocal'a kaydeder. Sonra Looper object'in loop() method'unu
çağırırız, bu method looper'ın messageQueue'sunda yeni bir mesaj var mı diye
bakar. Varsa bu mesajı Handler'a verir.)
As the name
indicates, the
Handler
class is mainly responsible for handling (adding, removing,
dispatching) messages of current thread's MessageQueue
. A Handler
instance is also bound to a thread. The binding between Handler and Thread is achieved via
Looper
and MessageQueue
. A Handler
is always bound to a Looper
, and subsequently bound to the thread associated with the Looper
. (Handler class'ı, current thread'in MessageQueue'sundaki
mesajlar ile ilgilenmekten sorumludur. Bir Handler object, bir thread'e
bound'dur, yani bir thread'e bağlıdır, bir thread ile ilişkilidir. Bir Handler
genellikle bir Looper'a bağlıdır(bound) dolayısıyla Looper'ın bağlı olduğu
thread'e bağlıdır. Dolayısıyla bir handler'ın bir thread ile ilişkilendirilmesi
Looper ve MessageQueue aracılığıyla gerçekleştirilir. Nasıl
ilişkilendirileceğini ben de bilmiyorum henüz. )
Unlike
Looper
, multiple Handler instances can be bound to the same thread. Whenever
we call post or any methods alike on the Handler
, a new message is added to the associated MessageQueue
. The target field of the message is set to current Handler
instance. When the Looper
received this message, it invokes dispatchMessage
on message's target field, so that the message routes back
to the Handler instance to be handled, but on the correct thread. ( 1 Looper'ın sadece 1 thread ile ilişkili olabileceğini
söylemiştik daha önce. Handler'larda ise durum farklıdır, 1'den fazla Handler
aynı thread'e ilişkilendirilebilir. Handler class'ının post() method'unu veya
buna benzer bir method'unu çağırdığımızda, Handler'ın ilişkili olduğu
Looper'daki MessageQueue'ya bir mesaj eklenir. Bu mesajın target alanına mesajı
gönderen Handler object set edilir, bu sayede bu mesajı looper'a gönderen
handler bir süre sonra bu mesajı looper'dan geri alacaktır. Yani her mesaj
göndericisine teslim edilir looper tarafından. Looper mesajı alınca, mesajın
target alanındaki dispatchMessage'I invoke edince bu mesaj doğru thread'deki
doğru handler'a gönderilir. )
---
Let's
start with the Looper. You can understand the relationship between Looper,
Handler and MessageQueue more easily when you understand what Looper is. Also
you can better understand what Looper is in the context of GUI framework.
Looper is made to do 2 things. ( Looper, Handler ve MessageQueue arasındaki ilişkiyi
anlamak için önce Looper'ın ne olduğunu anlamak gerekir. Looper şu iki işi
yapar. )
1) Looper transforms
a normal thread, which terminates when its run() method return, into
something run continuously until Android app is running, which is needed in
GUI framework (Technically, it still terminates when run() method return. But
let me clarify what I mean in below). ( Looper, Normal bir thread, run() method'u return
ettikten sonra yok edilir. Looper normal bir thread'i Android uygulaması
çalıştığı sürece çalışan bir thread'e dönüştürür. GUI framework için böyle bir
thread'e ihtiyaç vardır. Teknik olarak run() method'U return ettikten sonra
thread yok edilir ancak demek istediğimiz aşağıdakileri okuyunca
anlayacaksınız. )
2) Looper provides
a queue where jobs to be done are enqueued, which is also needed in
GUI framework. (Looper bir mesaj kuyruğu içerir. Yapılacak işler bu kuyruğa
sokulur, sonra single bir thread bu kuyruktaki işleri tek tek gerçekleştirir.
Böyle bir şeye GUI framework'de de ihtiyaç vardır. )
As you may know, when an application is
launched, the system creates a thread of execution for the application, called
“main”, and Android applications normally run entirely on a single thread by
default the “main thread”. But main thread is not some secret, special
thread. It's just a normal thread that you can also create with new Thread() code, which means it terminates
when its run() method return! Think of below example. ( Bir uygulamayı çalıştırdığımızda,
sistem bu uygulama için bir main thread yaratır. Bir Android uygulaması
genellikle single bir thread'de(main ui thread'de) çalışır. main thread'in hiç
bir ekstra özelliği yoktur, new Thread() diyerek yaratabileceğimiz normal bir
thread'dir. Yani bu haliyle main thread run() method'u return ettikten sonra
terminate olur(sonlanır, yok olur). Aşağıdaki örneği düşünelim, bu örnekteki
thread hemencecik çalışır run() method'u return edince de thread biter, yok
olur. )
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();
}
}
Now, let's apply this simple principle
to Android app. What would happen if an Android app is run on normal thread? A
thread called "main" or "UI" or whatever starts
application, and draws all UI. So, the first screen is displayed to users. So
what now? The main thread terminates? No, it shouldn’t. It should wait until
users do something, right? But how can we achieve this behavior? Well, we can
try with Object.wait() or Thread.sleep(). (Şimdi bir Android uygulamasındaki main thread'in de
yukarıdaki örnekteki gibi çalıştığını düşünelim, yani uygulamamızın normal bir
thread üzerinde çalıştığını düşünelim. Main thread çalışıp ui'ı çizdi, ilk ui
ekranı kullanıcıya gösterildi diyelim. Kullanıcıdan bir action bekleniyor
diyelim. Peki şu an main thread öldü mü sizce ne yapıyor? Hayır. Main thread,
kullanıcının bir şeyler yapmasını bekliyor.
Peki bu davranışı nasıl implement edebiliriz? Object.wait() veya
Thread().sleep() method'Unu çağırarak main thread'i bekletebiliriz. )
For example, main thread finishes its
initial job to display first screen, and sleeps. It awakes, which means
interrupted, when a new job to do is fetched. So far so good, but at this
moment we need a queue-like data structure to hold multiple jobs. Think about a
case when a user touches screen serially, and a task takes longer time to
finish. So, we need to have a data structure to hold jobs to be done in
first-in-first-out manner. Also, you may imagine, implementing ever-running-and-process-job-when-arrived
thread using interrupt is not easy, and leads to complex and often
unmaintainable code. We'd rather create a new mechanism for such purpose,
and that is what Looper is all about. ( Örneğin, main thread, ilk ekranı
gösterme işini bitirdi ve uyudu diyelim. Main thread, yapması gereken bir iş
geldiğinde uyanır; peki birden fazla iş gelirse ne olur? bu işleri bir kuyruğa
sokmamız gerekir yani main thread içerisinde queue'ya benzer bir data
structure'a ihtiyacımız vardır. Örneğin, kullanıcı ekrana art arda hızlıca
tıkladı, bir task'ın işinin bitmesi uzun zaman sürüyor diyelim, bu durumda
yapılacak işleri first-in first out olarak tutaacak bir data structure'a yani
queue'ya ihtiyacımız var. Sürekli çalışan ve gelen işleri yapan hiçbir zaman
terminate olan bir thread implement etmek kolay değildir, çok complex'dir ve bu
kodu sürdürmek çok zordur. Android böyle bir mekanizmayı bizim için zaten
implement etmiştir, bu amaçla kullanmak için Looper class'ını sunar bize. )
The official document of Looper class says, "Threads by default do not have a message
loop associated with them", and Looper is a class "used to run a
message loop for a thread". Now you can understand what it means. ( Looper class'ının resmi dökümanında
şu yazar : Default olarak, Thread'ler bir message loop'a(message queue gibi
düşün) sahip değildir. Looper class'ı
ise bize message loop'a(message queue'ya) sahip olan bir thread sağlar. )
Looper, MessageQueue'ya sahiptir.
Handler, çeşitli post(Runnable r) method'larından birini çağırarak ilişkili olduğu
Looper'daki message queue'ya yeni bir Message ekler.
My
last word is, so basically Looper is a class that is made to address a problem
that occurs in GUI framework. But this kind of needs also can happen in other
situation as well. Actually it is a pretty famous pattern for multi threads
application, and you can learn more about it in "Concurrent Programming in
Java" by Doug Lea(Especially, chapter 4.1.4 "Worker Threads"
would be helpful). Also, you can imagine this kind of mechanism is not unique
in Android framework, but all GUI frameworks may need somewhat similar to this.
You can find almost the same mechanism in Java Swing framework. ( Toparlayalım, Looper temel olarak
GUI framework'de olabilecek bir problemi çözmek için implement edilmiştir. Bu
çeşit ihtiyaçlar, başka durumlarda vardır. Aslında bu multithread uygulamalar
için kullanılan ünlü bir pattern'dır. Ayrıca bu mekanizma Android' özel değildir,
tüm GUI framework'leri buna benzer bir mekanizmaya ihtiyaç duyar, çünkü ekranda
bir şey gösterdikten sonra thread'in terminate olmaması çalışmaya devam etmesi
kuyruğa gelen işleri sırayla yapması için böyle bir mekanizmaya ihtiyaç vardır
graphical user interface'ler için. )
---
The
Looper
class is usually used in conjunction with a HandlerThread
(a subclass of Thread
). A Handler
is a utility class that facilitates interacting with a Looper
—mainly by posting messages and Runnable
objects to the thread's MessageQueue
. When a Handler
is created, it is bound to a specific Looper
(and associated thread and message queue). (Handler, Looper'daki MessageQueue'ya message ekler. Bir Handler
yaratıldığında belirli bir Looper ile ilişkilendirilir, ayrıca Looper'ın
MessageQueue'su ile de ilişkilendirilmiş olur, ayrıca looper ile
ilişkilendirilen Thread ile de ilişkilendirilmiş olur . Yani Handler şu 3 şeyle
ilişkilendirilmiş olur : Looper, MessageQueue, Thread. )
In typical usage,
you create and start a
HandlerThread
, then create a Handler
object (or objects) by which other threads can interact with the HandlerThread
instance. The Handler
must be created while running on the HandlerThread
, although once created there is no restriction on what threads
can use the Handler
's scheduling methods (post(Runnable)
, etc.)
The main thread (a.k.a. UI thread) in
an Android application is set up as a looper thread before your application is
created. (Genellikle şöyle kullanırız : Bir HandlerThread yaratırız
ve bu thread'i başlatırız. sonra bir Handler object yaratırız. Diğer thread'ler
HandlerThread object ile iletişim kurabilir. Handler, HandlerThread içerisinde
yaratılmak zorundadır; buna karşın Handler yaratıldıktan sonra Handler'ın
scheduling method'larını (post(Runnable) vs method'larını) her Thread
kullanabilir, bu konuda kısıtlama yoktur. Uygulamanızdaki main thread, looper
thread olarak set edilir. Bu paragraftaki şeyleri çok anlamasam da açıkladım
bunları tekrar incelemeliyim. )
----
AsynchronousAndroid.pdf kitabından :
http://files.dcarl.me/books/First_Collection_Programming_Books/Asynchronous%20Android%20%5BeBook%5D.pdf
https://github.com/steveliles/AsyncAndroid
Looper is a simple class that quite literally loops forever, waiting for
Messages to be added to its queue and dispatching them to target Handlers. Looper is an implementation of a
common UI programming concept known an Event
Loop. (Looper sonsuza kadar belirli bir döngüde çalışan basit bir
class'dır. İçerdiği kuyruğa mesaj gelmesini bekler, mesaj gelince bu mesajı
hedef Handler'a gönderir(dispatch). Looper class'ının yaptığı döngü budur işte.
Looper aslında Event Loop diye bilinen UI programlama konsepti'nin
implementation'ıdır. )
To set up a Looper thread, we need to invoke two static methods of Looper - prepare
and loop - from within the thread that will handle the message loop.( Looper object'i yaratmak için prepare() method'unu, çalışmaya
başlatmak için loop() method'Unu çağırırız. Looper'ın hangi thread ile ilişkili
olmasını istiyorsak, o thread'İn run() method'unun içerisinde çağırırız Looper.prepare() ve Looper.loop() method'larını. )
class SimpleLooper extends
Thread {
public void run() {
Looper.prepare();
Looper.loop();
}
}
That was easy; however, the SimpleLooper class defined here provides no way to add messages to its queue,
which is where Handler
comes in. ( SimpleLooper isimli bir thread tanımladık, bu thread
içerisinde bir looper tanımladık. Ancak bu looper'ın message queue'suna mesaj
eklemek için Handler'a ihtiyacımız var.
)
Handler serves
two purposes—to provide an interface to submit Messages to its Looper queue and to implement the callback for processing those Messages
when they are dispatched by the Looper. ( Handler'ın 2 amacı vardır :
- Looper'ın mesaj kuyruğuna mesaj eklemek
- Looper handler'a kuyruktaki bir mesajı gönderince handler bu
mesajı işlemek için bir callback implement eder, yani handler mesajları işler
diye anladım doğru mu emin değilim. )
To attach a Handler to SimpleLooper, we need to instantiate the Handler from within the same thread
that prepared the Looper. The Handler is associated with the main Looper by the super-class
constructor, which obtains the Looper using a static method. ( Yukarıda tanımladığımız
Looper ile bir Handler'ı ilişkilendirelim. Bunun için hem looper'ı hem de
handler'ı aynı thread içerisinde yaratırız. Yani, looper'ı yarattığımız thread
içerisinde, handler class'ından bir object yaratırız. Son cümleyi anlamadım. )
class SimpleLooper extends
Thread {
public Handler handler;
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}
Once started, the Looper thread will wait (Object.wait) inside Looper.loop()
for Messages to be added to
its queue.
When another thread adds a Message to the queue it will notify (Object.notify) the waiting thread, which will then dispatch the Message to its target Handler
by invoking the Handler's dispatchMessage method. ( Looper thread çalışmaya
başladıktan sonra Looper.loop() olduğu satırda mesaj gelmesini bekler. Başka
bir thread, bizim thread'imizin queue'suna bir mesaj ekleyince, thread'imizi
uyarır(notify). Bizim thread'imizin looper'ı gelen mesajı target handler'a
dispatch eder(hemencecik gönderir). )
The wait/notify mechanism is a very
efficient way of suspending activity on a particular thread while there is no
work for it to do, so that it doesn't waste CPU cycles, then resuming work once
there is something for it to do. (
wait/notify mekanizması, bir activity'yi belirli bir thread üzerinde yapacak
bir iş yokken bekletmek için kullanılan çok verimli bir mekanizmadır. Bu sayede
CPU cycle israfı önlenir. Yapacak bir iş gelince de activty thread üzerinde
çalışmaya devam eder. )
Because dispatchMessage
is invoked by the thread
running the message loop, we can send Messages to the Handler from any thread and they will always be dispatched by the message
loop thread as shown in the following diagram: (
Looper'ı çalışan thread'in dispatchMessage() method'u çağırıldığı için,
Handler'a herhangi bir thread'den mesaj gönderebiliriz. Aşağıdaki diagramı
bakınca daha iyi anlayacağız. Ortadaki thread bizim thread'imizdir. Soldaki ve
sağdaki thread'lerin handler'larının sendMessage()method'u çağırılarak bizim
thread'imizin message loop'una Runnable object veya Message ekler. Bizim
thread'imizin handler'ının dispatchMessage() method'u çağırılarak,
looper'ımızın handler'ımıza mesajı göndermesi sağlanır. )
We already saw that
we can create our own Looper
threads, but here's the fun
part and this may come as a surprise: the main thread is in fact a Looper thread, as we can see in the following stack trace: ( Main thread aslında looper thread'dir, aşağıdaki stack
trace'den de bunu anlayabilirsin. )
at example.handler.MyActivity.onCreate(Example1Activity.java:19)
at
android.app.Activity.performCreate(Activity.java:5206)
…
at
android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4898)
That's right!
Everything that happens in an Activity lifecycle callback method is running in a dispatchMessage call invoked by the main Looper! (main looper tarafından
çağırılan dispatchMessage( ) method'unda, bir activity'nin yaşam döngüsü
method'u çalışır. )
The interesting thing
to realize here is that we can send messages to the main thread from any other
thread (or even from the main thread itself) and in doing so, hand over work
from background threads to the main thread—for example, to have it update the
user interface with the results of background processing. ( Burada şunu fark edelim: Diğer thread'lerden veya main
thread'den main thread'e mesaj gönderebiliriz. Yani thread'ler arasında bir
iletişim kurarız bu mekanizmayla. Bu sayede arka planda çalışan thread'ler main
thread'e iş gönderip böylece ui'ı güncelleyebiliriz. )
Building responsive apps with Handler
The Handler class is fundamental to the infrastructure of Android
apps—together with Looper. It underpins everything that the main thread does—including the
invocation of the Activity
lifecycle methods. ( Handler class'ı Looper ile birlikte Android uygulamalarının altyapısının
temelini oluşturur. Handler class'ı main thread'in yaptığı işlerin temelini
oluşturur, activity'nin lifecycle method'larının çağırılmasının da temelini
oluşturur. )
While Looper takes care of dispatching work on its message-loop thread, Handler provides the means to add work to the message queue belonging to a
Looper. ( Looper'ın görevi mesaj
kuyruğundaki işleri Handler'a göndermektir(dispatch). Handler'ın görevi
Looper'ın mesaj kuyruğuna iş eklemektir. )
We can create a Handler to submit work to be processed on the main thread simply by
creating a new instance of Handler from an Activity
lifecycle method such as onCreate, shown as follows: ( Main
thread'de çalışmasını istediğimiz bir iş istiyoruz diyelim. Bunun için
activity'mizin lifecycle method'larından birinde mesela onCreate() method'unda
Handler class'ından bir object yaratırız. Handler object hangi thread
içerisinde yaratılırsa o thread ile
ilişkilendirilir, hatırla! Dolayısıyla main thread'de bir handler object
yarattığımız için handler'ımız main thread ile ve main looper ile ilişkilidir,
main looper'daki kuyruğa iş ekleyebilir. Bu 1. yoldur. )
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Handler handler = new
Handler();
// …
}
We could also be
explicit that we want to submit work to the main thread by passing a reference
to the main Looper
instance into the Handler constructor. ( Ayrıca, Handler'ın işi
main thread'e göndermesini de explicit bir şekilde belirtebiliriz. Handler'ın
constructor'ına main Looper instance'ı veririz. Artık bu handler işleri main
thread'e gönderecektir. Bu 2. yoldur. )
Handler handler = new
Handler(Looper.getMainLooper());
Exactly what we
mean by "work" can be described by subclasses of java.lang. Runnable, or instances
of android.os.Message.
We can post Runnables to a Handler
instance or send Messages to it, and it will add them to the
queue belonging to the associated Looper
instance. ( Mesaj kuyruğuna
ekleyeceğimiz object ya Runnable ya da Message class'ının instance'ıdır. Bir Handler'a Runnables veya Messages send ederiz, Handler da bunları
ilişkili olduğu Looper'ın mesaj kuyruğuna ekler. )
Scheduling work with post
We can post work to a
Handler
very simply, for example, by
creating an anonymous inner Runnable: ( Bir handler'a bir işi nasıl post ederiz? handler object'in
post method'unu çağırırız. post() method'una argument olarak yarattığımız
anonymous Runnable object'i veririz. )
handler.post(new Runnable(){
public void
run() {
//
do some work on the main thread.
}
});
The Looper instance to which the Handler is bound works its way through the queue, executing each Runnable as soon as possible. Posting with the post method simply adds a new Runnable at the end of the queue. (
Yukarıda kod çalışınca handler'ın ilişkili olduğu looper'ın kuyruğunun sonuna
bir Runnable object eklenir. )
If we want our Runnable to take priority over anything currently in the queue, we can post
it to the front of the queue ahead of existing work: (Aşağıdaki kod çalışınca handler'ın ilişkili olduğu looper'ın
kuyruğunun başına bir Runnable object eklenir. handler object'in postAtFrontOfQueue method'unu çağırılarak kuyruğa en öncelikli
bir iş eklemiş oluruz. . )
handler.postAtFrontOfQueue(new Runnable(){
public void
run() {
// do
some work on the main thread.
}
});
What if we want to
schedule some work in 10 seconds time? One way to do this would be to Thread.sleep the main thread for 10 seconds, but that would mean the main
thread can't do anything else in the meantime. Also, remember that we must
never block the main thread! The alternative is to post a delayed Runnable to the Handler. ( 10 saniye sonra bir iş
yapılmasını istiyoruz diyelim, bu durumda main thread'i uyutmayı deneyebiliriz
ancak böyle yaparsak main thread bu esnada hiçbir şey yapmaz, main thread'i
asla block etmemeliyiz. handler'ın postDelayed() method'u çağırılarak, handler'ın
ilişkili olduğu looper'ın kuyruğuna 10 sn bekledikten sonra bir Runnable object
eklenmesi sağlanır. )
handler.postDelayed(new
Runnable(){
public void run() {
// do some work on the main thread
//
in 10 seconds time
}
}, TimeUnit.SECONDS.toMillis(10)
);
We can still post additional work for execution in the
meantime, and our delayed Runnable instance will execute after the specified delay. Note that we're
using the TimeUnit class from the java.lang.concurrent
package to convert seconds to milliseconds. ( Runnable object 10 saniye sonra çalışacaktır. Bu örnekte concurrent
package'ını kullanarak saniyeleri milisaniyelere çevirdik. )
A further scheduling option for posted work is postAtTime, which schedules Runnable to execute at a particular time relative to the system uptime (how
long it has been since the system booted):
handler.postAtTime(new
Runnable(){
public void run() {
// … do some work on the main thread
}
}, SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(10));
Since postDelayed is implemented in terms of an offset from the SystemClock
uptime, it is usually easier to just use postDelayed. (postAtTime method'unu
kullanmaktasnsa postDelayed() method'Unu kullan postDelayed()method'U daha
basittir. )
As we'll see in the Issues section,
posting an anonymous Runnable makes for concise examples but when used with postDelayed or postAtTime requires care to avoid
potential resource leakage. (postDelayed ve postAtTime method'larını kullanırken çok dikkatli
olmalıyız çünkü bu method'ların yanlış kullanımı genellikle resource leakage'a
neden olur. )
Since we instantiated our Handler on the main thread, all
work submitted to it executes on the main thread. This means that we must not
submit long running operations to this particular Handler, but we can safely
interact with the user interface: (
Handler object'i main thread'de yarattığımız için Handler'a post edilen işler
main thread'de çalışır. Dolayısıyla bu Handler'a uzun süren işler post
etmemeliyiz, çünkü main thread'de uzun
süren işler yapıp main thread'i meşgul etmemeliyiz. Handler object'e post
edeceğimiz iş user interface'i değiştirebilir. )
handler.post(new Runnable(){
public void run() {
TextView text = (TextView)
findViewById(R.id.text);
text.setText("updated on the
UI thread ! ");
}
});
This applies
regardless of which thread posts the Runnable, which makes Handler an ideal way to send the results of work performed by other
threads to the main thread. ( Başka thread'ler, main
thread'e iş göndererek de ui güncellenebilir. Aşağıdaki kodu inceleyelim.
Handler object'i activity'nin onCreate() method'unda yaratmıştık. Dolayısıyla
bu handler object'in post method'una çağırarak handler'a post edeceğimiz işler
main thread'de yapılacaktır, dolayısıyla bu handler'a uzun süren bir iş post
etmememiz gerektiğii daha önce söylemiştik. Aşağıda bir thread object
tanımladık. Bu thread'in run() method'unda calculatePrime() method'unu
çağırdık, bu method'un return etmesi uzun sürecektir. Sonra handler object'in
post method'una bir Runnable object veririz, bu Runnable object'in run
method'unda textview'e hesapladığımız değeri set ederiz. En son bu thread'in
priority'isini set ederiz(thread.setPriority) ve thread'in çalışmasını başlatırız(thread.start()). Aşağıdaki kodda, background'da çalışan bir thread'de uzun
süren bir iş yapılmış, sonuç elde edilince handler'ın post method'u çağırılarak
main thread'in ilişkili olduğu looper'ın mesaj kuyruğuna iş eklenir, bu iş main
thread'de çalışıp textview'in içeriği set edilir. )
Thread thread = new Thread(){
public void run()
{
final
BigInteger result = calculatePrime(500);
handler.post(new
Runnable()
{
public
void run(){
TextView
text = (TextView) findViewById(R.id.text);
text.setText(result.toString());
}
});
}
};
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
If you start your own
threads for the background work, make sure to set the priority to Thread.MIN_PRIORITY to avoid
starving the main thread of CPU time. (
Arka planda çalışmak için bir thread başlatacağımız zaman bu thread'in
priority'sini Thread.MIN_PRIORITY olarak
set etmeliyiz. Aksi takdirde main thread CPU'dan yeteri kadar istifade
edemeyebilir. )
Handler is so
fundamental that it is integrated right into the View class hierarchy, so we can rewrite the last example as follows: ( Handler o kadar temel ve önemli bir kavramdır ki, View class
ile de entegre edilmiştir. Dolayısıyla biraz önceki örneği aşağıdaki şekilde de
yazabiliriz, yukarıdaki kod ve aşağıdaki kod aynı işi yapar. Önceki kodda, main
thread'de yarattığmız handler object'in post method'unu çağırmıştık, post
method'unun aldığı Runnable object'İn run() method'unda textview'i set
etmiştik. Aşağıda ise, önce textview object'i elde ettik. Sonra yeni bir
thread'İn run() method'Unda handler'ın değil textview'in post method'unu
çağırdık. Yani textview object'e handler object gibi davrandık bu örnekte. Yani
bir handler ile yapabileceğin bir işlemi textview ile dde yapabiliyorsun
demekki. )
final TextView text = (TextView)
findViewById(R.id.text);
Thread thread = new Thread()
{
public void run()
{
final BigInteger result = calculatePrime(500);
text.post(new Runnable()
{
public void run() {
text.setText(result.toString());
}
});
}
};
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
Bu iki kod arasındaki farkı
aşağıdaki resimden de inceleyebilirsin:
For comparison, both of the previous examples are roughly
equivalent to the following AsyncTask code: ( Yukarıdaki 2 kodun
yaptığı işin aynısını, aşağıdaki kod AsyncTask kullanarak yapabilir.
AsyncTask'ın doInBackGround()method'unda uzun süren işlemler yapılır,m bu
method return ettikten sonra onPostExecute()method'u çağırılarak textview set
edilir. Yani AsyncTask kullanarak ui'ı güncelleyebiliriz. )
new AsyncTask<Void, Void, BigInteger>()
{
public BigInteger doInBackground(Void… params)
{
return calculatePrime(500);
}
public void onPostExecute(BigInteger result)
{
TextView text = (TextView) findViewById(R.id.text);
text.setText(result.toString());
}
}.execute();
When
writing code in an Activity class, there is an alternative way of executing a Runnable on the main
thread using the runOnUiThread
method of Activity. If invoked from a background thread, the Runnable will be posted to a Handler instance attached to the main thread. If invoked from the main
thread, the Runnable
will be executed immediately. ( Main thread'de bir runnable object çalıştırmanın farklı bir
yolu da runOnUiThread()
method'unu kullanmaktır. Örneğin background'da çalışan bir thread yaratacağız
diyelim, bu thread'den ui'i değiştirebilmek istiyoruz, ancak daha önce de
öğrendiğimiz gibi background thread'ler yani non-ui thread'lerden ui
değiştirilemez. UI sadece main thread(ui thread) içerisinde değiştirilebilir.
Ancak bu kuralı esnetmenin bir yolu vardır. Bir background
thread içerisinden runOnUiThread() method'unu çağırırız, bu method'unda aldığı
argument olan Runnable object'İn run method'unda ui'ı değiştirebiliriz.
Yarattıımız background thread arka planda çalışır, sadece runOnUiThread()
method'unun aldığı Runnnable object'in run() method'unda ui thread'de çalışır.
Aşağıdaki koddaki gibi, runOnUiThread() method'unu bir background thread içerisinde
çağırırsak bu method'Unda aldığı argument olan Runnable object, maiin thread'İn
ilişkili olduğu Handler object'e post edilir. Eğer runOnUiThread() method'unu main thread içerisinde çağırırsak,
Runnnable anında execute edilir. )
public class TestActivity extends Activity {
Button
btn;
int
i = 0;
@Override
public
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn
= (Button)findViewById(R.id.btn);
btn.setOnClickListener(new
View.OnClickListener() {
@Override
public
void onClick(View v)
{
runThread();
}
});
}
private
void runThread()
{
new Thread() {
public
void run() {
while
(i++ < 1000) {
try
{
runOnUiThread(new
Runnable()
{
@Override
public
void run()
{
btn.setText("#"
+ i);
}
});
Thread.sleep(300);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
When you explicitly spawn a
new thread to do work in the background, this code is not run on the UIThread.
So what happens if this background thread needs to do something that changes
the UI? This is what the
runOnUiThread
is for. runOnUiThread
provides these background threads the ability
to execute code that can modify the UI. They do this by putting the
UI-modifying code in a Runnable object and passing it to the runOnUiThread
method.
non-UI
and UI thread'ler arasında iletişim kurmak için, runOnUiThread() method'unu
kullanabiliriz. Bu method, activity class'ında tanımlıdır. Dolayısıyla activity
class'ında kodyazarken kullanabiliriz bu method'u. Bir non-UI thread, UI
thread'de çalışmasını istediği bir Runnable olduğunda ,ilgili Runnable object'i
runOnUiThread() method'una verir. Bu runnable object, ui thread'in handler'ına
post edilir, dolayısıyla ui thread'İn looper'ının event kuyruğuna eklenir.
Sırası gelince kuyruktan çıkartılıp çalıştırılan bu Runnable iş, ui'ı
değiştirir.
runOnUiThread()
method'u, UI thread'de çağırılırsa, main looper'ın kuyruğuna mesaj ekleme falan
yapmaz , ilgili Runnable'ı anında çalıştırır. Yani bu sayede, bu method'u hangi
thread'de çağırdığımızı check etmemize gerek kalmaz, ui thread'de de nonui
thread'de çağırabiliriz bu method'u. Birisinde hemencecik çalıştırır diğerinde
ise kuyruğa ekler.
Canceling a pending Runnable
We can cancel a
pending operation by removing a posted Runnable callback from the queue. (
Kuyruğa eklediğimiz bir Runnable iş gerçekleşmeden önce bu Runnable'ı iptal
edebiliriz yani kuyruktan çıkartabiliriz. Ama bu Runnable iş çalışmaya
başladıysa, çalışmanın ortasında bu işi iptal edemeyiz. )
final Runnable runnable = new Runnable(){
public void run() {
// … do
some work
}
};
handler.postDelayed(runnable,
TimeUnit.SECONDS.toMillis(10));
Button cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new OnClickListener(){
public void
onClick(View v) {
handler.removeCallbacks(runnable);
}
});
Notice that in order to be able to specify what to remove, we
must keep a reference to the Runnable instance, and that cancellation applies only to pending tasks—it
does not attempt to stop a Runnable that is already mid-execution. ( Kuyruktaki bir Runnable'I kuyruktan silmek, çalışmadan iptal
etmek istiyorsak, bu Runnable object refer eden bir reference variable'a sahip
olmak zorundayız. Yukarıdaki örnekte bir Runnable object yarattık, bu Runnnable
object'e runnable isimli bir reference variable'ın refer etmesini sağladık.
Sonra daha önceden yaratılmış olan handler object'İn postDelayed() method'unu
çağırıp, runnable'ın 10 saniye sonra çağırılmasını istedik. Ancak 10 sn.
geçmeden yani Runnable henüz çalışmadan,
tanımladığımız butona tıklarsak handler object'in removeCallbacks(runnable) method'una argument olarak runnnable object'i verip çağırdık
diyelim:
handler.removeCallbacks(runnable);
Bu durumda Runnable'In çalışması iptal edilir, kuyruktan silinir. )
Scheduling work with send
When we post a Runnable, we can—as seen in the previous examples—define the work at the
call site with an anonymous Runnable. As such, the Handler does not know in advance what kind of work it might be asked to
perform. ( Önceki örneklerde bir
background thread'de main thread'İn handler'ına anonymous runnable object
yaratıp post ettik handler.post(runnable,..) method'unu çağırarak.
Handler ne çeşit bir iş yaptığını önceden bilmiyordu bu örneklerde. )
If we often need to
perform the same work from different call sites we could define a static or
top-level Runnable
class that we can instantiate
from different call sites. ( Eğer handler'a sürekli
olarak aynı işi göndereceksek, static bir Runnable class tanımlamalıyız. Farklı
thread'lerde bu runnable class'dan bir object yaratıp handler'a post ederiz.
tam anlamadım aslında bu paragrafı. )
Alternatively, we can turn the approach on its head by
sending Messages to a Handler, and defining the Handler
to react appropriately to
different Messages. ( Alternatif olarak,
bir handler'a Runnable göndermek yerine Message göndeririz. Handler'ın gelen
Mesajlara göre farklı davranmasını implement ederiz. )
Taking a simple
example, let's say we want our Handler to display hello or goodbye, depending on what type of Message it receives. To do that, we'll extend Handler and override its handleMessage method. ( Aşağıdaki tanımladığımız
Handler, aldığı Message'ın çeşidine göre(Message object'in what property'sine
göre) ekranda hello veya goodbye gösterir. Message object'in what property'si 0
ise hello gösterir, 1 ise goodbye gösterir. Burada Handler class'ının
handleMessage() method'unu override ettik, bu method'un aldığı Message object'e
göre bir davranış gösterdik. )
public static class
SpeakHandler extends Handler {
public static final int SAY_HELLO = 0;
public static final int SAY_BYE = 1;
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case SAY_HELLO:
sayWord("hello"); break;
case SAY_BYE:
sayWord("goodbye"); break;
default:
super.handleMessage(msg);
}
}
private void sayWord(String word) { … }
}
You'll notice that we
defined SpeakHandler class as a static class. Subclasses
of Handler should always be declared as top-level or
static classes to avoid inadvertent memory leaks! A top
level class is a class that is not a nested class. A nested class is any class
whose declaration occurs within the body of another class or interface.
(SpeakHandler class'ını static olarak
tanımladığımıza dikkat! Handler'ın subclass'ları top-level class olarak veya
static class olarak tanımlanmalıdır. Top level class, nested olmayan class
demektir. Nested class, bir class içerisinde tanımlanan class'a denir. Yani Bir
nonstatic inner class olarak bir handler class tanımlamamalıyız sakın ha!! Ya
nested olmayan bir class olarak ya da static bir class olarak tanımlamalıyız.
Böylece yanlışlıkla bir memory leak gerçekleşmesini önlemiş oluruz. )
To attach an instance
of our Handler
to the main thread, we simply
instantiate it from any method which runs on the main thread: ( Main thread'e bir handler bağlamak için, main thread'de
çalışan bir method'da bir handler object yaratırız. )
private
Handler handler;
protected void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new SpeakHandler();
// …
}
Remember that we can
send Messages to this Handler
from any thread, and they will
be processed by the main thread. We send Messages to our Handler as shown: ( Bu handler'a herhangi bir
thread'den mesaj gönderebiliriz. Bu handler'a gönderilen mesajlar, main
thread'de çalıştırılacaktır. Bu handler'a handler'ın send() method'larından birini
çağırarak bir mesaj gönderebiliriz, aşağıdaki
gibi: )
handler.sendEmptyMessage(SAY_HELLO);
Messages may also
carry an object payload as context for the execution of the message. Let's
extend our example to allow our Handler to say any word that the message sender wants: ( Handler'a gönderdiğimiz Message object, payload da içerebilir.
Message object, obj isimli property'si, Message object'in içerdiği
payload'a(object'e) refer eder. Az önce tanımladığımız SpeakHandler class'ını biraz daha
geliştirelim. Bu örnekte farklı olarak, SpeakHandler class'ında SAY_WORD isimli bir int variable
tanımlamıştır ve handleMessage() method'undaki switch statement'a yeni bir case
eklenmiştir, bu case durumda message object'in içerdiği payload(object) elde
edilip String'e çevrilip ekranda gösterilir. )
public
static class SpeakHandler extends Handler {
public static final int SAY_HELLO = 0;
public static final int SAY_BYE = 1;
public static final int SAY_WORD = 2;
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case SAY_HELLO:
sayWord("hello");
break;
case SAY_BYE:
sayWord("goodbye");
break;
case SAY_WORD:
sayWord((String)msg.obj);
break;
default:
super.handleMessage(msg);
}
}
private void sayWord(String word) { … }
}
Within our handleMessage method, we can access the payload of the Message directly by accessing the public obj property. ( Yukarıdaki kodda, handleMessage(Message msg) method'unda, message
object'in obj isimli public property'sine erişerek mesajın payload'una erişmiş
oluruz. )
The Message payload can be set easily via
alternative static obtain methods. ( Handler
object'e göndereceğimiz Message object'i yaratma ve set etme ve gönderme işini
aşağıdaki kod yapar. static obtain() method'larının varyasyonlarından birini
çağırarak mesaja payload koyabiliriz aşağıdaki gibi.)
handler.sendMessage( Message.obtain(handler,
SpeakHandler.SAY_WORD, "tada!"));
While it should be quite
clear what this code is doing, you might be wondering why we didn't create a
new instance of Message
by invoking its constructor,
and instead invoked its static method obtain. ( Peki neden Message
class'ından yeni bir object yaratmak için Message class'ının bir
constructor'ını çağırmadık, bunun yerine static bir method olan obtain() method'unu çağırdık? )
The reason is
efficiency. Messages are used only briefly—we instantiate, dispatch, handle,
and then discard them. So if we create new instances for each we are creating
work for the garbage collector. (
Daha verimli bir kod yazmak için böyle yaptık. Çünkü Message object'ler çok
küçük işler için kullanılırlar : yaratılırlar, gönderilirler, handle edilirler,
çöp olurlar. Dolayısıyla contructor'ını kullanarak bir Message object
yaratırsak ki bu mümkündür, garbage collector'un bunu temizlemesi gerekir.
Garbage collector'a iş çıkartmış oluruz yani constructor'ı kullanırsak. )
Garbage collection is expensive, and the Android platform
goes out of its way to minimize object allocation whenever it can. While we can
instantiate a new Message
object if we wish, the
recommended approach is to obtain one, which re-uses Message instances from a pool and cuts down on garbage collection
overhead. ( Garbage collection
pahalıdır, mümkün olduğunca object yaratma işini minimize etmek gereklidir
dolayısıyla. Message() constructor'ı kullanarak da bir Message object yaratmak
mümkün olmasına rağmen, tavsiye edilen yol değildir. obtain() method'unu
kullandığımızda, bir havuzdaki varolan bir Message object tekrar tekrar
kullanılabilecektir, yani sıfırdan bir object yaratmamış olacağız, bu sayede
Garbage collector'i de ekstra bir zahmetten kurtarmış olacağız. )
Just as we can
schedule Runnables with the variants of the post method, we can schedule
Messages with variants of send. ( Runnable object'leri
schedule etmek için gerekli post method'larını daha önce öğrenmiştik. Message
object'leri schedule etmek için gerekli send method'larını ise aşağıdadır: )
handler.sendMessageAtFrontOfQueue(msg);
handler.sendMessageAtTime(msg,
time);
handler.sendMessageDelayed(msg,
delay);
There are also
empty-message variants for convenience, when we don't have a payload. ( Paylod'a sahip olmayan bir mesaj (Boş mesaj) göndermek için
ise aşağıdaki method'ları çağırırız: )
handler.sendEmptyMessageAtTime(what, time);
handler.sendEmptyMessageDelayed(what,
delay);
Canceling pending Messages
Canceling sent Messages is also possible, and actually easier
than canceling posted Runnables because we don't have to keep a reference to
the Messages that we might want to cancel—instead we can just cancel Messages
by their what value. ( Daha önce post
edilmiş olan Runnable'ları iptal etmeyi öğrenmiştik, Runnable object'e refer
eden reference variable'ı tutmamız gerekiyordu bunun için. Daha önce send
edilmiş olan Message'ları iptal etmek de mümkündür, hatta daha kolaydır, çünkü
Message object'e refer eden reference variable'ı tutmaya gerek yoktur, Message
object'in sadece what değerini söyleyerek bir Mesajı iptal edebiliriz aşağıdaki
gibi: )
handler.removeMessages(SpeakHandler.SAY_WORD);
Note that just as with posted Runnables, Message cancellation only removes
pending operations from the queue—it does not attempt to stop an operation
which is already being executed. ( Runnable
object'leri iptal etmek konusunda da söylemiştik tekrar söyleyelim, Message
iptali sadece bekleyen(pending) Message'ları kuyruktan silmeyi sağlar.
Çalıştırılmakta olan bir Message'ı durdurmaya çalışmaz. )
Composition versus Inheritance
Bu konuyu tam anlamasam da açıklamaya çalıştım.
Önce Handler.Callback nested interface'ini implement eden
Speaker isimli bir class tanımladık.
Sonra
aynı işi yapmak için 2 class tanımladık: Handler'ı extend eden SpeakHandler
isimli bir class ve Handler.Callback nested interface'ini implement eden
Speaker isimli bir class.
Önceki
yazdığımız class bizim işimizi görüyordu, neden aynı işi 2 class'a paylaştırıp
yazdık anlamadım. Emin değilim ama nedeni şu olabilir : 1. class'da default
durumda handleMessage() method'u sadece false return ediyor, halbuki biz Handle
class'ının kendi handleMessage() method'unu çağırmak isityoruz default durumda
bunu da ancak Handler class'ının bir subclass'ında yaparız. Dolayısıyla işin
yapabildiğimiz kısmını Callback class'ında yapıyoruz böylece composition'dan
istifade etmiş oluyoruz çünkü composition inheritance'dan daha verimlidir diye
genel bir görüş vardır.İşin kalan kısmını ise inheritance ile yapıyoruz.
Nested classes
|
|
interface |
Callback interface you can use when
instantiating a Handler to avoid having to
implement your own subclass of
Handler.
|
Handler.Callback : Callback
interface you can use when instantiating a Handler to avoid having to implement
your own subclass of Handler.( Handler class'ının
bir subclass'ını implement etmek yerine callback isimli bu interface'i
kullanabiliriz. )
So far we subclassed Handler
to override its handleMessage
method, but that isn't our only option. We can
prefer composition over inheritance by passing an instance of Handler.Callback during Handler construction. ( Şimdiye kadarki örneklerde, handleMessage() method'unu override etmek için Handler class'ını extend ettik
yani inheritance kullandık. Ancak bu bizim tek seçeneğimiz değildir, başka bir
seçeneğimiz daha vardır o da composition'dır.
Handler class'ı, Callback isimli bir nested class içerir. Handler.Callback class'ı yani Handler class'Inın içerisindeki
Callback isimli nested class handleMessage() abstract method'unu içerir. Yani Handler.Callback
class'ını implement eden bir class tanımlarsak, handleMessage() method'unu override etmek zorundayız. Yani aslında Callback
class'ı bir interface'dir, bir abstract class'dır. Aşağıdaki örnekte Speaker isimli bir static class
Handler.Callback interface'ini implement eder, 3 tane int variable tanımlar, handleMessage() method'unu
override eder. handleMessage() method'unun aldığı Message object'in what property'sinin
değerine göre ekranda hello, goodbye, veya message object ile gönderilen
payload'u gösterir. Handler class'ındaki handleMessage() method'u void return eder,
Handler.Callback
class'ındaki handleMessage() method'u ise boolean return eder. Yukarıda Handler class'ını extend eden bir class tanımlamıştık o class ve
aşağıdaki class'ı tanımı arasında 2 fark vardır aşağıdaki Handler.Callback'i
implement eder ve handleMessage() boolean return eder. Yukarıdaki class'dan bir Handler object
şöyle yaratabiliyorduk :
SpeakHandler s = new
SpeakHandler();
Aşağıdaki class'dan ise bir
Handler object şöyle yaratabiliriz :
Handler handler = new Handler(new Speaker()); )
public static class Speaker implements
Handler.Callback {
public static final int SAY_HELLO = 0;
public static final int SAY_BYE = 1;
public static final int SAY_WORD = 2;
@Override
public boolean handleMessage(Message msg) {
switch(msg.what) {
case SAY_HELLO:
sayWord("hello"); break;
case SAY_BYE:
sayWord("goodbye"); break;
case SAY_WORD:
sayWord((String)msg.obj); break;
default:
return false;
}
return true;
}
private void sayWord(String word) { … }
}
Notice that the signature of handleMessage
is slightly different here—we must return a boolean
indicating whether or not the Message was
handled. To create a Handler that uses our Callback,
simply pass the Callback during Handler
construction. (handleMessage()
method'u boolean return eder, bu değer Message'ın handle edilip edilmediğini
gösteri, yani ekranda bir yazı gösterileceği durumlarda bu method true return
eder. Yukarıda tanımladığımız callback class'ını kullanan bir Handler yaratmak
için Handler class'ının constructor'ına, Callback isimli class'dan yarattığmız
object'i veririz, aşağıdaki koddaki gibi. )
Handler
handler = new Handler(new Speaker());
If
we return false from the handleMessage
method of our Callback,
the Handler will invoke its own handleMessage
method, so we could choose to use a combination of inheritance
and composition to implement default behavior in a Handler
subclass, and mix in special behavior by passing in an instance
of Handler.Callback. ( Callback class'ının handleMessage() method'u false return ederse, Handler
kendi handleMessage() method'unu çağıracaktır. Dolayısıyla inheritance ve
composition'ın kombinasyonunu kullanabiliriz; default behaviour'ı yani
handler'ın kendi handleMessage() method'unu çağırması durumunu Handler'ın
subclass'ında implement ederiz, özel durumları yani message object'in what
değeri şöyle gelirse şöyle yap gibi durumları ise Handler.Callback class'ında
yapıp bu class'ın bir instance'ını Handler subclass'ının constructor'ına
veririz. Handler subclass'ının constructor'ı argument olarak Callback object
alıp super(callback) 'i çağırır. )
public static class SpeakHandler extends Handler
{
public static final int SAY_HELLO = 0;
public static final int SAY_BYE = 1;
public SpeakHandler(Callback callback) {
super(callback);
}
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
case SAY_HELLO:
sayWord("hello"); break;
case SAY_BYE:
sayWord("goodbye"); break;
default:
super.handleMessage(msg);
}
}
private void sayWord(String word) { … }
}
public static class Speaker implements Handler.Callback {
public static final int SAY_WORD = 2;
@Override
public boolean handleMessage(Message msg) {
if (msg.what == SAY_WORD) {
sayWord((String)msg.obj);
return true;
}
return false;
}
private void sayWord(String word) { … }
}
SpeakHandler isimli
Handler subclass'ından aşağıdaki gibi bir object yaratırız. Sonra bu Handler'a
bir message object yaratıp göndeririz.
Handler h = new
SpeakHandler(new Speaker());
h.sendMessage(Message.obtain(handler,
Speaker.SAY_WORD, "!?"));
With SpeakHandler set up like this, we can easily send Messages from any thread to
update the user interface. Sending from a background thread or the main thread
itself is exactly the same—just obtain a Message
and send it via SpeakHandler. ( SpeakHandler class'ını bu
şekilde tanımladıktan sonra, herhangi bir thread'den bu handler'a message
göndererek ui'ı güncelleyebiliriz. Background thread'den veya main thread'den
göndermek aynıdır : bir message object elde edilip SpeakHandler'a gönderilir. )
Multithreaded example
Let's extend our example to bind the app to a network socket
and echo lines of text it receives from the socket to the screen. Listening for
lines of text from the network socket is a blocking operation, so we must not
do it from the main thread. (Yukarıdaki
örneğimizi biraz daha geliştirelim. Aşağıda bir Thread class'ı tanımladık.
Thread'de bir server çalıştıralım ve client server'a bir text gönderince bunu
ekrana yazdıralım.Bir network socket'den text dinlemek(text gelmesini beklemek)
blocking operation'dır, dolayısıyla bunu main thread'de yapmamalıyız. )
We'll start a background thread, then bind the server socket
and wait for a client to connect over the network. When this background thread
receives text from the socket, it will send it in a Message to the SpeakHandler instance,
which will update the user interface on the main thread. ( Arka planda bir thread başlatacağız, bunun için aşağıda bir
class tanımladık bu class'dan bir object yaratılıp bu object'in start()
method'u çağırılmalıdır. Bu class'da,
server socket'i 4444.port'a bind ettik(bağladık), client'ın bu server'a
bağlanmasını bekledik. Bu background thread, socket'den bir text alınca, bu
text'i bir Message object içerisinde, SpeakHandler
isimli Handler object'e gönderir. Bu handler da main thread'deki ui'ı
günceller. )
static class Parrot extends Thread {
private Handler handler;
private InetAddress
address;
private ServerSocket server;
public Parrot(InetAddress
address, Handler handler)
{
this.handler = handler;
this.address
= address;
setPriority(Thread.MIN_PRIORITY);
}
public void run() {
try {
server = new ServerSocket(4444, 1, address);
while
(true) {
Socket client = server.accept();
handler.sendMessage(
Message.obtain(handler,
SpeakHandler.SAY_HELLO) );
BufferedReader in = new BufferedReader( new InputStreamReader( client.getInputStream()));
String word;
while (!"bye".equals(word = in.readLine())) {
handler.sendMessage( Message.obtain(handler, SpeakHandler.SAY_WORD,
word));
}
client.close();
handler.sendMessage(
Message.obtain(handler,SpeakHandler.SAY_BYE));
}
} catch (Exception exc) {
Log.e(TAG, exc.getMessage(), exc);
}
}
Yukarıda tanımladığımız Thread class'ında 3 tane variable
tanımladık:
private Handler handler;
private InetAddress
address;
private ServerSocket server;
Thread class'ımızın constructor'ında bu variable'lardan
ikisini set ettik. Yani thread class'ından bir object yaratılırken
constructor'a handler ve InetAddress object
verilmek zorundadır.
public Parrot(InetAddress address, Handler handler)
{
this.handler = handler;
this.address
= address;
setPriority(Thread.MIN_PRIORITY);
}
Let's have a quick look at the key
elements of the run method. First, we
bind a socket to port 4444 (any port higher than 1024 will do) on the given
address, which allows us to listen for incoming connections. The second
parameter says we're only allowing one connection at a time: (Thread class'ımızın run() method'una bakalım.3.
argument'deki adresin 4444.port'una bir socket bind ederiz. Böylece bu adresin
4444. portunda bağlantı kurulmasını bekleriz. ServerSocket constructor'ının
aldığı 2.parametre, aynı anda kaç tane client'ın server'a bağlanabileceğini
belirtir. )
server = new
ServerSocket(4444, 1, address);
We
loop forever, or until an exception is thrown. Inside the loop we wait for a
client to connect: (while(true) döngüsü sonsuza
kadar çalışır, tabi bir exception olmadığı sürece. Döngü içerisinde bir
client'ın server'a bağlanmasını bekleriz server.accept() ile.
)
Socket
client = server.accept();
The
accept method blocks, so our background
thread is suspended until a client makes a connection. As soon as a client
connects, we send a SAY_HELLO message
to our Handler, so the main
thread will update the user interface for us: (
accept method'u blocking bir operation'dır, return edene kadar kod bu satırda
bekler, yani runtime bu kod bu satırda client bağlanana kadar bekler.
Dolayısıyla bu thread bir client bağlanana kadar bu satırda bekler. Bir client
bağlanınca Handler'a SAY_HELLO
mesajı gönderilerek main thread'in ui'ı güncellemesi sağlanır. )
handler.sendMessage( Message.obtain(handler, SpeakHandler.SAY_HELLO));
Next,
we wrap a buffering reader around our socket's input stream, and loop on its readLine
method, which blocks until a line of text is available. ( Bağlanan client'dan bir text gelene kadar aşağıdaki
satırda bekleriz, client bir text gönderince bu text okunur, "bye"
değilse aşağıdaki while döngüsünde kalır.
)
BufferedReader in = new
BufferedReader( new InputStreamReader( client.getInputStream()));
String word;
while
(!"bye".equals(word = in.readLine()))
{
When we receive some
text from the socket, we send it in a Message to our Handler. ( client'dan gelen text
okunup word isimli variable'a koyuldu, bu text, handler'a gönderilir. )
handler.sendMessage(
Message.obtain(handler, SpeakHandler.SAY_WORD, word));
If we receive bye, we'll break out of the loop, disconnect this client, and say
goodbye: ( client'dan gelen text "bye"
ise, loop'dan çıkılır, client ile bağlantı koparılır, handler'a SpeakHandler.SAY_BYE tipinde
birMessage object gönderilir. Message.obtain() method'unun aldığı 2. parametre,
Message object'in what variable'ını set eder, 3.parametre ise Message object'in
içerdiği obj isimli object'i set eder. )
client.close();
handler.sendMessage( Message.obtain(handler,
SpeakHandler.SAY_BYE));
To obtain the address
of the device, we can use the Wi-Fi service. We'll need to convert the value we
get from the Wi-Fi service to an instance of the InetAddress class, elided here for brevity. ( anlamadım şu an için önemli değil zaten. )
private InetAddress getAddress()
{
WifiManager wm =
(WifiManager)getSystemService(WIFI_SERVICE);
return
asInetAddress(wm.getConnectionInfo().getIpAddress());
}
Binding the socket and using the Wi-Fi service requires
permissions to be requested in the Android manifest: ( Socket'i bind etmek ve WIFI service'i kullanmak için aşağıdaki
izinleri almak gerekir. )
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE"/>
Finally we need to create and start Parrot in a suitable Activity lifecycle method, for
example, onResume. To make it easy to connect, we'll display the address and port
on the screen. ( Parrot'ı Activity yaşam
döngüsündeki uygun bir method içerisinde yaratmalı ve başlatmalıyız, örneğin
onResume() method'unda yapabiliriz bunu. parrot daha önce yaratılmış mı diye
check ederiz, yaratılmamışsa, önce getAddress() metod'unu çağırarak cihazımızın
bağlı olduğu internetin ip adresini elde ederiz. Sonra Parrot constructor'a bu
adresi ve handler object'i vererek Parrot isimli thread class'dan bir object
yaratırız, sonra bu thread'i başlatırız start() method'unu çağırarak. Sonra
ekrndaki textviewde ip adresini ve portu gösteririz. )
TextView view = (TextView)
findViewById(R.id.speak);
if (parrot == null) {
InetAddress address = getAddress();
parrot = new Parrot(address, handler);
parrot.start();
view.setText("telnet " + address.getHostAddress() +
" 4444");
}
We should also remove pending messages in onPause, and disconnect the
socket if the Activity is finishing—the complete code is available for download from the
accompanying website. ( Pending mesajları
onPause() method'unda silebiliriz, activity yok edilirken socket'le bağlantıyı
koparabiliriz. Bu kodun source kodunun tamamı linkteki ch3. ex3 bölümünde
mevcuttur : https://github.com/steveliles/AsyncAndroid )
When the app starts, you'll see a message on the device
screen like telnet 192.168.0.4 4444. To connect and send messages to
your device's screen, open the command prompt on your development computer and
copy the text from your device screen to the command prompt. ( Uygulama başlatıldığnda ekranda şunu göreceğiz : telnet
192.168.0.4 4444 Uygulamaya mesaj gönderip bu mesajın ekranda gözükmesi
için, komut satırı açalım, şu komutu girelim: telnet 192.168.0.4 4444 )
You should see the following output: ( Komut satırında aşağıdaki output'u görürüz, bilgisayarımız'dan
telefona bağlandık demektir bu. )
Trying 192.168.0.4...
Connected to 192.168.0.4.
Escape
character is '^]'.
Congratulations! You're connected. Enter some words in the
command prompt and they'll appear on your device screen. Enter bye to disconnect. ( Komut
satırına bir şeyler yazıp enter'a basınca telefonun ekranında bu yazı
gösterilecektir. Bağlantıyı koparmak için bye yazmalısınız. )
Sending Messages versus posting Runnables
It is worth spending a few moments to consider the difference
between posting Runnables and sending Messages. (
Handler'a Runnable post edebiliriz veya Message send edebiliriz demiştik. Peki
bu ikisi arasındaki fark nedir? )
The runtime difference mostly comes down to efficiency.
Creating new instances of Runnable each time we want our Handler to do something adds garbage collection overhead, while sending
messages re-uses Message instances, which are sourced from an application-wide pool. ( Runtime'da verimlilik açısından fark vardır. Handler'a vermek
için Runnable instance yaratırsak, garbage collection'a iş çıkartmış oluruz.
Message göndermeyi seçersek, Message object'ler tekrar tekrar kullanılacağı
için yeni object yaratılmaz dolayısıyla garbage collection'a ekstra iş çıkmaz,.
Bu yüzden Message kullanmak daha verimlidir. )
The difference at development time is between allowing code
at the call site to specify arbitrary work for the Handler to do (Runnable.run) potentially
spreading similar code throughout the codebase, versus the Handler defining the work it is
prepared to do in a single place (Handler.handleMessage). ( Geliştirme süreleri
farklıdır. Handler'ın bağlı olduğu thread'de yapılacak bir iş belirtmek
istediğimiz Runnable yazarız. Veya Handler'a bir mesaj gönderip bu mesaj ile ne
yapılacağına Handler'ın handleMessage() method'unda karar veririz. )
For prototyping and small one-offs, posting Runnables is
quick and easy, while the advantages of sending Messages tend to grow with the
size of the application. It should be said that Message
sending is more "The Android Way", and is
used throughout the platform to keep garbage to a minimum and apps running
smoothly. ( Küçük ve tek seferlik
işler için Runnable'ı kullanmak hızlı ve kolay sonuç verir. Ancak uygulamanın
size'ı büyüdükçe Message send etmenin avantajları artar. Message kullanmak
Android için daha uygundur, ekstra çöp çıkartmadığı için uygulamalar daha hızlı
çalışır.)
Building responsive apps with HandlerThread
So far we only really considered Handler as a way to request work
be performed on the main thread—we've submitted work from the main thread to itself,
and from a background thread to the main thread. ( Şimdiye
kadarki gördüğümüz örneklerde, arka planda çalışan thread'ler main thread'e iş
gönderiyordu veya main thread'de çalışan thread'ler main thread'e iş
gönderiyordu. Handler main thread'de yaratılıyordu dolayısıyla handler main
thread'e iş göndermek için kullanılıyordu. )
In fact we can bind Handlers to any thread we create, and in
doing so allow any thread to submit work for another thread to execute. For
example, we can submit work from the main thread to a background thread or from
one background thread to another. ( Aslında
Handler'ı sadece main thread ile değil başka thread'ler ile de
ilişkilendirebiliriz, diğer bir deyişle bir handler'ı bir background thread'e
bağlayabiliriz, bu handler'ı kullanarak bu background thread'e diğer thread'ler
iş gönderebilir. Örneğin, bir background thread'den bir main thread 'e iş
gönderebiliriz, Bir main thread'den bir background thread'e iş gönderebiliriz,
bir background thread'den başka bir background thread'e iş gönderebiliriz. )
We saw one way of setting up a Looper thread with a Handler in the Understanding Looper section earlier in this
chapter, but there's an easier way using a class provided by the SDK for
exactly this purpose, android.os.HandlerThread. ( Yukarıdaki Understanding Looper başlığı
altında, bir thread'in içerisinde Looper yaratmayı öğrenmiştik. Ancak bunun
daha kolay bir yolu vardır, o da Android SDK'nın bize sağladığı android.os.HandlerThread class'ını kullanmaktır.
Normal bir thread'in default olarak bir looper'ı yoktur, istersek kendimiz bir
looper tanımlamalıyız normal bir thread için. Ancak bir HandlerThread'in
default olarak bir looper'ı vardır doğru anladıysam. )
When we create a HandlerThread, we specify two
things: a name for the thread, which can be helpful when debugging; and its
priority, which must be selected from the set of static values in the android.os.Process class. (HandlerThread class'ından bir object yaratırken, constructor 2
parametre vererek şu 2 şeyi belirtiriz:
- 1.parametre ile thread'e isim veririz. Debugging yaparken işe
yarar bu, log'lardan thread'i ismiyle takip ederiz.
- 2. parametre Thread'in priority'sini(önceliğini) belirler.
android.os.Process class'ındaki static değerlerden birisi argument olarak verilir. )
HandlerThread thread =
new
HandlerThread("bg", Process.THREAD_PRIORITY_BACKGROUND);
Adding THREAD_PRIORITY_MORE_FAVORABLE
to THREAD_PRIORITY_BACKGROUND
when configuring your HandlerThread moves the
thread into the default cgroup, but always consider whether it is really
necessary—it often isn't! ( Genellikle,
thread'in priority'sini Process.THREAD_PRIORITY_BACKGROUND olarak set etmek gerekir. İstersek THREAD_PRIORITY_MORE_FAVORABLE
olarak da set edebiliriz, ancak bunun gerçekten gerekli olduğundan eminseniz
yapın bunu, genellikle bu priority'ye ihtiyaç yoktur. )
HandlerThread extends java.lang.Thread, and we must
start() it before it
will actually begin processing its queue: ( HandlerThread, java.lang.Thread class'ını extend eder.
HandlerThread'in, kuyruğundaki işleri işlemeye başlaması için start()
method'unu çağırmalıyız. )
thread.start();
Now we need a Handler through which we can pass work to our HandlerThread. So we
create a new instance, but this time we parameterize the constructor with the Looper associated with our HandlerThread. ( HandlerThread'e diğer thread'lerin iş gönderebilmesi için bir
Handler'a ihtiyacımız vardır. Dolayısıyla bir Handler object yaratırız, ancak
bu sefer Handler object'i yaratırken öncekilerden farklı olarak şunu yaparız :
Handler'ın constructor'ına argument olarak HandlerThread ile ilişkili Looper'ı
veririz. HandlerThread ile ilişkili olan Looper'ı elde etmek için,
HandlerThread object'in getLooper() method'unu çağırırız. )
Handler handler = new Handler(thread.getLooper());
That's all there is to it—we can now post Runnables for our HandlerThread to execute: ( İşte bu kadar !! Bu iş bitti!! Artık HandlerThread'imize iş
gönderebiliriz bu handler object'in post() veya send() method'larını
kullanarak. )
handler.post(new Runnable(){
public void
run() {
// ... do
some work in the background thread
}
});
To send Messages to our HandlerThread, we'll need to override handleMessage
or provide a Callback
via the alternate Handler
constructor: (handler
object'in send() method'unu çağırarak handler object'in ilişkili olduğu HandlerThread'e Message object gönderebiliriz. Bu durumda
Handler class'ını extend eden bir subclass tanımlayıp bu class'da
handleMessage() method'unu implement etmeliyiz. Veya Handler.Callback
class'ından anonymously bir object yaratmalıyız, bu class'da handleMessage()
method'unu da implement ederiz, sonra bu object'i Handler constructor'ına
2.argument olarak veririz; Handler constructor'ına 1. argument olarak
HandlerThread ile ilişkili olan looper'ı veririz. )
Handler.Callback callback = new Handler.Callback(){ … };
Handler handler = new Handler(thread.getLooper(), callback);
If we create a HandlerThread to do background work
for a specific Activity, we will want to tie
the HandlerThread's lifecycle closely to
that of the Activity
to
prevent resource leaks. ( Eğer
belirli bir activity için arka planda çalışacak bir HandlerThread yaratırsak,
HandlerThread'in yaşam döngüsü method'ları ve Activity'nin yaşam döngüsü
method'larını senkronize çalışacak şekilde ayarlamalıyız, mesela activity
başlatılırken handlerThread'i başlatmalı activity yok edilirken
handlerthread'in resource'larını free etmeliyiz, böylece resource leak olmasını
önleriz. )
A HandlerThread
can be shut down by invoking quit, which will stop the HandlerThread from processing any more work from its queue. A quitSafely method was added at API level 18, which causes the HandlerThread to process all remaining tasks before shutting down. Once a HandlerThread has been told to shut down, it will not accept any further tasks. ( quit() method'unu çağırarak bir HandlerThread'i
durdurabiliriz, böylece HandlerThread kuyruğundaki işleri çalıştırmayı
durdurur. quitSafely() method'unu çağırarak bir HandlerThread'i durdurabiliriz, böylece HandlerThread kuyruğunda
kalan işleri bu işler bitene kadar çalıştırır ama kuyruğuna yeni bir iş almaz,
sonra thread durur. Bir HandlerThread'i kapatılmasını söylersek bu HandlerThread
artık kuyruğuna çalıştırılacak yeni bir task almaz. )
Just as we did in the
previous chapter with AsyncTask, we can use the Activity lifecycle methods to
determine when we should quit the HandlerThread. ( AsyncTask'ı anlatan chapter'da da anlatıldığı gibi,activity
yaratılırken, Handler object yaratırız, activity pause edilirse thread'in
quit() method'unu çağırarak thread'i durdururuz. )
private
HandlerThread thread;
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thread = new HandlerThread( … );
}
protected
void onPause() {
super.onPause();
if ((thread != null) &&
(isFinishing()))
thread.quit();
}
Handler programming issues
The Handler class is truly fundamental to the Android platform and is used
widely throughout, but there are plenty of ways we can get ourselves into
trouble if we aren't careful.( Handler
class'ı, Android platformunun temel yapıtaşlarından biridir ve gerçekten çok
önemlidir. Ancak Handler kullanırken çok dikkatli olmalıyız, özellikle aşağıda
konularda dikkatli olmalıyız aksi takdirde kendimizi sıkıntıya sokabiliriz. )
Leaking implicit references
The biggest worry when using Handler
within an Activity
is resource leakage which, just as with AsyncTask, is very easy to do.
Here's one of our earlier examples: (
Bir activity içerisinde handler kullanırken yaşayabileceğimiz en büyük endişe
resource leakage'dır. Çok kolay bir şekilde resource leakage 'a neden
olabilecek bir yanlış yapabiliriz. Önceki örneklerimizden birine şimdi daha
yakından bakalım : )
final
Runnable runnable = new Runnable(){
public void run() {
// … do some work
}
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10));
By declaring an
anonymous inner Runnable
inside an activity, we have
made an implicit reference to that containing Activity instance. We've then posted the Runnable to a handler and told it to execute in 10 seconds time. ( Bir activity'nin içerisinde, bir anonymous inner Runnable
object tanımlarsak, Activity instance'e implicit bir reference yapmış oluruz. )
If the Activity finishes before the 10 seconds are up, it cannot yet be garbage
collected because the implicit reference in our Runnable means that the Activity is still reachable by live objects. (
Activity 10 saniye geçmeden biterse, activity garbage collector tarafından
temizlenemez çünkü Runnable object'deki implicit reference activity'nin hala
erişilebilir olduğunu söyler. )
So, although it makes
for a concise example, it is not a good idea in practice to post non-static
Runnables onto the main thread's Handler queue (especially with postDelayed or postAtTime) unless we're very careful to clean up after ourselves. ( Bu örnekteki gibi, main thread'in Handler'ına nonstatic bir
Runnable object post etmek iyi bir fikir değildir, özellikle postDelayed()
ve postAtTime() method'larıyla birlikte nonstatic bir Runnable object post etmek çok çok kötü bir fikirdir. Bu
kötü bir fikir olmasına rağmen bunu gene de yapacaksak, arta kalanları çok iyi
temizlememiz çok dikkatli olmamız gerekir. )
One way to minimize
this problem is to avoid using non-static inner classes; for example, by always
declaring Runnables as top-level classes in their own file, or as static classes in an Activity subclass. This means that references must be explicit, which makes
them easier to spot and nullify. ( Bu
problemi minimize etmek için, nonstatic inner class'lar kullanmaktan
kaçınmalıyız. Örneğin Runnable'ları top-level class olarak ayrı bir dosyada
tanımlamalıyız veya Activity class'ın içerisinde static inner class olarak
tanımlamalıyız. Böylece bu Runnable object'lerin garbage collector tarafından bulunup
temizlenmesi daha kolay hale gelir. )
In addition, we can
cancel pending tasks during Activity lifecycle callbacks such as onPause. This is easiest if we're working with Messages since we can
remove them by their what
value, and don't have to keep
references as we would with Runnables. ( Ayrıca, activity'nin onPause() method'u çağırıldığında
kuyrukta bekleyen task'ları iptal edebiliriz. Eğer Message object'ler ile
çalışıyorsak bunu yapmak çok kolay olacaktır, çünkü what değerini kullanarak
Message object'leri silebiliriz, Runnable'larda olduğu gibi Message object'e
reference'ı tutmamıza gerek yoktur. )
For HandlerThread
instances we've created, we
should make sure to quit
when the Activity is finishing, which will prevent further execution and free up the
Runnable
and Message objects for garbage collection. (Activity bittiğinde, HandlerThread object'lerin
quit() method'larını çağırarak durdurduğumuzdan emin olmalıyız. Bu sayede
kuyruğa yeni iş gelmesini önleriz, Runnable ve Message object'leri free etmiş
oluruz. )
Leaking explicit references
If we are to interact
with the user interface, we'll at least need a reference to an object in the View hierarchy, which we might pass into our static or top-level
Runnable's constructor. ( User interface ile
etkileşim içerisindeysek bir view
object'e refer eden bir reference variable'a ihtiyacımız olabilir, bu reference
variable'ı static veya top-level olan Runnable class'ımızın constructor'ına
parametre olarak verebiliriz aşağıdaki gibi, artık Runnable'ımızda view
object'e refer eden reference variable'a sahibizdir. )
static class
MyRunnable implements Runnable {
private View view;
public MyRunnable(View view) {
this.view = view;
}
public void run() {
// … do something with the view.
}
}
However, by keeping a strong reference to the View, we are again subject to potential memory leaks if our Runnable outlives the View; for example, if some other part of our code removes this View from the display before our Runnable executes. (
Yukarıdaki kodda, View object'e refer eden bir strong reference variable
tutarız Runnable class'ında. Eğer Runnable'ımız View'den daha üzün süre
yaşarsa, strong reference variable'dan dolayı muhtemel memory leak sorunuyla
karşılaşabiliriz. Örneğin, kodumuzun başka bir yerinde Runnable çalışmadan
ilgili view'i ekrandan siliyor olabiliriz. Bu durumda Runnable içerisindeki
strong reference variable olmayan bir view object refer eder patlarız
yani. )
One solution to this
is to use a weak reference, and check for null before using the referenced View. ( Bu problemi çözmenin bir
yolu weak reference kullanmak ve refer edilen View'i kullanmadan önce null olup
olmadığını check etmektir. Aşağıdaki kodu inceleyelim, yukarıdaki Runnable'dan
farklı olarak şunları yaptık: reference variable'ların View olan type'larını WeakReference<View> yaptık. run() metod'unda view object'in get() method'unu
çağırdık, bu method null değilse view ile ilgili işlem yapabiliriz, null ise
View object çöpe atılmıştır. )
static
class MyRunnable implements Runnable
{
private WeakReference<View> view;
public MyRunnable(View view)
{
this.view = new
WeakReference<View>(view);
}
public void run()
{
View v = view.get(); // might
return null
if (v != null) {
// … do something with
the view.
}
}
}
If you haven't used WeakReference before, what it gives us is a way to refer to an object only for
as long as some other live object has a stronger reference to it (for example,
a "normal" property reference). (WeakReference bize şunu sağlar: WeakReference variable bir view
object'e refer etmesi için şu şart gerekir: yaşayan bir object'in sahip olduğu
bir stronger reference variable, bu view object'e refer ediyor olmalı. )
When all strong
references are garbage collected, our WeakReference will also lose its reference to the View, get()
will return null, and the View
will be garbage collected. ( Tüm
strong reference'lar garbage collector tarafından temizlenince, bizim
WeakReference'ımız da View'e reference'ını kaybeder. get() method'u null return
eder. Ve view object garbage collector tarafından temizlenir. )
This fixes the
resource leakage problem, but we must always check for null before using the returned object, to avoid potential
NullPointerException's.
If we're sending
Messages to our Handler
and expecting it to update the
user interface, it will also need a reference to the View hierarchy. A nice way to manage this is to attach and detach the Handler from onResume
and onPause. ( Eğer Handler'ımıza Message
object gönderip, user interface'i güncellemek istiyorsak, Handler'ımızda ilgili
View object'e refer eden bir reference variable'a sahip olmalıyız. Bunun için
Handler class'ımızda 2 tane mothod tanımlayalım, bu method'lara 2 tane rastgele
isim verelim: attach() ve detach(). Activity'nin onResume() method'u
çağırıldığında attach(view method'u çağırılarak handler'a view'e refer eden
reference variable'ı verelim. detach() method'u çağırıldığında ise Handler'ın
içerdiği view isimli reference variable'ı null olarak set edelim.
handleMessage() method'unu view variable'ı istediğimiz şekilde set edebiliriz. )
private static class MyHandler extends Handler
{
private TextView
view;
public void
attach(TextView view)
{
this.view =
view;
}
public void
detach()
{
view =
null;
}
@Override
public void
handleMessage(Message msg)
{
//…
}
}
Applications of Handler and HandlerThread
The Handler class is incredibly versatile, which makes its range of
applications very broad. (Handler class'ı çok
kullanışlıdır, beceriklidir bir çok uygulamada kullanılır. )
The Android platform also uses Handler
extensively as a mechanism for abstracting the work
that needs doing from the thread that will do it. A nice example of this can be
found in android.hardware. SensorManager, which allows listeners to be registered along with a Handler so that we can easily
handle sensor data in a separate HandlerThread. You'll find an example of processing sensor data in the
background, using the magnetic field sensor and the accelerometer in
combination to create a simple compass. ( Bu bölümün başında verdiğimiz github linkinde tekrar verelim
hatta : https://github.com/steveliles/AsyncAndroid bu linkte chapter 3 örnek 5
'de SensorManager örneği implement edilmiştir. Handler thread'de yapılacak işi
abstract etmek için kullanılmıştır, ne demek istediğini ben de anlamadım. Bu
örnekte, listener'lar Handler'a register olurlar böylece ayrı bir
HandlerThread'de sensor verileri handle edilir. Magnetic field sensor ve
accelerometer kullanılarak basit bir compass yaratılmıştır bu örnekte. )
Summary
In this chapter we used Handler
to queue work for the main thread to process, as a
means of maintaining responsiveness in a single-threaded application. ( Main thread'İn iş kuyruğuna iş göndermek için Handler
kullandık. )
We saw the different ways we can define work with Handler—arbitrary work defined
at the call site with Runnable, or predefined work implemented in the Handler itself and triggered by
message sending. ( Handler ile
çalışmanın 2 farklı yolu olduğunu öğrendik. Birincisi, Runnable object
tanımlanır, Handler'a Runnable object gönderilir, sırası gelince bu Runnable
object çalışır. İkincisi, Handler'da handleMessage() method'unu tanımlarız, bu
method'da Handler'a bir Message object gelince ne yapılacağını belirleriz.)
We learned how to use Handler
in a multithreaded application to pass work and
results back and forth between cooperating threads, performing blocking
operations on an ordinary background thread, and communicating the results back
to the main thread to update the user interface.
( )
We also met HandlerThread and used it to create a background thread with its own Looper, allowing us to use these same techniques to queue work for
background processing. ( HandlerThread'i öğrendik, HandlerThread
kendisine ait looper'I olan bir background thread'dir. Bu thread'e verdiğimiz
işler arka plandaki bir thread'de çalışır. )
This isn't the last
we'll see of Handler
and HandlerThread—they can also be usefully put to work in other contexts, as we'll
discover in Chapter 5, Queuing Work with IntentService and Chapter
6, Long-running Tasks with Service. ( Handler ve HandlerThread konularına IntentService ve Service
konularında da değineceğiz. )
-*-*-
A
Looper
is an Object associated with the Thread
from which it is created. As you can
guess by it's name a Looper
is going to loop over something, but
looping over what ? Over a message queue also
associated with the same thread.
Next question is: How can I put
something in this message queue ? By using
Handler
.
The
Handler
can play 2 roles (and that's maybe why it is confusing)
First role of the Handler : you must use it to post
messages to it's associated Looper (in fact to its message queue). You can use one of the
various
Handler.sendMessage*
(or Handler.post*
) methods to do that. (and note the sendMessageDelayed/postDelayed
methods allowing you to post a Message/Runnable to be handled in future)
What is the Looper associated with a
Handler ? Very easy : the Looper of the current Thread if you don't specify it
manually; but you can use the constructor with a Looper :
new Handler(Looper
looper)
and in this case the handler is associated with looper in
argument.
At
this point, we know that :
- a
Looper is associated with one and only one Thread.
- a
Looper loops over it's associated message queue
- as
a consequence : there is one message queue associated with one Thread (as soon
as we have a Looper for the Thread)
- a
Handler is always associated with one Looper
- a
Handler can be used to post message to the message queue
Now, let's see the second part : the
message processing/message handling.
First, let's look at the
Looper
looping over it's message queue.
Is there is a message in the queue ? Yes
(i.e. at some point, a
Handler
has posted it.) Is it time
to handle this message (if it was posted with postDelayed) ? If not, wait a little. If it is time : let's dispatch
this message.
Remember that I told that the Handler
have 2 roles... and here is the second role of the Handler : a Handler (as indicated by
it's name) can handle messages. To be able to handle custom messages you must
subclass the
Handler
class and implements the handleMessage(Message)
method.
So, the
Looper
will simply call the handleMessage
of the Handler who posted
the message and its job (i.e. dispatching the messages) is finished (the Looper
can move on to the next Message in the queue).
Final
note
·
UI-thread is a first class citizen :
On Android, there is a main Looper
associated with the main Thread (i.e. the UI-thread). You can get a reference
to it with
Looper.getMainLooper()
, so you create a Handler associated with the main
Looper with :
Handler myHandler = new Handler(Looper.getMainLooper());
and
with that you can post a message from any thread to the UI-thread
·
Should you really use messages and subclassing Handler to use
this ? No (not always).
You don't always need to create message
explicitly to use this mechanism. You can easily post a
Runnable
to a Handler
and in this case you don't even need to override the handleMessage(Message)
because the default
implementation of the Handler will simply execute the Runnable
(under the hood : a message is created with the Runnable associated to it)
·
Looper must be prepared (to receive messages).
By default there is no Looper on every
thread (by default, there is only a prepared one in the UI-Thread). To prepare
a Looper for the current thread : call
Looper.prepare()
----
https://developer.android.com/reference/android/os/Looper.html
Class used to run a message loop for a
thread. Threads by default do not have a message loop associated with them; to
create one, call prepare() in the thread that is to run the loop, and then
loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is
through the Handler class.
This is a typical example of the
implementation of a Looper thread, using the separation of prepare() and loop()
to create an initial Handler to communicate with the Looper.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message
msg) {
// process incoming messages
here
}
};
Looper.loop();
}
}
Additional
Resources
Bu kaynakları incelemedim ancak
incelemekte fayda var,hatta vakit olursa bu kaynakları inceleyip buraya
ekleyebilirim:
https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/
https://blog.nikitaog.me/2014/10/18/android-looper-handler-handlerthread-ii/
http://skillgun.com/question/588/android/threads/what-is-the-difference-between-thread-and-handler-thread-in-android
https://www.safaribooksonline.com/library/view/efficient-android-threading/9781449364120/ch04.html
http://androidshortnotes.blogspot.com.tr/2013/02/thread-concept-in-android.html
http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/
asynchronous
android kitabını indirdim. yukarıda bu kitabın 3.chapter'ını inceledim linki de
yukarıda.
http://guides.codepath.com/android/managing-threads-and-custom-services
https://medium.com/@ali.muzaffar/handlerthreads-and-why-you-should-be-using-them-in-your-android-apps-dc8bf1540341#.pt64dwm17
http://androidsrc.net/android-loopers-and-handlers-code-tutorial/
https://blog.mindorks.com/android-core-looper-handler-and-handlerthread-bd54d69fe91a#.6jz7wba6l
http://stackoverflow.com/questions/25094330/example-communicating-with-handlerthread
http://blog.xebia.com/android-looper-anatomy/
http://stephendnicholas.com/posts/android-handlerthread
http://codetheory.in/android-handlers-runnables-loopers-messagequeue-handlerthread/
https://www.safaribooksonline.com/library/view/efficient-android-threading/9781449364120/ch08.html
https://newfivefour.com/android-service-handler-tutorial.html
http://techtej.blogspot.com.tr/2011/02/android-passing-data-between-main.html
http://www.concretepage.com/android/android-start-stop-service-from-activity-example-using-handlerthread
http://alvinalexander.com/java/jwarehouse/android/core/java/android/os/HandlerThread.java.shtml
Hiç yorum yok:
Yorum Gönder