Android Surfaceview Threads и утечки памяти

Я создаю игру на android и заметил, что в игре утечка памяти. Ив сумел изолировать утечку памяти в меньшем приложении, чтобы я мог хорошо видеть, попытаться разобраться, как это исправить.

Приложение использует вид поверхности для своего представления и имеет поток, прикрепленный к нему, чтобы делать все рисунок на экран. Утечка памяти происходит, когда я начинаю новое действие и закрываю тот, который я использую в данный момент. Я вижу это, когда делаю дамп памяти в своем тестовом приложении, поскольку все, что он делает, - это открывать и закрывать действие (действие a -> действие b -> действие a). Iv вроде как исчерпал идеи относительно того, как я могу это исправить, поскольку iv попытался обнулить все мои ссылки, которые я создаю для представления (внутри потока), 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;
    }
}

- Править-- Я внес изменения в класс представления в его surfaceDestroyed (держатель 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;
    }

, вам также необходимо изменить метод 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;
    }

. Сделав это, казалось, что проблема решена, единственная проблема теперь заключается в том, что поток, кажется, остается в памяти из-за класса потока и группы потоков. Но я думаю, что это могло быть из-за отладчика.

8
задан Spider 29 August 2011 в 11:37
поделиться