我有一个Android项目,由一个带有ImageView的布局组成。
public class MainActivity extends AppCompatActivity {
/* original and stretched sized bitmaps */
private Bitmap bitmapOriginal;
private Bitmap bitmapStretched;
/* the only view */
private ImageView iv;
....
}
此ImageView由此runnable函数
更新 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函数中,但我得到了相同的结果
答案 0 :(得分:6)
我猜你正在尝试创建bitmapOriginal ouside线程。因此,当编译器在60秒后尝试再次调用时,它会获得相同的对象并且无法识别任务。我建议如下更好。
#!/bin/bash
IsSSL=$1
if [ $IsSSL = "Yes" ] || [ $IsSSL = "No" ];
then
echo "correct input"
else
echo "incorrect input"
fi
答案 1 :(得分:5)
将绘图逻辑与设备的帧速率同步的正确方法是使用SurfaceView而不是ImageView。您应该创建一个尝试尽可能快地渲染帧的渲染线程,而不是使用您自己的计时器将帧推送到View。当您致电surfaceHolder.lockCanvas()
时,Android系统将自动阻止,直到呈现框架为止。使用unlockCanvasAndPost()
解锁画布时,系统会将缓冲区绘制到屏幕上。
有关详细信息,请参阅https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview。希望这有帮助!
答案 2 :(得分:4)
答案 3 :(得分:4)
在此提供使用此类渲染方法的目的?你想做什么?,android引擎中有很棒的动画功能,可能这个任务可以用这个动画完成。
如果您将使用像您这样的手机电池将会非常快地运行零,这将加载cpu / gpu到最大值。
无论如何 - 尝试从正在运行的任务中放置块,在启动时设置bool taskRun = true
并在更新ui后检查if (!taskRun){ taskRun = true; //here start your task..}
和ui线程,你可以切换到taskRun = false;
使用这个你可以跳过一些帧,但不应该崩溃。
答案 4 :(得分:3)
问题是主线程的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);
}
});
}
答案 5 :(得分:1)
我认为你是正确的,因为你无法确定,Android会以60 FPS渲染图像。是的,我认为你只需要将Bitmap Native Callback与Android Render同步。所以,让我们开始吧。
我更喜欢使用来自并发堆栈Java的Lock。因为您看到,当您锁定对象时,以及解锁时。如果在Bitmap对象上使用volatile(例如,确保还有参考限制),则需要检查在使用Bitmap的地方锁定此对象。
另外我认为你应该使用来自THIS EXAMPLE的锁(从任何其他线程解锁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);
}
}