【问题标题】:UI Thread and Image processingUI线程和图像处理
【发布时间】:2014-04-14 14:07:49
【问题描述】:

我有一个优先级设置为Thread.MIN_PRIORITY + 1 的线程,它为我进行一些图像处理(调整大小、读取和写入)。

我注意到,每当该线程处理图像时,我的 UI 线程中的动画就会开始卡顿和冻结。

优先级足够低,因此动画应该比该线程具有更高的优先级,而且我的 CPU 肯定不会缺乏资源。

发生这种情况有什么特别的原因吗?


编辑 - 我的线程:

public class WorkerQueue extends Thread {
  public Handler handler;
  int priority = Thread.MIN_PRIORITY + 1;
  private static WorkerQueue self = null;

  public static WorkerQueue getInstance() {
    if (self == null) {
      self = new WorkerQueue();
      self.start();
      self.setPriority(priority);
    }

    return self;
  }

  @Override
  public void run() {
      Looper.prepare();
      handler = new Handler();
      handler.getLooper().getThread().setPriority(priority);
      Looper.loop();    
  }

  public synchronized void enqueueTask(final Worker task) {
    handler.post(new Runnable() {
      @Override
      public void run() {
          task.run();
      }
    });
  }
}

【问题讨论】:

  • 我建议您阅读这篇文章,而不是回答您的问题 - developer.android.com/training/displaying-bitmaps/index.html
  • 我已经通读了它,由于我的任务运行时间很长,我决定使用我自己的 looper 线程而不是 asyncTask。除此之外,我的代码与那里给出的示例非常相似。

标签: java android multithreading performance ui-thread


【解决方案1】:

可能是GC。

图像处理可能需要大量内存分配和随之而来的垃圾收集。 Android 中的 GC 使用 mark-and-sweep 算法,该算法要求 VM 停止执行您的程序 - 您可以看到这会如何导致 UI 卡顿。例如,请参阅 this answer 和这个价廉物美的报价:

mark-and-sweep 方法的主要缺点是普通程序 在垃圾收集算法运行时暂停执行。特别是,这 可能是与人类用户交互的程序中的问题,或者必须满足真实的 时间执行约束。例如,一个使用标记和 清理垃圾收集周期性地变得无响应。

您可以通过查看 logcat 来确认这是否正确 - 这会为每次 GC 打印一条消息,并且在此期间,UI 线程将被阻塞。

如果是 GC,那么解决它的一种方法是查看您的内存分配模式,看看是否可以通过重用缓冲区和工作内存来减少内存流失。

【讨论】:

  • 肯定会对此进行调查,看看日志是否与我所看到的相符!将报告我的结果
  • 看来是GC。 GC_FOR_ALLOC 被频繁调用,最多占用 100 毫秒,这会阻塞包括 UI 线程在内的所有线程!
【解决方案2】:

LazyList可以帮到你。
这是在其他线程中加载图像并在完成后显示在 UI 上的开源代码

【讨论】:

  • LazyList 不太适合我的需要。我已经有了一个可以正常加载和显示图像的实现。我的问题更多与调整大小有关
【解决方案3】:

您应该使用AsyncTask 来处理和显示位图:

https://developer.android.com/training/displaying-bitmaps/process-bitmap.html#async-task

这个类专门为此用途而设计,至于为什么会在您的线程上发生这种情况,这可能与 Android 处理 GC 与常规 Java 应用程序不同的方式有关。在处理ListViewGridView 中显示的Bitmap 对象时尤其如此。

【讨论】:

  • 加入我上面发布的线程的工作线程(Runnables)实际上并没有触及 UI 线程,这就是为什么我没有看到需要使用 AsyncTask 的原因。他们所做的只是从磁盘加载图像,调整大小,然后将调整大小的图像保存到磁盘。
  • 我知道这一点,但您仍然需要使用 AsyncTask,尤其是在从磁盘或网络位置加载时。如果从内存中加载它就不那么重要了。你需要阅读我链接的文档,特别是关于 AsyncDrawable 和 WeakReference 的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 2018-06-03
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多