Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating views inside a worker thread

I have a requirement to generate a bitmap out of an EditText and then perform some manipulations on it. My main concern is not to call View.buildDrawingCache() method on the UI thread and possibly block it, especially when talking about large screens (i.e. Nexus 10) since the EditText will occupy about 80% of the available screen size.

I execute Runnables inside a ThreadPoolExecutor, those will inflate dummy views on a worker thread and set all the required attributes to them, then simply call buildDrawingCache() & getDrawingCache() to generate a bitmap.

This works perfect on some devices yet recently I have encountered a few devices that crash with the following message:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

I understand why this happens, as some phones must have modified implementation for EditText that creates a Handler and thus requires Looper.prepare() to be called first.

From what I've read online there is no issue with calling Looper.prepare() inside a worker thread though some stated it is highly unrecommended yet I could not find a reason for that.

Other than that, most posts related to this issue state you are not supposed to inflate views inside a background thread, probably due to the following from Android's official documentation (Processes and Threads):

"Do not access the Android UI toolkit from outside the UI thread"
  • What is the recommended approach to dealing with this problem?

  • Is there any harm in calling build/get drawingcache from the main thread? (performance-wise)

  • Will calling Looper.prepare() inside my worker thread solve this problem?

EDIT

Just to elaborate on my specific requirement, I have a user-interface consisting of an ImageView and a custom EditText on top of it, the EditText can change it's font and color according to the user selection, it can be zoomed in/out using "pinch to zoom" gesture and can also be dragged around to allow the user to reposition it on top of the image.

Eventually what I do is create a dummy view inside my worker thread using the exact same values (width, height, position) it currently has on the UI and then generate it's drawingcache, the original image's bitmap is decoded again from a local file.

Once the two bitmaps are ready I merge them into a single bitmap for future use.

So to put it simple, is there anything wrong with executing the following code (from within a background thread):

Call Looper.prepare() Create a new view with application context, call measure() & layout() manually and then build+get drawingcache from it, i.e.:

Looper.prepare();

EditText view = new EditText(appContext);

view.setText("some text");
view.setLayoutParams(layoutParams);

view.measure(
        View.MeasureSpec.makeMeasureSpec(targetWidth, View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(targetHeight, View.MeasureSpec.EXACTLY));

view.layout(0, 0, targetWidth, targetHeight);

view.buildDrawingCache();

Bitmap bitmap = view.getDrawingCache();

How does this apply to the restriction with not accessing the Android UI toolkit from outside the UI thread, what could possibly go wrong?

like image 329
cdroid Avatar asked Feb 16 '14 14:02

cdroid


1 Answers

In your case, you can do it of course, but be carefull only reading values from UI data, to avoid synchronizations bug.

Also you should not recreate the EditText from the background thread, it will be more efficient to directly access the already existant one instead:

Looper.prepare();
myEditText.setDrawingCacheEnabled(true);
Bitmap bitmap = myEditText.getDrawingCache();

If your question is : why it is not recommanded by android guidelines, here is a good SO answer to your question.

like image 168
Gomino Avatar answered Oct 07 '22 00:10

Gomino