surfaceDestroyedメソッドには引数としてSurfaceHolderがある。
SurfaceHolderについては、surfaceCreatedメソッドや、surfaceChangedメソッドにおいて、lockCanvasメソッドを使ってcanvasに描画するのが主な使い方になる。
しかし、surfaceDestroyedメソッドは、終了をする時点で呼ばれるのであるため、こんな時点で描画を行っても、あまり意味は無い。
では、surfaceDestroyedメソッドの引数SurfaceHolderは何に使うのか。
これは、他のスレッドでcanvasを使わせることを阻止するための、排他制御の仕掛けとして使うのである。「描画をする」という目的では使わない。
Threadクラスでは、例えば、stopメソッドはdeprecatedであるとされている。スレッドを外部から止めることはできない。スレッドの外部で、何かフラグを立ててやることになる。
SurfaceViewが非表示になった後で、描画用スレッドがSurfaceViewのcanvasに書き込みを行うと、何か不都合が発生すると思われる。
この不都合を阻止するため、surfaceDestroyedメソッド内で、SurfaceHolderのlockCanvasメソッドを使って、canvasを排他的に取得し、取得後「SurfaceViewは終了しました」という趣旨のフラグ等を設定するのである。canvasには描画しない。
そうして、描画用スレッドでは、フラグを見て終了を検知し、canvasへの書き込みを行わないようにすれば良い。
この場合、描画用スレッドがcanvasに描画している最中に、surfaceDestroyedメソッドが呼ばれる、という事態も想定しなければならないと思われる。
下記のsample codeではBitmapを解放することを主眼にしてしまっているが、肝心なことは、描画用スレッドに対して終了を通知しcanvasへの書き込みを止めさせれば良いのである。
@Override
public void surfaceDestroyed(SurfaceHolder h) {
//Bitmapを解放することが目的である。
Canvas c = h.lockCanvas();//排他的にキャンバスを使う
if(c==null){//もし、他のスレッドでキャンバスを使っていた場合。
tDestroy = new Thread(this);
tDestroy.start();//このスレッド内でBitmapを解放する。
}
else{
go.Destroy();//Bitmapを解放する。
go = null;//終了を示すフラグを代入する。
h.unlockCanvasAndPost(c);
}
}
@Override
public void run(){
//tDestroyは、Bitmapを解放することを目的としている。
if(Thread.currentThread()==tDestroy){
Canvas c = getHolder().lockCanvas();
if(c==null){//他のスレッドがCanvasを使っている場合
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
}
tDestroy.start();//再度、呼び出す。
return;
}
go.Destroy();//Bitmapを解放する。
go = null;//終了を示すフラグを代入する。
getHolder().unlockCanvasAndPost(c);
return;
}
//tは、描画用のスレッドである。
if(Thread.currentThread()==t){
//終了スレッドが動いている場合、tスレッドではCanvasを使えないようにする。
if(tDestroy!=null) return;
Canvas c = getHolder().lockCanvas();
if(c==null) return;//他のスレッドがCanvasを使っている場合
if(go!=null
&& go.getBackground()!=null){
//まだ、稼働中である場合、描画する。
c.drawBitmap(go.getBackground(), 0, 0, null);
}
getHolder().unlockCanvasAndPost(c);
}
}
0 件のコメント:
コメントを投稿