What does this line from the following code sample mean?
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
I can guess what it does, but what is it called and how does it works? Is it a nameless synchronized function?
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
There is no hidden method there, the code is just synchronizing on the _surfaceHolder object. Basically, it says to get a lock on _surfaceHolder before executing the lines in {}'s.
See Intrinsic Locks and Synchronization.
Related
I have a SurfaceView and a GameThread which updates game state and draw a bitmap on canvas. surface.update() below updates game variables outside the lock and creates a bitmap that has to be drawn on canvas. After locking surface.doDraw draws that bitmap on canvas. Update() is outside UI thread locking is to improve performance. Is this approach wrong? Do I have to update under the lock?
public class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private GameViewSurface surface;
private boolean running = false;
public GameThread(SurfaceHolder holder, GameViewSurface GameSurface) {
surfaceHolder = holder;
surface = GameSurface;
}
public void setRunning(boolean run) {
running = run;
}
public boolean getRunning() {
return running;
}
#Override
public void run() {
while (running) {
Canvas canvas=null;
try {
if(!surfaceHolder.getSurface().isValid())
continue;
surface.update();
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
surface.doDraw(canvas);
}
} catch(Exception p){
}finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
}catch (Exception e){}
}
}
}
}
}
I have this android application.
It use a SurfaceView, from where I get the Surface through the SurfaceHolder.
It also use ExoPlayer to stream videos. However I have instantiated an ImageReader, getting its Surface and passing to the ExoPlayer.
Now, I am in the ImageReader.OnImageAvailableListener#onImageAvailable and I access the latest Image.
I want to manipulate the Image and send the new data to the "SurfaceView" Surface.
How can I "draw" an android.media.Image to an android.view.Surface ?
The question is not clear to me.
The way to get android.media.Image is by the Camera2 API, and there you can provide a surface and the "camera" will draw over it. Please refer to Camera2Video example
Another way to get the Image object is from ImageReader (while decoding video for example). In this case you want to draw the image, but you can not provide a surface to the ImageReader(there is an internal surface that is not displayed). In this case you can draw the Image on a SurfaceView.
Assuming this is the case, you need to convert an Image to a Bitmap objects.
You have discussion about how perform this here
Possible duplicate of: how to draw image on surfaceview android
First get your canvas by using lockCanvas() (see here), second get your image and make it a drawable using:
my_bitmap = Bitmap.createBitmap(
MediaStore.Images.Media.getBitmap(getContentResolver(), uri),
0,0,90, 90);
drawable=new BitmapDrawable(my_bitmap);
After that you can draw the drawable to the locked canvas and use unlockCanvasAndPost (Canvas canvas) to post the updated canvas back to the surfaceview.
here is the answer for your question.
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mySurfaceView mySurfaceView = new mySurfaceView(getApplicationContext());
setContentView(mySurfaceView);
}
}
mySurfaceView.java
public class mySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private TutorialThread _thread;
public mySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap _scratch = BitmapFactory.decodeResource(getResources(),
R.drawable.icon);
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(_scratch, 10, 10, null);
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
public void surfaceCreated(SurfaceHolder arg0) {
_thread.setRunning(true);
_thread.start();
}
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private mySurfaceView _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, mySurfaceView panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
I'm doing a pretty standard drawing using SurfaceView.
The code is running smoothly ~60fps on most devices. BUT -
On Android 5, I'm getting real bad performance.
Any clue why?
class MySurfaceView extends SurfaceView implements Runnable{
Thread thread = null;
SurfaceHolder surfaceHolder;
volatile boolean running = false;
public MySurfaceView(Context context) {
super(context);
surfaceHolder = getHolder();
}
public void onResumeMySurfaceView(){
running = true;
thread = new Thread(this);
thread.start();
}
public void onPauseMySurfaceView(){
boolean retry = true;
running = false;
while(retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void run() {
while(running){
if(surfaceHolder.getSurface().isValid()){
Canvas canvas = surfaceHolder.lockCanvas();
// drawing
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
I have a background animation drawn onto a SurfaceView by another thread in my app. The animation seems to work well except when the screen is rotated. Then the main thread will sometimes hang for a couple of seconds. Using DDMS I see that the main thread is calling Object.wait(), I don't understand where or why it's doing that though.
Below is some abbreviated code, if needed the full source can be found on github at https://github.com/GavinDBrown/Amazing.
Main Activity:
public class StartMenu extends Activity {
/** A handle to the thread that's running the Game Of Life animation. */
private GOLThread mGOLThread;
/** A handle to the View in which the background is running. */
private GOLView mGOLView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_of_life_background);
startGOLBackground();
}
private void startGOLBackground() {
// get handles to the GOLView and it's GOLThread
mGOLView = (GOLView) findViewById(R.id.game_of_life_background);
mGOLThread = new GOLThread(mGOLView.getHolder());
mGOLView.setThread(mGOLThread);
mGOLThread.start();
}
private void stopGOLBackground() {
if (mGOLThread != null) {
mGOLThread.halt(); // stop the animation if it's valid
boolean retry = true;
while (retry) {
try {
mGOLThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
mGOLThread = null;
mGOLView = null;
}
}
#Override
public void onResume() {
super.onResume();
mGOLThread = mGOLView.getThread();
mGOLThread.unpause();
}
#Override
public void onPause() {
super.onPause();
mGOLThread.pause();
}
#Override
protected void onDestroy() {
super.onDestroy();
stopGOLBackground();
}
}
The SurfaceView:
public class GOLView extends SurfaceView implements SurfaceHolder.Callback {
/** The thread that actually draws the animation */
private GOLThread thread;
SurfaceHolder surfaceHolder;
public GOLView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (hasWindowFocus){
thread.unpause();
} else {
thread.pause();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
thread.setSurfaceSize(width, height);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.pause();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.unpause();
}
}
And finally the Thread:
public class GOLThread extends Thread {
private GameOfLife gameOfLife;
private final Object GOLLock = new Object();
private int mCanvasHeight;
private int mCanvasWidth;
private SurfaceHolder mSurfaceHolder;
public GOLThread(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
}
#Override
public void start() {
synchronized (mSurfaceHolder) {
stopped = false;
mSurfaceHolder.notify();
}
super.start();
}
public void halt() {
synchronized (mSurfaceHolder) {
paused = true;
stopped = true;
mSurfaceHolder.notify();
}
}
public void pause() {
synchronized (mSurfaceHolder) {
paused = true;
}
}
public void unpause() {
synchronized (mSurfaceHolder) {
paused = false;
mSurfaceHolder.notify();
}
}
public Bundle saveState(Bundle outState) {
synchronized (GOLLock) {
if (outState != null) {
outState.putParcelable(GAME_OF_LIFE_ID, gameOfLife);
}
}
return outState;
}
public synchronized void restoreState(Bundle savedState) {
synchronized (GOLLock) {
gameOfLife = (GameOfLife) savedState.getParcelable(GAME_OF_LIFE_ID);
}
}
#Override
public void run() {
while (!stopped) {
while (paused && !stopped) {
try {
synchronized (mSurfaceHolder) {
mSurfaceHolder.wait(100L);
}
} catch (InterruptedException ignore) {
}
}
// Check if thread was stopped while it was paused.
if (stopped)
break;
beforeTime = System.nanoTime();
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas();
synchronized (GOLLock) {
if (gameOfLife != null) {
gameOfLife.drawAndUpdate(c);
} else {
pause();
}
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
sleepTime = FRAME_DELAY
- ((System.nanoTime() - beforeTime) / 1000000L);
try {
// actual sleep code
if (sleepTime > 0 && !stopped && !paused) {
synchronized (mSurfaceHolder) {
mSurfaceHolder.wait(sleepTime);
}
}
} catch (InterruptedException ex) {
}
}
}
public void setSurfaceSize(int width, int height) {
synchronized (GOLLock) {
if (mCanvasWidth != width || mCanvasHeight != height) {
mCanvasWidth = width;
mCanvasHeight = height;
// reset the GOL
if (mCanvasWidth > 0 && mCanvasHeight > 0) {
gameOfLife = new GameOfLife();
gameOfLife.init(mCanvasWidth, mCanvasHeight);
}
}
}
}
}
The problem is GOLThread is calling SurfaceHolder.lockCanvas() and getting null as a result too often.
From the docs on SurfaceHolder.lockCanvas()
If you call this repeatedly when the Surface is not ready (before
Callback.surfaceCreated or after Callback.surfaceDestroyed), your
calls will be throttled to a slow rate in order to avoid consuming
CPU.
So the OS was throttling calls by putting my threads to sleep.
I fixed it by updating the code in GOLThread.run() to have
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas();
if (c == null) {
// Pause here so that our calls do not get throttled by the
// OS for calling lockCanvas too often.
pause();
} else {
synchronized (GOLLock) {
if (gameOfLife != null) {
gameOfLife.drawAndUpdate(c);
} else {
pause();
}
}
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
HI! I have a surfaceView inside a horizontal scrollview that I want to fill with images with a onDraw() call. However, nothing is drawn.
I have a class in which the drawing is done from the thread CanvasThread.
public class PanelChart extends SurfaceView implements SurfaceHolder.Callback {
private CanvasThread canvasthread ;
public PanelChart(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
getHolder().addCallback(this);
canvasthread = new CanvasThread(getHolder(), this);
setFocusable(true);
I have tried to change the
`synchronized (_surfaceHolder) {
_panel.postInvalidate();
}`
to
synchronized (_surfaceHolder) {
_panel.postInvalidate();
}
I have also tried to add the call setWillNotDraw(false) without luck:
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
canvasthread.setRunning(true);
canvasthread.start();
setWillNotDraw(false);
This seems to be a common issue, but none of the solutions I have come across have worked for me.
Thanks!
postInvalidate will not call onDraw with surfaceView. You need to unlock canvas, draw things and then lock canvas. Here is an example of a thread for surfaceView:
class CanvasThread extends Thread {
private SurfaceHolder surfaceHolder;
private PanelChart panel;
private boolean run = false;
public CanvasThread(SurfaceHolder surfaceHolder, PanelChart panel) {
this.surfaceHolder = surfaceHolder;
this.panel = panel;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit the frame rate to maximum 60 frames per second (16 miliseconds)
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16){
try{
Thread.sleep(16 - timeDelta);
}catch(InterruptedException e){
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
panel.onDraw(c); //draw canvas
computePhysics(); //calculate next frame
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c); //show canvas
}
}//try finally
} //while
}//run
}//thread