2011-08-29 4 views
8

게임을 안드로이드로 생성하면 게임에 메모리 누수가있는 것으로 나타납니다. Iv는 메모리 누수를 작은 응용 프로그램으로 격리하여 잘 보면서 해결할 수 있도록했습니다.Android Surfaceview 스레드 및 메모리 누출

응용 프로그램이보기에 표면보기를 사용하고 모든 그림을 화면에 표시하기 위해 스레드에 첨부되어 있습니다. 새 액티비티를 시작하고 현재 사용중인 메신저를 닫으면 메모리 누수가 발생합니다. 모든 것이 열려 있고 활동 (활동 a -> 활동 b -> 활동 a)이므로 내 테스트 응용 프로그램에서 메모리 덤프를 할 때 이것을 볼 수 있습니다. 이베 일종의 아이디어가 다 떨어졌을 때 iv에서 이것을 고칠 수 있었는데 뷰에서 (thread 내에서) 만드는 모든 참조를 null로 만들려고했는데 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, 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)); 
    } 
} 
이 뷰를 설정되도록 0

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-- 는 I는 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. 스택 추적에 관해서는 어디에서 예외를 추가할까요? 그것은 아무 것도 던지지 않고 있습니다. 결국 메모리가 부족 해지면 좋겠지 만, 테스트 애플리케이션으로 많은 메모리를 사용하지 않으므로 시간이 걸릴 것입니다. 메신저를 사용하여 힙 덤프를 분석하여 메모리에 갇혀있는 것을 볼 수 있습니다. – 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