3

View.getWidth()これは、メソッドとメソッドのスレッドセーフに関する簡単な質問View.getHeight()です。

View オブジェクトを指定して操作を実行するバックグラウンド タスクを作成しています。実際に必要なビューの唯一の部分は、その寸法です。ただし、タスクが開始されたときにビューがレイアウト プロセスを経ていない可能性があるため、その寸法が 0 に設定されています。タスクは UI で開始されますが、バックグラウンドで長い操作が実行されてから、結果を投稿する UI スレッド ( によく似ていますAsyncTask)。

バックグラウンド スレッドからこれらのゲッター メソッドを呼び出すと、どのような影響があるのだろうと考えていました。UI ツールキットがシングル スレッド用に設計されていることはわかっているため、安全でないパブリケーションのリスクを冒している可能性があります。

Kcoppock は、ディメンションがわかっているときに UI スレッドでコールバックを受け取ることによって、この問題をすべて回避する優れたソリューションを提供しています。

これらのディメンションがバックグラウンド スレッド (以下に添付) からいつ表示されるかを確認する小さなテストを書きましたが、コールバックと同時に利用できるようです。

もちろん、これが何の証明にもならないことは理解していますので、UI フレームワークについてより詳しい知識をお持ちの方に参加していただければ幸いです。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final ImageView view = new ImageView(this);
    view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Log.d("TAG", Thread.currentThread().getName() + ": onGlobalLayout()");
            Log.d("TAG", Thread.currentThread().getName() + ": view dimensions: " + view.getWidth() + "x" + view.getHeight());
        }
    });

    // Bad code follows
    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                Log.d("TAG", Thread.currentThread().getName() + ": view dimensions: " + view.getWidth() + "x" + view.getHeight());
                try {
                    Thread.sleep(1);
                } catch (InterruptedException ignored) {}
            }
        }
    }).start();

    setContentView(view);
    Log.v("TAG", "Set view as content");
}
4

1 に答える 1

2

Android UI フレームワークはシングルスレッド環境で動作するように設計されているため、内部同期はなく、すべての View クラスはスレッドセーフではありません。widthこれは、値が UI スレッドから設定された後、height値がバックグラウンド スレッドから見えるという保証がないことを意味します。古い値を受け取ったり、バックグラウンド スレッドで正の値をまったく受け取っていない可能性があります。Java のスレッドがメモリを介してどのように対話するかを説明するJava メモリ モデルがあり、さまざまなスレッドを介して変数を可視化するために何をする必要があるかを示しています。

あなたのコードはユニプロセッサ システムでは動作すると思いますが、SMP では失敗する可能性があります。これが深刻な問題だとは言えませんが、いずれにせよ、このコードは Java メモリ モデルに関して正しくありません。それを修正するにはwidthheight修飾子volatileまたはsynchronizedブロックまたはAtomicInteger. コードは次のようになります。

...
private volatile int width;
private volatile int height;
...
@Override
public void onGlobalLayout() {
    // here values are published safely
    width = view.getWidth();
    height = view.getHeight();
}
...
new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            // and here they are read
            Log.d("TAG", Thread.currentThread().getName() + ": view dimensions: " + width + "x" + height);
            try {
                Thread.sleep(1);
            } catch (InterruptedException ignored) {}
        }
    }
}).start();

volatilewidthモディファイアは、UI スレッドから行われたおよびheight変数の変更がバックグラウンド スレッドですぐに表示され、このバックグラウンド スレッドが最新の値を受け取ることを保証します。

于 2012-07-15T17:40:36.540 に答える