作者: ドラマー
序文
これら 2 つのクラスは、ActivityThread と ViewRootImpl です。これらに触れることができない理由は、これら 2 つのクラスまたはそのオブジェクトを通常の方法で参照したり、メソッドを呼び出したり、プロパティを直接取得したりすることができないためです。しかし、実際にはどこにでも存在しており、アプリケーション開発においても密接に関係していることが多く、これらを読んで内部実装をマスターすることは、Android の動作メカニズムを理解する上で非常に有益です。この記事では、よく問い合わせられるいくつかの側面から ViewRootImpl について説明しようとします。
1 ViewRootImpl はどこから来たのですか?
1 つ目は、android.view パッケージの下にある ViewRootImpl で、その場所から、おそらく View に関連していると推測できます。その機能は、Window と View の間のリンクであるという一文に要約できます。
一番馴染みのあるActivityから始めますが、ActivityのレイアウトViewの設定はsetContentView()メソッドを使っていることが分かりますが、このメソッドについては記事もたくさんあるので簡単に整理してみます。
- アクティビティ setcontentView() は内部的に getWindow().setContentView(layoutResID) を呼び出します。つまり、Window の setContentView メソッドを呼び出します。Android での Window の唯一の実装クラスは PhoneWindow、PhoneWindow setContentView であり、DecorView を初期化し、そのサブクラスとして設定した View を使用します。 。
- ActivityThread に注目してください。はい、これは先ほど述べたもう 1 人の主人公です。まず、彼の handleResumeActivity() メソッドに注目してください。その中のキー コードは次のとおりです。
public void handleResumeActivity(){
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
wm.addView(decor, l);
}
- WindowManager の実装クラス WindowManageImpl の addView メソッドは、mGlobal.updateViewLayout(view, params); を呼び出します。
- 最後に、WindowManagerGlobal の addView メソッドでそれを見つけました。
public void addView(){
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
まとめ
- このプロセスを整理すると、setContenview() は実際には View チェーンを Window の下にぶら下げているだけであり、View チェーンのルートは ViewRootImpl であることがわかります。
- Window を介して View と Activity を接続します。
- View チェーンの実際の追加操作は、最終的に WindowManagerGlobal に渡されて実行されます。
- もう 1 つの点: PopupWindow は基本的に、現在の Window の下に View チェーンをハングします。Lei Feng Tower に Lei Feng がないのと同じように、PopupWindow 自体には Window がありません。Dialog には独自のウィンドウがあります。この点については、ソース コードをチェックして検証できます。
2 ViewRootImpl View チェーン レンダリング用の転送ステーション
ビューのレンダリングは、大まかにレイアウトと描画の測定を通じて、上から下にレイヤーごとに開始され、ビュー チェーンのマネージャーは ViewRootImpl です。レンダリング アクションは、scheduleTraversals() メソッドを通じて開始されます。実際のイベントの進行はChoreographerにお任せください。Choreographer に詳しくない場合は、私の他の記事を参照してください。最後に、performTraversals() メソッドを実行します。
private void performTraversals(){
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
3 サブスレッドでViewを操作できない?
ViewRoot の RequestLayout には次のようなコードがあります。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
- テキストを TextView に設定するなど、View での操作は最終的に、上記のようなチェック ロジックを持つ ViewRootImpl の requestLayout() メソッドをトリガーします。これは、子スレッドでビューを更新できないとよく言われることです。
- 実はViewの操作は子スレッドでも実行できるのですが、Viewがマウントされていない場合という前提があります。View がマウントされていない場合、RequestLayout はトリガーされません。これは単なる通常の Java オブジェクトです。マウントロジックはどこにありますか?
4 ビューマウント
- ViewRootImplのperformTraversals()にこのコードがあります
java复制代码private void performTraversals(){
host.dispatchAttachedToWindow(mAttachInfo, 0);//此处的host为ViewGroup
}
- ViewGroup のdispatchAttachedToWindo() メソッドは、AttachInfo オブジェクトを各 View に割り当て、最終的にいわゆるマウントを実現します。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
- マウントされたViewに何らかのトラブルがあった場合、イベントは大きなbossViewRootImplに渡されます。
addView によって追加されたビューは、親ビューの mAttachInfo も受け取りますが、ここでは展開されません。
5 View.post() の Runnable はどこで最終的に実行されますか?
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
- 上記は View post() のコードです。マウントされた View が実装されている場合、ポスト受信メッセージは処理のために直接ハンドラーに渡され、それ以外の場合は HandlerActionQueue にポストされて後続のメッセージを待機することがわかります。処理。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
..
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);//内部也是调用handler.post()
mRunQueue = null;
}
..
}
最後に、これらの Runnable は、View がマウントされたとき、つまり、dispatchAttachedToWindow() メソッドで実行されます。
6 View.post が幅と高さを取得できる理由
-
これは拡張された問題であり、Activity の OnCreate() メソッドで幅と高さを直接取得することは不可能です。通常は、Runnable の view.post を使用して取得します。理由は、Activity onCreate が setContentView を渡すと、View が作成されるだけでマウントされず、onResume 中にマウントされ、アンマウントされた View は測定プロセスを経ないためです。
-
そして、post メソッドを通じて、前のセクションから、アンマウントされたビューにポストされた後、タスクはマウント後にハンドラーを通じて再ポストされることがわかります。
7 もう一つ注目すべき点
ViewRootImpl はレンダリングのための転送ステーションであるだけでなく、タッチ イベントのための転送ステーションでもあります。
ViewRootImpl は、ハードウェア センサーがタッチ イベントを受信し、それをアプリケーション ウィンドウに層ごとに配布するときに最初に停止します。なぜそんなことを言うのですか?証拠があるから~。これはViewRootのコードです
public void setView(){
..
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
}
- WindowInputEventReceiver は ViewRootImpl の内部クラスであり、入力イベントを受信した後、イベントを配信します。
- ここでのインスピレーションは、すべてのメイン スレッド タスクがハンドラー メカニズムを通じて実行されるわけではないということです。onTouch() イベントは最下層から直接コールバックされます。これは、前のフリーズ監視ソリューションで説明したソリューションの 1 つと同じです。 . onTouchEventモニター用です。
結び目
ViewRoot のコードは 10,000 行以上ありますが、この記事で分析したのは氷山の一角であり、直接研究すべき詳細がたくさんあります。
ViewRootImpl に関連するいくつかのポイントを通じて、お役に立てればと思い、ViewRootImpl について簡単に紹介および分析しました。
Androidの勉強メモ
Android パフォーマンスの最適化: https://qr18.cn/FVlo89
Android Vehicle: https://qr18.cn/F05ZCM
Android リバース セキュリティ調査ノート: https://qr18.cn/CQ5TcL
Android フレームワークの原則: https://qr18.cn/AQpN4J
Android オーディオとビデオ: https://qr18.cn/Ei3VPD
Jetpack (Compose を含む): https://qr18.cn/A0gajp
Kotlin: https://qr18.cn/CdjtAF
Gradle: https://qr18.cn/DzrmMB
OkHttp ソース コード分析ノート: https://qr18.cn/Cw0pBD
Flutter: https://qr18.cn/DIvKma
Android Eight Knowledge Body: https://qr18.cn/CyxarU
Android Core Notes: https://qr21.cn/CaZQLo
Android過去の面接の質問: https://qr18.cn/CKV8OZ
2023 年最新の Android 面接の質問集: https://qr18.cn/CgxrRy
Android 車両開発の就職面接の演習:https://qr18.cn/FTlyCJ
音声とビデオの面接の質問:https://qr18.cn/AcV6Ap