在工作线程内创建视图

时间:2014-02-16 14:19:53

标签: android multithreading android-layout android-ui

我需要从EditText生成位图,然后对其执行一些操作。 我主要担心的是不要在UI线程上调用View.buildDrawingCache()方法并且可能阻止它,特别是在谈论大屏幕(即Nexus 10)时,因为EditText将占用可用屏幕大小的大约80%

我在Runnable内执行ThreadPoolExecutor,这些会在工作线程上膨胀虚拟视图并为其设置所有必需属性,然后只需调用buildDrawingCache()& getDrawingCache()生成位图。

这在某些设备上运行良好但是最近我遇到了一些因以下消息而崩溃的设备:

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

我理解为什么会发生这种情况,因为某些手机必须修改EditText的实施,以创建Handler,因此需要首先调用Looper.prepare()

从我在网上看到的内容,在工作线程中调用Looper.prepare()没有问题,虽然有人说这是非常不推荐但我找不到原因。

除此之外,与此问题相关的大多数帖子都表明您不应该在后台线程中夸大视图,可能是由于Android's official documentation (Processes and Threads)中的以下内容:

"Do not access the Android UI toolkit from outside the UI thread"
  • 处理此问题的推荐方法是什么?

  • 从主线程调用build / get drawingcache有什么害处吗? (性能明智)

  • 在我的工作线程中调用Looper.prepare()会解决这个问题吗?

修改

为了详细说明我的具体要求,我有一个用户界面,包括一个ImageView和一个自定义的EditText,EditText可以根据用户选择改变它的字体和颜色,它可以放大/使用“捏拉缩放”手势,也可以拖动以允许用户将其重新定位在图像上。

最后我做的是使用当前在UI上完全相同的值(宽度,高度,位置)在我的工作线程中创建一个虚拟视图,然后生成它的drawingcache,原始图像的位图再次从本地解码文件。

一旦两个位图准备就绪,我将它们合并到一个位图中以备将来使用。

所以简单来说,执行以下代码有什么不对(来自后台线程):

致电Looper.prepare() 使用应用程序上下文创建一个新视图,调用measure()& layout()手动,然后构建+从中获取drawingcache,即:

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

如果不从UI线程外部访问Android UI工具包,这是如何适用于限制的,哪些可能出错?

6 个答案:

答案 0 :(得分:4)

在您的情况下,您当然可以这样做,但要小心只读取UI数据中的值,以避免同步错误。

此外,您不应该从后台线程重新创建EditText,而是直接访问已存在的那个更高效:

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

如果您的问题是:为什么Android指南不推荐,here对您的问题是一个很好的答案。

答案 1 :(得分:1)

调用View.buildDrawingCache()调用Bitmap.nativeCreate这可能是一个大的分配,所以是的,在主线程上运行可能有害。我没有看到在后台线程中调用Looper.prepare()的问题。但是,目前还不清楚您要实现的目标,并且可能有更好的解决方案来解决您的问题。

答案 2 :(得分:1)

你不应该从其他线程获得UI工具包的原因是它不是被编写为线程安全的,它是在假设只有一个线程运行它的情况下编写的。这意味着很难分辨出什么可能出错,如果有的话,坏的影响将主要发生在由于线程的特定时间而不可重复的情况下。 您对此尝试的描述不太清楚。在你的情况下,我只是分配一个大的位图,并在其中绘制文本。你为什么首先使用EditText?这似乎是一种黑客攻击,黑客最终会破坏。

答案 3 :(得分:1)

为什么View.buildDrawingCache()?如何使用View.draw(Canvas canvas)手动呈现为Canvas支持的Bitmap?方法似乎很简单,不会在后台线程上引起问题。

答案 4 :(得分:0)

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

Lalit当您尝试在onCreate方法中构建缓存时,绘图尚未发生,因此drawingCache应该没有任何内容。将buildDrawingChache方法放在onClick方法中。或者在onCreate中使用以下代码。

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

答案 5 :(得分:-1)

我也曾多次遇到过这个错误:

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

只需在工作线程中创建一个处理程序,即可将数据更改应用到您的UI