Android Surfaceview线程和内存泄漏

我在Android中创build一个游戏,我注意到游戏有一个内存泄漏。 IV设法隔离内存泄漏到一个较小的应用程序,以便我可以看到很好的尝试和解决,如何解决它。

应用程序为其视图使用曲面视图,并将线程附加到该视图上,以便将所有绘图执行到屏幕上。 内存泄漏发生在我开始一个新的活动,并closures一个即时通讯使用。 我可以看到这一点,当我做我的testing应用程序的内存转储,因为它只是打开和closures一个活动(活动a – >活动b – >活动a)。 Iv种类的想法,我怎么可以解决这个问题,作为四尝试null我所有的引用,我创build视图(内线程),四尝试删除从SurfaceView的callback,当我摧毁了视图,也在活动内部,似乎没有任何区别。

MemoryLeakActivity.java

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)); } } 

MemoryLeakViewThread.java

 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; } } 

MemoryLeak.java

 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; } } 

TestActivity.java

 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)); } } 

TestViewThread.java

 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; } } 

Test.java

 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; } } 

– 编辑 – 我做了更改视图类的SurfaceDestroyed(SurfaceHolder持有人),以便它将设置线程必须为null,当线程被告知停止的视图。 我做的改变是

 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; } 

您还需要将surfaceCreated(SurfaceHolder holder)方法更改为

 public void surfaceCreated(SurfaceHolder holder) { if(!this.vThread.isAlive()) { this.vThread =new MemoryLeakViewThread(); this.vThread.setRunning(true, this); this.vThread.start(); } } 

那么在线程类中我们需要改变下面的内容

 public MemoryLeakViewThread() { } public void setRunning(boolean run) { this.run =run; } public void setRunning(boolean run, MemoryLeak view) { this.run =run; this.view =view; } 

通过这样做似乎可以解决问题,现在唯一的问题是线程似乎留在内存中,由于线程类和线程组。 但我想这可能是由于debugging。

Solutions Collecting From Web of "Android Surfaceview线程和内存泄漏"

在onSurfaceCreated中创build它时,不应在构造函数中创build新的Thread。 比较你的代码和我的例子: 如何在canvas内使用animation框架?

正如你所看到的:

http://developer.android.com/resources/articles/avoiding-memory-leaks.html

在Android中启动内存泄漏的最简单方法是将视图的构造函数传递给整个活动,而不是应用程序上下文。 你有没有尝试改变这一行:

 setContentView(new MemoryLeak(this)); 

进入这一个:

 setContentView(new MemoryLeak(Context.getApplicationContext())); 

希望能帮助到你。