从另一个活动返回时 SurfaceView 崩溃
我的 SurfaceView 有一个线程问题,这很常见,但我还没有在任何地方找到解决方案。
我在框架布局中使用 SurfaceView 作为背景:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/blobmusic_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.myapp.Background
android:id="@+id/background_blobmusic"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
我的背景类是典型的 SurfaceView 类,带有支架,线程将调用我们的 onDraw 方法等。一些代码可以避免出现问题(在构造函数方法):
public void init() {
ready = false;
holder = getHolder();
holder.addCallback( new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
holder.setFormat(PixelFormat.RGBA_8888);
estrelles = new Estrelles(getResources(),getHeight());
bgdt.setRunning(true);
bgdt.start();
ready = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
和我的线程类(bgtd):
public class BGDrawThread extends Thread {
private Background view;
static final long FPS = 60;
private boolean running = false;
public BGDrawThread(Background view) {
this.view = view;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
long ticksPS = 1000/FPS;
long startTime;
long sleepTime;
//fps checker
long contms=0;
long lasttimecheck = System.currentTimeMillis();
int fps=0;
while (running) {
if(contms>1000) {
//souts ??
contms=0;
fps=0;
lasttimecheck = System.currentTimeMillis();
}
else {
fps++;
contms+=System.currentTimeMillis()-lasttimecheck;
lasttimecheck=System.currentTimeMillis();
}
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
}
这工作正常,直到另一个活动出现在前面,然后我们从它返回,崩溃。
这是我的 onpause/onresume 方法的代码类:
@Override
protected void onResume() {
super.onResume();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.resumethread();
}
@Override
protected void onPause() {
super.onPause();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.stopthread();
}
和SurfaceView 类中的 strop/resume 线程:
public void stopthread() {
if(!bgdt.isAlive()) return;
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public void resumethread() {
if(ready && !bgdt.isRunning()) {
bgdt.setRunning(true);
bgdt.start();
}
}
欢迎所有帮助:)
编辑:我让它运行(可能不是一个优雅的解决方案,但我将代码留在这里): (查看下面的评论以查看其他更改)
public void startthread() {
if(!surfacecreated) return;
if(threadrunning) return;
bgdt = new BGDrawThread(this);
bgdt.setRunning(true);
bgdt.start();
threadrunning = true;
}
public void stopthread() {
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
threadrunning = false;
retry = false;
} catch (InterruptedException e) {}
}
}
i have a thread problem with my surfaceView, its quite common but i have not found a solution anywhere.
I use a SurfaceView as a Background in a frame layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/blobmusic_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.myapp.Background
android:id="@+id/background_blobmusic"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
and my background class is the tipical surfaceview class, with the holder, the thread wich will call our onDraw method, etc. Some code to avoid questions (init() is called in the constructor methods):
public void init() {
ready = false;
holder = getHolder();
holder.addCallback( new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
holder.setFormat(PixelFormat.RGBA_8888);
estrelles = new Estrelles(getResources(),getHeight());
bgdt.setRunning(true);
bgdt.start();
ready = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
and my thread class (bgtd):
public class BGDrawThread extends Thread {
private Background view;
static final long FPS = 60;
private boolean running = false;
public BGDrawThread(Background view) {
this.view = view;
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run() {
long ticksPS = 1000/FPS;
long startTime;
long sleepTime;
//fps checker
long contms=0;
long lasttimecheck = System.currentTimeMillis();
int fps=0;
while (running) {
if(contms>1000) {
//souts ??
contms=0;
fps=0;
lasttimecheck = System.currentTimeMillis();
}
else {
fps++;
contms+=System.currentTimeMillis()-lasttimecheck;
lasttimecheck=System.currentTimeMillis();
}
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
}
This works fine, until another activity comes to front and then we come back from it, that crashes.
Here's my code of onpause/onresume methods of the class:
@Override
protected void onResume() {
super.onResume();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.resumethread();
}
@Override
protected void onPause() {
super.onPause();
Background bmbg = (Background) findViewById(R.id.background_blobmusic);
bmbg.stopthread();
}
and the strop/resume thread in the surfaceview class:
public void stopthread() {
if(!bgdt.isAlive()) return;
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
retry = false;
} catch (InterruptedException e) {}
}
}
public void resumethread() {
if(ready && !bgdt.isRunning()) {
bgdt.setRunning(true);
bgdt.start();
}
}
All help is welcome :)
EDIT: i made it run (probably not an elegant solution, but i leave the code here):
(look at the comment below to see the other changes)
public void startthread() {
if(!surfacecreated) return;
if(threadrunning) return;
bgdt = new BGDrawThread(this);
bgdt.setRunning(true);
bgdt.start();
threadrunning = true;
}
public void stopthread() {
boolean retry = true;
while (retry) {
try {
bgdt.setRunning(false);
bgdt.join();
threadrunning = false;
retry = false;
} catch (InterruptedException e) {}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

IIRC 表面仅在 surfaceChanged() 回调之后才准备好进行操作,而不是 surfaceCreated() ,并且在 onResume() 之后调用 surfaceChanged() - 将初始化和线程启动移动到那里(到 surfaceChanged() )
IIRC surface is ready for manipulations only after surfaceChanged() callback, not surfaceCreated() , and surfaceChanged() is called after onResume() - move your initialisation and thread startup there ( to surfaceChanged() )