By Martín Zangl – Android Developer at Santex
If we are to understand some basic, common behaviors for approaching a simple solution using threads, we know that at some point we have to manage communication and execution order. For that, Android provides us with a variety of concurrency and communication frameworks that are designed, implemented, and integrated in accordance with many of the patterns that you can find in POSA books.
Android provides different tools according to concurrency patterns:
We can resolve many problems with one or more of the following:
- Using handlers and other threads
Custom Handlers and Threads
As previously stated, there is only one Looper per Thread, but we can also have different handlers post messages and process them.
For example, we could define two threads with two handlers to send messages to each other.
For instance, what if for one of the threads in the Main Thread, we could send update messages to UI widgets and notify them of something that happens in the other thread? Remember, if we are just using a simple pattern like an observer pattern and notifying every change directly to the UI, it won’t work because we are trying to change the state of view object from a thread other than the main thread. In that case, we could post the result or intermediate results on the handler registered to the main thread. In other words, we have an Activity, we declare a new Handler, that handler would be associated to the Main Thread, that is, the main looper, so every time we send a message from another thread, the handler will post the message in the message queue, then the looper will process that message by sending it to the registered handler. Then we can update some UI widgets.
This is a pretty clean solution, but it becomes very difficult to handle the UI widgets’ lifecycle. That is to say, when a configuration change happens or when the user changes the context often.
The worst case scenario is when a configuration change happens, because in that case a new instance of the activity will be created. It would be even worse if we had references for any particular view on those messages; in that case the reference cannot be garbage collected and we’d just be leaking memory.
More in detail, every time that a configuration change happens two new messages are posted to the looper queue, one is
CONFIGURATION_CHANGE and the other is
RELAUNCH_ACTIVITY and this is what happens after that message is processed:
onDestroy()on the old activity instance.
- create a new activity instance.
onResume()on that new activity instance.
In the case of sending messages from other threads to the main thread, we have to be careful. We cannot predict in which activity instance we are posting those messages.
We could do some speculation and think if any message posted before the orientation change would be handled before
onPause() the leaving activity, and if any message posted a after the orientation change will be handled after
onResume() the incoming activity.
What can we do?
Fix the architecture instead of messing it up with
Do not hold view or context references.
Remove all messages or callbacks in the activity
And the last one if you want to get fired use
handler.postAtFrontQueue() to make sure a message posted before onPause() will be handled before onPause().
In the next post we’ll talk about Asynctask, IntentService, and Loaders.
About the Author – Martín Zangl is an experienced Android Developer at Santex, passionate about his work. Martín is continuously learning and training to investigate new technologies.
Source: https://corner.squareup.com/2013/12/android-main-thread-2.html https://www.youtube.com/watch?v=S1Y1Zt3lfeQ&list=PLZ9NgFYEMxp4KSJPUyaQCj7x--NQ6kvcX&index=32