【问题标题】:Android - Update Bitmap from timer threadAndroid - 从计时器线程更新位图
【发布时间】:2016-08-22 11:30:10
【问题描述】:

我得到了一个由带有 ImageView 的单个 Layout 组成的 Android 项目。

public class MainActivity extends AppCompatActivity {

    /* original and stretched sized bitmaps */
    private Bitmap bitmapOriginal;
    private Bitmap bitmapStretched;

    /* the only view */
    private ImageView iv;

    ....
}

这个 ImageView 被这个可运行函数更新

    runnable = new Runnable() {
        @Override
        public void run() {
            iv.setImageBitmap(bitmapStretched);
        }
    };

runnable 由临时 JNI 函数运行,在后台线程上运行,每秒调用 60 次。

public void jniTemporizedCallback(int buf[]) {

    /* set data to original sized bitmap */
    bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);

    /* calculate the stretched one */
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);

    /* tell the main thread to update the image view */
    runOnUiThread(runnable);
}

绘制某些帧后,应用程序崩溃并显示以下消息。

A/OpenGLRenderer: Task is already in the queue!

我猜这是因为渲染器没有完成完整渲染 ImageView 的前一帧而生气。

如果我删除runOnUiThread(runnable);,问题就会消失(显然)

如何避免这种情况?如何将我的应用程序与 openGL 渲染器同步?

我也尝试扩展 ImageView 并将画布上的位图绘制到 onDraw 函数中,但我得到了相同的结果

【问题讨论】:

标签: android multithreading bitmap opengl-es imageview


【解决方案1】:

我猜你正在尝试在线程之外创建 bitmapOriginal。因此,当编译器在 60 秒后再次尝试调用时,它会获取相同的对象并且无法识别任务。我会建议如下更好。

 public void jniTemporizedCallback(int buf[]) {
      // Initialize
      bitmapOriginal = Bitmap.createBitmap(///)
     /* set data to original sized bitmap */
     bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);

    /* calculate the stretched one */
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);

    /* tell the main thread to update the image view */
    runOnUiThread(runnable);
}

【讨论】:

    【解决方案2】:

    将绘图逻辑与设备的帧速率同步的正确方法是使用 SurfaceView 而不是 ImageView。与其使用自己的计时器将帧推送到视图,不如创建一个渲染线程来尝试尽可能快地渲染帧。当你调用surfaceHolder.lockCanvas() 时,Android 系统会自动阻塞直到渲染帧。当您使用unlockCanvasAndPost()解锁画布时,系统会将缓冲区绘制到屏幕上。

    请参阅https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview 了解更多信息。希望这会有所帮助!

    【讨论】:

      【解决方案3】:

      问题与位图本身完全无关......

      是实时时钟信号干扰了 Android RenderThread。

      这里有进一步的解释:

      Android and JNI real time clock

      【讨论】:

        【解决方案4】:

        在这里提供使用这种方法进行渲染的目的?你想做什么?,android引擎中有很棒的动画功能,可能这个任务可以用这个动画完成。

        如果您将使用像您的手机电池这样的代码,那么您的手机电池将非常快地运行到零,因为这会将 cpu/gpu 加载到最大。

        无论如何-尝试从正在运行的任务中放置块,在启动时设置 bool taskRun = true 并检查 if (!taskRun){ taskRun = true; //here start your task..} 并在更新 ui 后在 ui 线程上切换到 taskRun = false; 使用它可以跳过一些帧,但应该不会崩溃。

        【讨论】:

          【解决方案5】:

          问题是主线程的Handler 保留了对您的Runnable 的引用。当您想第二次运行 Runnable 时,旧的 Runnable 已经在 Message Queue 中,因此是 Task is already in the queue 消息。如果您每次想像下面的代码那样执行Runnable 时创建一个Runnable,我认为问题将得到解决。

          public void jniTemporizedCallback(int buf[]) {
              /* set data to original sized bitmap */
              bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth,         origHeight);
          
              /* calculate the stretched one */
              bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);
          
               /* tell the main thread to update the image view */
              runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                      iv.setImageBitmap(bitmapStretched);
                  }
              });    
          }
          

          【讨论】:

            【解决方案6】:

            我认为您是有道理的,因为您无法确定 Android 以 60 FPS 渲染图像。是的,我认为您只需将 Bitmap Native Callback 与 Android Render 同步即可。那么,让我们开始吧。

            我更喜欢使用 Java 并发堆栈中的 Lock。因为你看到了,当你锁定对象时,当你解锁时。如果在 Bitmap 对象上使用 volatile(例如,确保也有引用限制),您需要检查在使用 Bitmap 的地方锁定该对象。

            另外我认为你应该使用THIS EXAMPLE 中的 Lock(从任何其他线程解锁 Lock 对象)。所以,这里是例子。下面的示例将正常工作。只是不要忘记上下文删除和停止任务:

            public class MainActivity extends AppCompatActivity {
            
                /* Initialize lock (avoid lazy init, with your methods) */
                private ReentrantLock lock = new ReentrantLock();
            
                ............
            
                private runnableDrawImage = new Runnable() {
                    @Override
                    public void run() {
                        iv.setImageBitmap(bitmapStretched);
                        lock.unlock();
                    }
                };
            
                .......... 
            
            
               public void jniTemporizedCallback(int buf[]) {
                   /* synchronize by locking state*/
                   lock.lock();
            
                   bitmapOriginal = Bitmap.createBitmap(///)
                   bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
            
                   bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
                   MainActivity.this.runOnUiThread(runnableDrawImage);
               }
            
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2019-01-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多