Android的Surfaceview线程和内存泄漏线程、内存、Android、Surfaceview

2023-09-05 00:25:11 作者:心许君兮君知否

林创建一个游戏在Android中,我注意到游戏中有内存泄漏。四,设法隔离内存泄漏到一个较小的应用程序,这样我可以看得很清楚尝试找出,如何解决它。

Im creating a game in android, and i noticed that the game has a memory leak. Iv managed to isolate the memory leak into a smaller application so that i can see well try and work out, how to fix it.

该应用程序使用surfaceview其观点和具有连接到,为了完成所有的绘制到屏幕上一个线程。当我开始一个新的活动,关闭,即时通讯目前正在使用一个内存泄漏发生。我可以看到这个的时候我做了内存转储在我的测试应用程序,它所做的只是打开和关闭的活动(活动一个 - > b活动 - >活性)。四样的跑出来的想法如何,我可以解决这一问题为IV尝试归零我所有的引用,我做创造的视图(内螺纹),IV试图从surfaceview取出回调,当我摧毁的观点,同时也活动里面,它似乎没有任何区别。

The application uses a surfaceview for its view and has a thread attached to that in order to do all the drawing to the screen. The memory leak happens when i start a new activity and close the one that im currently using. I can see this when i do a memory dump on my test application as all it does is open and close an activity (activity a -> activity b -> activity a). Iv kind of ran out of ideas as to how i can fix this as iv tried nulling all my references that i do create to the view (inside the thread), iv tried removing the callback from the surfaceview when i destroy the view, and also inside the activity, it doesn't seem to make any difference.

MemoryLeakActivity.java

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

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

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

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

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

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持有人),使其设置的线程为空当线程被告知要停止的观点。我所做的更改

--Edit-- I made changes to the view class to its surfaceDestroyed(SurfaceHolder holder) so that it will set the view that the thread has to null when the thread is told to stop. The changes i made are

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持有人)的方法来改变

you also need to change the surfaceCreated(SurfaceHolder holder) method to

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

然后在线程类中,我们需要改变以下

then on the thread class we need to change the following

public MemoryLeakViewThread() {
    }

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

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

这样做似乎给解决了这一问题,唯一的问题现在是线程似乎停留在内存中,由于Thread类和线程组。但是我想这可能是由于调试器。

By doing this it seemed to of fixed the problem, the only problem now is the thread seems to stay in memory, due to the thread class and thread group. But im thinking this might be due to the debugger.

推荐答案

您不应该当你在onSurfaceCreated创建它的构造函数创建新的主题。比较你的code,以我的例子:How我可以用帆布内的动画框架?

You should not create new Thread in the constructor when you are creating it in onSurfaceCreated. Compare your code to my example: How can I use the animation framework inside the canvas?