我在
android中创建一个游戏,我注意到游戏有内存泄漏.我设法将内存泄漏隔离到一个较小的应用程序,以便我可以看到很好的尝试和解决,如何解决它.
该应用程序使用一个surfaceview作为其视图,并附加一个线程,以便完成所有绘图到屏幕.当我开始一个新活动并关闭我当前正在使用的活动时,会发生内存泄漏.当我在我的测试应用程序上执行内存转储时,我可以看到这一点,因为它只是打开并关闭一个活动(活动a – >活动b – >活动a).我有点想法如何解决这个问题,因为iv尝试将我创建的所有引用归零(在线程内),iv尝试在我销毁视图时从surfaceview中删除回调,并且在活动中,它似乎没有任何区别.
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,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 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 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,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; }
通过这样做似乎解决了问题,现在唯一的问题是线程似乎留在内存中,由于线程类和线程组.但我认为这可能是由于调试器.
解决方法
在onSurfaceCreated中创建新的Thread时,不应该在构造函数中创建新的Thread.将您的代码与我的示例进行比较:
How can I use the animation framework inside the canvas?