Создание представлений внутри рабочего потока

У меня есть требование генерировать растровое изображение из EditText, а затем выполнять некоторые манипуляции на нем. Моя основная проблема заключается не в вызове View.buildDrawingCache() в потоке пользовательского интерфейса и, возможно, его блокировании, особенно при разговоре о больших экранах (например, Nexus 10), так как EditText займет около 80% доступного размера экрана.

Я Runnable s внутри ThreadPoolExecutor , они будут раздувать фиктивные представления рабочего потока и устанавливать для них все необходимые атрибуты, а затем просто вызвать buildDrawingCache() и getDrawingCache() для создания растрового изображения.

Это отлично работает на некоторых устройствах, но в последнее время я столкнулся с несколькими устройствами, которые разбились со следующим сообщением:

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

Я понимаю, почему это происходит, так как некоторые телефоны должны иметь модифицированную реализацию для EditText которая создает Handler и, следовательно, Looper.prepare() требуется Looper.prepare() .

Из того, что я читал в Интернете, нет проблем с вызовом Looper.prepare () внутри рабочего потока, хотя некоторые заявили, что он очень не рекомендуется, но я не мог найти причину этого.

Кроме того, в большинстве сообщений, связанных с этой проблемой, говорится, что вы не должны раздувать представления внутри фонового потока, возможно, из-за того, что из официальной документации Android (Процессы и потоки) :

 "Do not access the Android UI toolkit from outside the UI thread" 
  • Каков рекомендуемый подход к решению этой проблемы?

  • Есть ли вред в вызове build / get drawingcache из основного потока? (С точки зрения производительности)

  • Будет ли вызов Looper.prepare () внутри рабочего потока решить эту проблему?

РЕДАКТИРОВАТЬ

Чтобы подробно остановиться на моем конкретном требовании, у меня есть пользовательский интерфейс, состоящий из ImageView и пользовательского EditText поверх него, EditText может изменить его шрифт и цвет в соответствии с выбором пользователя, его можно увеличить / уменьшить с помощью " Щепотку, чтобы увеличить масштаб », а также можно перемещать, чтобы пользователь мог переместить его поверх изображения.

В конечном итоге то, что я делаю, создает фиктивный вид внутри моего рабочего потока, используя те же самые значения (ширина, высота, положение), которые он имеет в настоящее время в пользовательском интерфейсе, а затем генерирует его чертежныйcache, растровое изображение исходного изображения снова декодируется из локального файла.

Как только два растровых изображения готовы, я объединю их в одно растровое изображение для будущего использования.

Итак, чтобы было просто, что-то не так с выполнением следующего кода (из фонового потока ):

Вызовите Looper.prepare () Создайте новое представление с контекстом приложения, вызовите measure() и layout() вручную, а затем создайте + getcachecache из него, то есть:

 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(); 

Как это относится к ограничению без доступа к набору инструментов Android UI из-за пределов пользовательского интерфейса, что может пойти не так?

Solutions Collecting From Web of "Создание представлений внутри рабочего потока"

В вашем случае вы можете это сделать, конечно, но будьте осторожны, только считывая значения из данных пользовательского интерфейса, чтобы избежать ошибки синхронизации.

Также вы не должны воссоздавать EditText из фонового потока, более эффективно напрямую обращаться к уже существующей:

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

Если ваш вопрос: почему он не рекомендован руководством Android, вот хороший ответ SO на ваш вопрос.

Calling View.buildDrawingCache() вызывает Bitmap.nativeCreate который может быть большим распределением, поэтому да, это может быть потенциально опасно для запуска в основном потоке. Я не вижу проблемы с вызовом Looper.prepare () в фоновом потоке. Однако неясно, чего вы пытаетесь достичь, и может быть лучшее решение вашей проблемы.

Причина, по которой вы не должны использовать инструментарий UI из других потоков, состоит в том, что он не написан для потокобезопасности, он написан в предположении, что выполняется только один поток. Это означает, что очень сложно сказать, что может пойти не так, плохие эффекты, если таковые имеются, будут в основном случаться в un-repeatable из-за определенного времени потоков. Ваше описание того, что вы пытаетесь сделать, не слишком понятно. В вашем случае я бы просто выделил большое растровое изображение и нарисую в нем текст. Почему вы используете EditText в первую очередь? Кажется, это своего рода хак, и хаки, как правило, ломаются в конце концов.

Почему View.buildDrawingCache() ? Как насчет использования View.draw (Canvas canvas) для рендеринга вручную на Canvas поддерживаемого Bitmap ? Метод кажется достаточно простым, чтобы не вызывать проблем с фоновыми потоками.

 EditText edit = (EditText)findViewById(R.id.edit); edit.buildDrawingCache(); ImageView img = (ImageView)findViewById(R.id.test); img.setImageBitmap(edit.getDrawingCache()); 

Lalit, когда вы пытаетесь создать кеш в методе onCreate, чертеж еще не произошел, поэтому у чертежа Cache не должно быть ничего. Либо поместите метод buildDrawingChache в метод onClick. Или используйте следующий код в onCreate.

 ViewTreeObserver vto = editText.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { editText.buildDrawingCache(); } }); 

Я также столкнулся с этой ошибкой несколько раз:

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

Мое решение:

 new Thread(new Runnable(){ @Override public void run(){ //add implementations that DOES NOT AFFECT the UI here new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run(){ //manage your edittext and Other UIs here } }); } }).start(); 

Просто создайте обработчик внутри рабочего потока, чтобы применить изменения данных к вашему пользовательскому интерфейсу