2012-04-28 2 views
8

私はSurfaceViewの拡張機能を持っていますが、その裸の骨はLunar Landerのように実装されています。null SurfaceViewスレッドのキャンバスが停止しているにも関わらず、surfaceDestroyed()のスレッドはAndroid 4/ICSでのみ

public void run() { 
    while (mRun) { 
     Canvas c; 
      try { 
       c = mSurfaceHolder.lockCanvas(); 
       synchronized (mSurfaceHolder) { 
        doDraw(c); // Main drawing method - not included in this code snippet 
       }         
      } 

      finally { 
       // do this in a finally so that if an exception is thrown 
       // during the above, we don't leave the Surface in an 
       // inconsistent state 
       if (c != null) { 
        mSurfaceHolder.unlockCanvasAndPost(c); 
       } 
      } 
     } 
    } 
} 

そしてThreadは適切に表面が破壊されたときに停止されています:それは、図面Threadrun()方法は、本質的である、私は通常、現在までにテストしているデバイスでは

public void surfaceDestroyed(SurfaceHolder holder) { 
    // we have to tell thread to shut down & wait for it to finish, or else 
    // it might touch the Surface after we return and explode 
    boolean retry = true; 
    thread.setRunning(false); 
    while (retry) { 
     try { 
      thread.join(); 
      retry = false; 
     } 
     catch (InterruptedException e) { 
     } 
    } 
} 

(HTC Desire、Desire HD、Archos 101の間ではOS 2.2と2.3.3があります)、上記の問題は一度もありませんでした。つまり、ユーザがActivityを撤回したためにサーフェスが破棄されたり、Activityが呼び出されたりすると、のコードは、nullを返すように常にmSurfaceHolder.lockCanvas()が呼び出されないようにします。

私はアンドロイド4/ICSを実行している私の新しいHTCワンX上で見つけた違いは、しかし、方法surfaceDestroyed()の呼び出し(そのメソッド内すなわちコードがまだ実行されている)の間に私の描画Threadが与えられるだろうということですnullキャンバスmSurfaceHolder.lockCanvas()です。もちろん、これはアプリケーションクラッシュを引き起こします。私の一つのXで、これは表面が破壊されごと単一の時間起こるのだろう - などそれはActivityのバックアウト、電話を回転さに起因するものかどうかを、

私は下にあったので、私はこのことについて困惑しています印象はmSurfaceHolder.lockCanvas()nullCanvassurfaceDestroyed()まで実際にはから出てになるはずです。確かに、これはjavadocはこう言われる、今の

This is called immediately before a surface is being destroyed. After returning from this call, you should no longer try to access this surface. If you have a rendering thread that directly accesses the surface, you must ensure that thread is no longer touching the Surface before returning from this function.

私のソリューションは、ちょうどnullをチェックすることです。これは正常に動作します:

if(c != null){ 
    doDraw(c); // Main drawing method - not included in this code snippet 
} 

しかし、私は突然のAndroid 4/ICSのためにこれを行うために抱えている理由を任意のアイデア?

+0

ヌルを返すことは、表面に触れないようにするための方法です。そうすれば、うまくいくはずです。 Idkなぜ、または基本的な実装で変更されたか。 – zapl

+0

ありがとう、zapl。ええ、それは本当に奇妙です。この実装の明らかな変化を引き起こすために私がやっているどこかで奇妙なことがない限り、確かにSurfaceViewベースのアプリの多くはICSで死ぬだろう。 – Trevor

+0

ICSデバイスやエミュレータでもこのエラーが発生したため、誰も「正式な」理由がわかりません。 – Lenciel

答えて

5

私のコメントを詳しく説明すると、行動のこの変更に対して開いているバグチケットがあると思われます。ここにはhttp://code.google.com/p/android/issues/detail?id=38658があります。それがあなたに影響を与えた場合、それを見るだけの価値があるかもしれません。ちょうど重要なことです。

個人的には、最新のAndroid SDKに付属している月面着陸機の例を使用するだけで、私はこれを自分で見たことがあります。私はそれを私のHTC Sensation XE(4.0.3)に置きました。向きが変わると、表面が破壊される前にいくつかのヌルキャンバスが返されます。

私が使用する回避策は、更新とレンダリングのメソッドに渡す前に、キャンバスがnullでないことを再確認することです。

アンディ

+0

面白い!私もこの問題を抱えていて、同様の方法でそれを周回しました。唯一の違いは、実際に私のdrawメソッド内にヌルキャンバスのチェックを入れたことです:-) – Zippy

+0

これは数日間同じ問題で苦労して見つけました!どうもありがとう!私はnullをチェックすることについて考えましたが、nullをチェックするのはちょっと面倒なので別の解決策を探し続けましたが、私たちはそれをやり直さなければならないと思います。 –

0

小さな実験をしました。私はこのようなsurfaceDestroyed()方法でいくつかの通知フラグを置く:私が見つけた何

public void surfaceDestroyed(SurfaceHolder holder) { 
Log.i("i","i have started"); 
...[the code]... 
Log.i("i","i have finished"); 
} 

nullPionterExceptionOccursは、第1フラグの後に、しかし二番目の前に発生しているという事実です。あなたの質問のための最も可能なanwserは、古いビューが到達可能なときにキャンバスをロックすると、あなたがそれを描くが、その間に画面が変化するということです。キャンバスのロックを解除すると、結果を表示する場所がなくなり、エラーが発生します。

P.S. サンプルのアンドロイドコードからLunar Landerを少し再生し、ゲームが動作している間に画面を回転しようとします。エラーが発生すると、背景ビットマップがどのように描画されるかを見てください。画面の向きが変わったことがわかりますが、プログラムは何も起こらないかのようにビットマップを描こうとしました。

関連する問題