Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid Surfaceview Threads and memory leaks
    primarykey
    data
    text
    <p>Im creating a game in android, and i noticed that the game has a memory leak. Iv managed to isolate the memory leak into a smaller application so that i can see well try and work out, how to fix it.</p> <p>The application uses a surfaceview for its view and has a thread attached to that in order to do all the drawing to the screen. The memory leak happens when i start a new activity and close the one that im currently using. I can see this when i do a memory dump on my test application as all it does is open and close an activity (activity a -> activity b -> activity a). Iv kind of ran out of ideas as to how i can fix this as iv tried nulling all my references that i do create to the view (inside the thread), iv tried removing the callback from the surfaceview when i destroy the view, and also inside the activity, it doesn't seem to make any difference.</p> <p>MemoryLeakActivity.java</p> <pre><code>package memory.leak; import memory.leak.view.MemoryLeak; import android.app.Activity; import android.os.Bundle; public class MemoryLeakActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new MemoryLeak(this)); } } </code></pre> <p>MemoryLeakViewThread.java</p> <pre><code>package memory.leak.thread; import memory.leak.view.MemoryLeak; import android.view.SurfaceHolder; import android.graphics.Canvas; public class MemoryLeakViewThread extends Thread { private MemoryLeak view; private boolean run =false; public MemoryLeakViewThread(MemoryLeak view) { this.view =view; } public void setRunning(boolean run) { this.run =run; } @Override public void run() { Canvas canvas =null; SurfaceHolder holder =this.view.getHolder(); while(this.run) { canvas =holder.lockCanvas(); if(canvas !=null) { this.view.onDraw(canvas); holder.unlockCanvasAndPost(canvas); } } holder =null; this.view =null; } } </code></pre> <p>MemoryLeak.java</p> <pre><code>package memory.leak.view; import memory.leak.TestActivity; import memory.leak.thread.MemoryLeakViewThread; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.GestureDetector.OnGestureListener; public class MemoryLeak extends SurfaceView implements SurfaceHolder.Callback, OnGestureListener { private GestureDetector gesture; private MemoryLeakViewThread vThread; private Context context; public MemoryLeak(Context context) { super(context); this.getHolder().addCallback(this); this.vThread =new MemoryLeakViewThread(this); this.gesture =new GestureDetector(this); this.context =context; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} public void surfaceCreated(SurfaceHolder holder) { if(!this.vThread.isAlive()) { this.vThread =new MemoryLeakViewThread(this); this.vThread.setRunning(true); this.vThread.start(); } } public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; if(this.vThread.isAlive()) { this.vThread.setRunning(false); while(retry) { try { this.vThread.join(); retry =false; } catch(Exception ee) {} } } this.vThread =null; this.context =null; } public boolean onTouchEvent(MotionEvent event) { return this.gesture.onTouchEvent(event); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { } @Override public void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); } @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } @Override public void onLongPress(MotionEvent e) {} @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onShowPress(MotionEvent e) {} @Override public boolean onSingleTapUp(MotionEvent e) { Intent helpScreenIntent =new Intent(this.context, TestActivity.class); this.context.startActivity(helpScreenIntent); if (this.context instanceof Activity) ((Activity) this.context).finish(); return true; } } </code></pre> <p>TestActivity.java</p> <pre><code>package memory.leak; import memory.leak.view.Test; import android.app.Activity; import android.os.Bundle; public class TestActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new Test(this)); } } </code></pre> <p>TestViewThread.java</p> <pre><code>package memory.leak.thread; import memory.leak.view.Test; import android.view.SurfaceHolder; import android.graphics.Canvas; public class TestViewThread extends Thread { private Test panel; private boolean run =false; public TestViewThread(Test panel) { this.panel =panel; } public void setRunning(boolean run) { this.run =run; } @Override public void run() { Canvas canvas =null; SurfaceHolder holder =this.panel.getHolder(); while(this.run) { canvas =holder.lockCanvas(); if(canvas !=null) { this.panel.onDraw(canvas); holder.unlockCanvasAndPost(canvas); } } holder =null; this.panel =null; } } </code></pre> <p>Test.java</p> <pre><code>package memory.leak.view; import memory.leak.MemoryLeakActivity; import memory.leak.thread.TestViewThread; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.GestureDetector.OnGestureListener; public class Test extends SurfaceView implements SurfaceHolder.Callback, OnGestureListener { private GestureDetector gesture; private TestViewThread vThread; private Context context; public Test(Context context) { super(context); this.getHolder().addCallback(this); this.vThread =new TestViewThread(this); this.gesture =new GestureDetector(this); this.context =context; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} public void surfaceCreated(SurfaceHolder holder) { if(!this.vThread.isAlive()) { this.vThread =new TestViewThread(this); this.vThread.setRunning(true); this.vThread.start(); } } public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; if(this.vThread.isAlive()) { this.vThread.setRunning(false); while(retry) { try { this.vThread.join(); retry =false; } catch(Exception ee) {} } } this.vThread =null; this.context =null; } public boolean onTouchEvent(MotionEvent event) { return this.gesture.onTouchEvent(event); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { } @Override public void onDraw(Canvas canvas) { canvas.drawColor(Color.RED); } @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } @Override public void onLongPress(MotionEvent e) {} @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onShowPress(MotionEvent e) {} @Override public boolean onSingleTapUp(MotionEvent e) { Intent helpScreenIntent =new Intent(this.context, MemoryLeakActivity.class); this.context.startActivity(helpScreenIntent); if (this.context instanceof Activity) ((Activity) this.context).finish(); return true; } } </code></pre> <p>--Edit-- I made changes to the view class to its surfaceDestroyed(SurfaceHolder holder) so that it will set the view that the thread has to null when the thread is told to stop. The changes i made are</p> <pre><code>public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; if(this.vThread.isAlive()) { this.vThread.setRunning(false); while(retry) { try { this.vThread.join(); retry =false; } catch(Exception ee) {} } this.vThread.setRunning(false, null); } this.vThread =null; this.context =null; this.gesture =null; } </code></pre> <p>you also need to change the surfaceCreated(SurfaceHolder holder) method to</p> <pre><code>public void surfaceCreated(SurfaceHolder holder) { if(!this.vThread.isAlive()) { this.vThread =new MemoryLeakViewThread(); this.vThread.setRunning(true, this); this.vThread.start(); } } </code></pre> <p>then on the thread class we need to change the following</p> <pre><code>public MemoryLeakViewThread() { } public void setRunning(boolean run) { this.run =run; } public void setRunning(boolean run, MemoryLeak view) { this.run =run; this.view =view; } </code></pre> <p>By doing this it seemed to of fixed the problem, the only problem now is the thread seems to stay in memory, due to the thread class and thread group. But im thinking this might be due to the debugger.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload