Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android Surfaceview线程和内存泄漏_Android_Multithreading_Memory Leaks_Surfaceview - Fatal编程技术网

Android Surfaceview线程和内存泄漏

Android Surfaceview线程和内存泄漏,android,multithreading,memory-leaks,surfaceview,Android,Multithreading,Memory Leaks,Surfaceview,我正在用android创建一个游戏,我注意到这个游戏有内存泄漏。Iv设法将内存泄漏隔离到一个较小的应用程序中,这样我就可以很好地了解如何修复它 该应用程序使用surfaceview作为其视图,并在其上附加了一个线程,以便在屏幕上绘制所有图形。当我启动一个新活动并关闭当前正在使用的活动时,内存泄漏就会发生。当我在测试应用程序上进行内存转储时,我可以看到这一点,因为它所做的只是打开和关闭一个活动(活动a->活动b->活动a)。我已经没有办法解决这个问题了,因为我尝试了清空我对视图创建的所有引用(在线

我正在用android创建一个游戏,我注意到这个游戏有内存泄漏。Iv设法将内存泄漏隔离到一个较小的应用程序中,这样我就可以很好地了解如何修复它

该应用程序使用surfaceview作为其视图,并在其上附加了一个线程,以便在屏幕上绘制所有图形。当我启动一个新活动并关闭当前正在使用的活动时,内存泄漏就会发生。当我在测试应用程序上进行内存转储时,我可以看到这一点,因为它所做的只是打开和关闭一个活动(活动a->活动b->活动a)。我已经没有办法解决这个问题了,因为我尝试了清空我对视图创建的所有引用(在线程内部),我尝试了在销毁视图时从surfaceview中删除回调,在活动内部,这似乎没有任何区别

MemoryLeakaActivity.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));
    }
}
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));
    }
}
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;
    }
}
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;
    }
}
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.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));
    }
}
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.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;
    }
}
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 holder holder),以便在通知线程停止时将线程设置为null的视图。我所做的改变是

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();
        }
    }
然后在thread类上,我们需要更改以下内容

public MemoryLeakViewThread() {
    }

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

    public void setRunning(boolean run, MemoryLeak view) {
        this.run =run;
        this.view =view;
    }
通过这样做,似乎解决了这个问题,现在唯一的问题是线程似乎留在内存中,这是由于线程类和线程组。但我认为这可能是由于调试器造成的。

如您所见:

在Android中启动内存泄漏的最简单方法是将整个活动传递给视图的构造函数,而不是应用程序上下文。您是否尝试更改此行:

setContentView(new MemoryLeak(this));
在这一点上:

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


希望有帮助。

在onSurfaceCreated中创建新线程时,不应在构造函数中创建新线程。将您的代码与我的示例进行比较:

添加异常和堆栈跟踪将有助于了解问题。Iv在设置线程的运行状态时传递视图,然后在将运行状态设置为false时将其设置为null,从而修复了大多数内存泄漏问题。这已经从内存中删除了活动和视图,现在唯一留下的是线程,它似乎卡在了threadgroup类中。我确实记得读过一些关于线程的文章,如果没有开始,我现在就找不到它的链接。就这样。至于堆栈跟踪,您希望我在哪里添加异常?它不会抛出任何内存,但最终会在内存耗尽时抛出,但由于我的测试应用程序没有使用太多内存,因此需要一段时间。我使用MAT来分析堆转储,这样我就可以看到感兴趣的内存中卡住了什么。我运行了您的代码,并添加了一些日志记录。您说活动已被删除-但是,如果在命令行上运行“adb shell dumpsys meminfo memory.leak”,我会在每个交换机上看到一个新活动添加到堆栈中,即使调用了onDestroy(),线程循环似乎退出OK。所以我不知道如何修复它,但我认为这可能是对你有用的信息。是的,这是我的原始问题,我设法使它达到一个点,使线程只留在内存中,我认为这与调试器有关。你显然没有尝试过,或者,当您尝试编译对非静态方法的静态引用时,您会看到发生了什么。尝试您所说的,并从构造函数方法中删除线程的创建,将其放在Surfacecreated方法中。这阻止了线程卡在内存中:我所有的记忆问题现在都解决了。