facebook twitter youtube
Salesforce Java Android PHP JavaScript MySQL Perl node.js HTML iOS
in Android - 24 1月, 2016
by sato - no comments
Androidの描画処理(Activity.setContentView())続き

前回途中まで確認したsetContentView()の動作の続きです。

前回最後に確認したメソッドはLayoutInflaterのinflate()で処理としては下記です。


    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
~~~~~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~~~~~~~
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

root.addView()の中身から追いかけていきます。
addView()の中身はこんな感じです。


    public void addView(View child, LayoutParams params) {
        addView(child, -1, params);
    }

    public void addView(View child, int index, LayoutParams params) {
        if (DBG) {
            System.out.println(this + " addView");
        }
        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

ここでは3つの処理を行っています。
requestLayout()、invalidate()、addViewInner()
そのうちの2つのrequestLayout()とinvalidate()は描画要求をする処理として利用したことがある方もいると思います。

今回はrequestLayout()から追いかけていきます。
これはViewGroupの中でオーバーライドされていないので、親クラスであるViewの処理を見に行きます。


    public void requestLayout() {
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
    }

ここでは描画用のフラグを立てた後親のViewに対しrequestLayout()を呼び出しています。
Viewクラスはそれぞれ親のViewを持っているため、子から親に向かって描画要求が上がっていきます。
最終的にはViewRootImplと言うクラスがすべてのViewの親となるためViewRootImplのreequestLayout()を見ます。


    @Override
    public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            scheduleConsumeBatchedInput();
        }
    }

まず、checkThread()でこの処理が実行されているスレッドがUIスレッドか確認します。
その後レイアウト要求フラグをtrueに変更し、scheduleTraversals()で描画処理を予約します。
scheduleTraversals()の中では、UIスレッドのLooperからBarrierを取得し、UIスレッドへの要求を止めた後、mTraversalRunnableをpostし描画処理を実行します。
ChoreographerのpostCallback()内ではHandlerに対しBarrierを無視するようにMessageを投げています。
HandlerからmTraversalRunnableが実行されると描画処理が始まり、画面表示が変更されます。

Handler経由で描画処理を実行しているため、前の処理がHandlerで実行中だと描画処理は即時実行されません。
そのためAndroidでは描画処理を即時実行することが出来ず、Android OS側のタイミングにより処理が実行されるといわれます。