2011-08-29 51 views
8

Androidでゲームを作成していて、ゲームにメモリリークがあることがわかりました。 Ivはメモリリークをより小さなアプリケーションに分離することができたので、私はよく試してみることができ、解決する方法を知ることができます。Android Surfaceviewスレッドとメモリリーク

アプリケーションは、ビューのためにサーフェイスビューを使用し、すべての図面を画面に表示するためにスレッドにアタッチされています。メモリリークは、私が新しいアクティビティを開始し、現在使用しているアクティビティを閉じると発生します。私はテストアプリケーションでメモリダンプを行うと、そのすべてが開いていて、アクティビティを閉じている(アクティビティa - >アクティビティb - >アクティビティa)ので、これを見ることができます。 Ivのようなアイデアは、どのように私はこれを修正することができますか?私はビュー(スレッド内)に作成するすべての私の参照を無効にしようとしましたが、静的ビューからコールバックを削除しようとしました。活動の中で、それは何の違いもないようです。

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

--Edit-- それはビューを設定するように、私はそのsurfaceDestroyed(SurfaceHolderホルダ)にビュークラスを変更しましたスレッドが停止するように指示されたときに、スレッドはnullを返す必要があります。私が行った変更はあなたにも

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

にsurfaceCreated(SurfaceHolderホルダー)メソッドを変更する必要が

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

は、スレッドクラスに、私たちは、次の

public MemoryLeakViewThread() { 
    } 

    public void setRunning(boolean run) { 
     this.run =run; 
    } 

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

を変更する必要がありますこれを行うことで問題は解決されたように見えましたが、スレッドクラスとスレッドグループのためにスレッドがメモリ内に残っているように見えます。しかし、私はこれがデバッガのためかもしれないと考えています。

+0

例外とスタックトレースを追加すると、問題を知るのに役立ちます。 – Arslan

+0

Ivは、スレッドの実行状態を設定したときにビューを渡し、実行状態をfalseに設定したときにnullに設定することで、メモリリークの問題のほとんどを解決することができました。これにより、メモリからのアクティビティーとビューの両方が削除されました。スレッドグループクラス内でスタックされていると思われるスレッドだけが残っています。私はスレッドが何かを読んで覚えていますが開始されていない場合は、私はちょうど今それへのリンクを見つけることはできません。 – Spider

+0

http://code.google.com/p/android/issues/detail?id=7979 thats it。スタックトレースに関しては、どこに例外を追加したいですか?それは何も投げていない、まあそれは最終的にメモリが不足しているが、私のテストアプリケーションで多くのメモリを使用していないので、それはしばらく時間がかかります。私はメモリにスタックしているのを見ることができるように、MATを使用してヒープダ​​ンプを分析します。 – Spider

答えて

5

onSurfaceCreatedで作成するときに、コンストラクターに新しいスレッドを作成しないでください。コードと私の例を比較してください:How can I use the animation framework inside the canvas?

+0

あなたが言ったことを試して、コンストラクタメソッドからスレッドの作成を削除し、代わりにSurfacecreatedメソッドの中に配置しました。これにより、スレッドがメモリにスタックされなくなりました。 :D私の記憶の問題はすべて修正されました。 – Spider

-1

あなたがここに見ることができるように:アンドロイドのメモリリークを開始する

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

最も簡単な方法は、アプリケーションコンテキストの代わりにビューのコンストラクタ全体の活動を渡すことです。この一つに

setContentView(new MemoryLeak(this)); 

:あなたはこの行を変更しようとしてい

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

希望します。

+1

あなたは明らかにそれを試していないか、静的でないメソッドへの静的参照をコンパイルしようとすると何が起こったのか分かりました。 – NickT

関連する問題