Skip to main content

Android Loopers

This post comes out of a crash fix that I just did for my codebase.


We had been facing some ui delays in our app for past some time now and it was beyond a comfortable user experience now. So we decided it was time to sit down and solve the issues behind this.


As you’d have guessed, we were doing some things on main thread that we shouldn’t have been doing. The tool that helped us track down the exact things is BlockCanary. It’s a great tool that prints the whole stacktrace of the method call that’s holding your main thread.


So, we quickly pushed all of this work to our background thread. In doing so, we didn’t realise that we were creating a handler in of those things for the current thread so that we could post some runnables on it.


If you have worked with handlers before, you’d have guessed it by now. The app started crashing.
And the reason was, handler requires a looper to be running on a thread and we didn’t create one. It was working correctly before because the code was running on main thread and it already has a looper.

The crash fix was pretty easy but I went ahead to learn more about the loopers and I must say they are pretty interesting! The purpose of this post is to share that with you.


looper transforms a normal thread which terminates when its run() method returns into something that runs continuously, which is needed in a GUI framework. It provides a queue where jobs to be done are enqueued.


An Android app can’t run on a normal thread. A thread called “main” or “UI” starts your application and draws all UI. So the first screen is displayed to users. What next? The main thread terminates? No, it shouldn’t. It should wait until users do something. But how can we achieve this behavior? Well, we can try with Object.wait() or Thread.sleep(). 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. That is what looper is all about.


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 I guess you understand what it means.


In our case, we wanted a message queue for our background thread as well, so we created a looper for it.


How did we do it?


You just have to call prepare before creating the handler, then loop after creating the handler and when you are done, quit. That’s it.


Cheers !


Let me know in case of any doubts.


References : https://stackoverflow.com/questions/7597742/what-is-the-purpose-of-looper-and-how-to-use-it

Comments

Popular posts from this blog

Android : AbsSavedState cannot be cast to $SavedState

Android AbsSavedState cannot be cast to <View>$SavedState I came across a strange crash today. This is the stacktrace : Fatal Exception: java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to android.widget.ScrollView$SavedState at android.widget.ScrollView.onRestoreInstanceState(ScrollView.java:1810) at android.view.View.dispatchRestoreInstanceState(View.java:14768) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3123) at android.view.View.restoreHierarchyState(View.java:14746) at android.support.v4.app.Fragment.restoreViewState(SourceFile:470) at android.support.v4.app.FragmentManagerImpl.moveToState(SourceFile:1326) at android.support.v4.app.FragmentManagerImpl.moveFragmentsToInvisible(SourceFile:2323) at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(SourceFile:2136) at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(SourceFile:2092)

Android Tip : Handling back button in Fragments

Android Tip : Handling hardware back button in Fragment and DialogFragment This post explains how to handle hardware back button in a Fragment OR DialogFragment . In DialogFragment, it’s quiet straight forward to achieve this. You’ve to get the dialog instance and set onKeyListener on it : if (getDialog() != null ) { getDialog().setOnKeyListener( new DialogInterface.OnKeyListener() { @Override public boolean onKey (DialogInterface dialog, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { Timber.i( "hardware back button pressed" ); } } }); } This can be done in the onViewCreated callback. For fragments, this method doesn’t work and fragments doesn’t have a direct callback to catch this event. So in this case, the approach that we follow is : You

DialogFragment : NullPointerException (support library)

Another weird crash this time ! Here’s the stack trace : Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{<activity.fully.qualified.path>}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Dialog.setContentView(android.view.View)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2659) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2724) at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4524) at android.app.ActivityThread.-wrap19(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1479) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6123) at java.lang.reflect.Method.invoke(Method.java) at com.android