2011-08-29 55 views
8

Im tạo trò chơi trong android và tôi nhận thấy rằng trò chơi có rò rỉ bộ nhớ. Iv quản lý để cô lập rò rỉ bộ nhớ vào một ứng dụng nhỏ hơn để tôi có thể nhìn thấy cũng cố gắng và làm việc ra, làm thế nào để sửa chữa nó.Android Surfaceview Chủ đề và rò rỉ bộ nhớ

Ứng dụng sử dụng trình xem lướt sóng cho chế độ xem của nó và có một chuỗi được gắn vào đó để thực hiện tất cả bản vẽ trên màn hình. Việc rò rỉ bộ nhớ xảy ra khi tôi bắt đầu một hoạt động mới và đóng một hoạt động hiện đang sử dụng. Tôi có thể thấy điều này khi tôi làm một bãi chứa bộ nhớ trên ứng dụng thử nghiệm của tôi như tất cả nó là mở và đóng một hoạt động (hoạt động a -> hoạt động b -> hoạt động a). Iv loại hết ý tưởng như thế nào tôi có thể sửa chữa này như iv đã cố gắng nulling tất cả các tài liệu tham khảo của tôi mà tôi tạo ra để xem (bên trong thread), iv đã cố gắng loại bỏ các cuộc gọi lại từ surfaceview khi tôi phá hủy xem, và cũng bên trong hoạt động, nó dường như không tạo ra bất kỳ sự khác biệt nào.

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-- tôi đã thực hiện thay đổi đối với lớp nhằm surfaceDestroyed (giữ SurfaceHolder) của nó để nó sẽ thiết lập các quan điểm rằng thread phải null khi thread được yêu cầu dừng lại. Những thay đổi tôi đã làm là

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

bạn cũng cần phải thay đổi phương pháp surfaceCreated (giữ SurfaceHolder) để

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

sau đó trên lớp chủ đề chúng ta cần phải thay đổi sau

public MemoryLeakViewThread() { 
    } 

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

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

Bằng cách này nó dường như cố định vấn đề, vấn đề duy nhất bây giờ là thread dường như ở lại trong bộ nhớ, do lớp thread và nhóm thread. Nhưng im nghĩ rằng điều này có thể là do trình gỡ lỗi.

+0

Thêm ngoại lệ và theo dõi ngăn xếp sẽ giúp biết sự cố. – Arslan

+0

Iv quản lý để sửa chữa hầu hết các vấn đề rò rỉ bộ nhớ bằng cách đi qua khung nhìn khi tôi thiết lập trạng thái đang chạy của luồng, và sau đó thiết lập nó thành null khi tôi đặt trạng thái đang chạy thành false. Điều này đã loại bỏ cả hoạt động và chế độ xem từ bộ nhớ, bây giờ điều duy nhất nằm ở đó là chuỗi có vẻ bị kẹt bên trong lớp threadgroup. Tôi nhớ đọc một cái gì đó về chủ đề sẽ gặp khó khăn trong đó nếu không bắt đầu tôi chỉ không thể tìm thấy liên kết đến nó ngay bây giờ. – Spider

+0

http://code.google.com/p/android/issues/detail?id=7979 thats it. Đối với ngăn xếp dấu vết, bạn muốn tôi thêm ngoại lệ vào đâu? Nó không phải là ném bất kỳ, nó sẽ cuối cùng khi nó hết bộ nhớ, nhưng như im không sử dụng nhiều bộ nhớ với các ứng dụng thử nghiệm của tôi nó sẽ mất một lúc. Im sử dụng MAT để phân tích một đống đống để tôi có thể nhìn thấy những gì đang gặp khó khăn trong bộ nhớ – Spider

Trả lời

5

Bạn không nên tạo Chủ đề mới trong hàm tạo khi bạn tạo nó trong onSurfaceCreated. So sánh mã của bạn với ví dụ của tôi: How can I use the animation framework inside the canvas?

+0

Hãy thử những gì bạn nói và loại bỏ việc tạo ra các sợi ra khỏi phương pháp xây dựng và đặt nó bên trong phương pháp Surfacecreated thay thế. Điều này đã ngăn chặn các chủ đề bị kẹt trong bộ nhớ. : D tất cả các vấn đề bộ nhớ của tôi được cố định ngay bây giờ. – Spider

-1

Như bạn có thể thấy ở đây:

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

Cách dễ nhất để bắt đầu một rò rỉ bộ nhớ trong Android là phải vượt qua constructor của một cái nhìn toàn bộ hoạt động thay vì bối cảnh ứng dụng. Bạn đã cố gắng thay đổi dòng này:

setContentView(new MemoryLeak(this)); 

thành một này:

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

?

Hy vọng điều đó sẽ hữu ích.

+1

Vâng, bạn rõ ràng đã không thử nó, hoặc bạn đã có thể nhìn thấy những gì sẽ xảy ra khi bạn cố gắng biên dịch một tham chiếu tĩnh đến một phương pháp không tĩnh. – NickT