26

ビュー階層が描画されているときにjava.lang.StackOverflowErrors が発生します。

at android.view.View.draw(View.java:6880)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6883)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
...

調査によると、私のビュー階層は深すぎて Android では処理できません。実際、Hierarchy Viewerを使用すると、最長のネストが19ビュー (!)であることがわかります。

私のアプリは、Google Play ストア アプリに似ています (スワイプ タブ付き)。すべてのタブは、v4 サポートと HoloEverywhere を使用して、フラグメント ビュー ページャー内のネストされたフラグメントです。明らかに、これが私のヒエラルキーがおかしくなった理由です。

私の質問:

  1. 実際のスタックサイズの制限は? UI スレッドのスタック サイズを測定する方法が見つかりませんでした。ネット上では 8KB という噂もありますが、一部のサンプル デバイスでこれを正確に測定する方法はありますか?

  2. OSのバージョンによってスタックサイズの制限は変わりますか? 同じ階層が4.0.3 デバイスではクラッシュしませんが、2.3.3 デバイス (同一のハードウェア) ではクラッシュします。何故ですか?

  3. 階層を手動で最適化する以外に解決策はありますか? UI スレッドの途方もなく小さいスタックを増やす方法が見つかりませんでした。申し訳ありませんが、60 ~ 100 のスタック フレーム制限は冗談です。

  4. #3 に奇跡的な解決策がないと仮定すると、コア階層の最適化をどこで行うべきかについての推奨事項はありますか?

  5. クレイジーなアイデア - すべてのビュー レイヤーが約 3 つの関数呼び出し (View.draw、ViewGroup.dispatchDraw、ViewGroup.drawChild) を追加することに気付きました。draw() 中のスタックの無駄が少ない独自の ViewGroup 実装 (カスタム レイアウト) を作成できるのではないでしょうか?

4

5 に答える 5

27

メイン スレッドのスタックは JVM (Android の場合は Dalvik JVM) によって制御されていると思います。私が間違っていなければ、関連する定数はdalvik/vm/Thread.h以下にあります#define kDefaultStackSize

dalvik ソース履歴からスタック サイズを参照する:

したがって、ネストされたビューをいくつ持つことができますか:

正確に言うことは不可能です。スタックサイズが上記のとおりであると仮定すると、それはすべて、最も深いネストにある関数呼び出しの数 (および各関数が取る変数の数など) によって異なります。問題のある領域は、 から始まるビューが描画されているときのようですandroid.view.ViewRoot.draw()。各ビューはその子の描画を呼び出し、最も深い入れ子と同じくらい深くなります。

少なくとも、上記のすべての境界グループに表示されるデバイスに対して実証テストを実行します。エミュレーターを使用すると正確な結果が得られるようです (ただし、ネイティブの x86 エミュレーターと実際のデバイスを比較しただけです)。

実際のウィジェット/レイアウトの実装方法の最適化もこれに影響する可能性があることに注意してください。そうは言っても、スタック スペースのほとんどは、約 3 つのネストされた関数呼び出しを追加するすべてのレイアウト階層によって消費されると思いdraw()ます。dispatchDraw()drawChild()

于 2013-06-04T14:16:53.833 に答える
2

これこれを試してみると、多くの質問が解決され、uithread でより大きなスタックが実際には必要ない理由をさらに理解するのに役立ちます。それが役に立てば幸い!

于 2013-06-10T07:32:04.360 に答える
1

スタック サイズの制限が何であるかはわかりません。率直に言って、それを検索してもあまり役に立たないと思います。2番目の提案が示唆するように、デバイスに存在するAndroidおよび/またはDalvik VMのバージョンに依存する可能性が非常に高い.

レイアウトの最適化に関しては、次のようなオプションがあります。

  1. ViewGroups、特に LinearLayouts 内の LinearLayouts をネストする代わりに RelativeLayout を使用して、ビュー階層をフラット化します。これは万能のソリューションではありません。実際、RelativeLayout をネストすると、パフォーマンスが低下する可能性があります (RelativeLayout は常にmeasure()2 回実行されるため、ネストすると測定フェーズに指数関数的な影響が及ぶためです)。

  2. 5 番目の質問に従って、カスタム Views/ViewGroups を使用します。これを行うアプリがいくつかあると聞いたことがあります。Google アプリの一部でさえこれを行うと思います。

  3. ビュー階層に役に立たない子が見つかった場合は<merge>、いくつかのレイアウトでタグを使用してみてください (ただし、私自身はそれらの使用法をあまり見つけていません)。
于 2013-05-30T18:35:13.300 に答える
0

クレイジーなアイデア 5 - それほどクレイジーではないかもしれません。私はあなたに理論を説明し、あなたはそれを何らかの方法で実装しようとします。3 つの入れ子になったビュー A > B > C があるとします。C を B に入れ子にする代わりに、D に入れ子にし (関係のないビュー)、B が自分自身を描画するときは、B.draw() を呼び出す必要があります。もちろん、あなたが遭遇するかもしれない問題は悪いレイアウトです。しかし、それに対する解決策を見つけることは可能です。

于 2013-06-09T10:24:25.527 に答える
0

私のクレイジーなアイデアのより良い説明 5. それが良いアイデアだと言っているわけではありません :) しかし、どうすればそれができるのかを明確にしたかったのです。

基本レイアウト (LinearLayout、FrameLayout、RelativeLayout) の独自の実装を作成し、オリジナルの代わりに使用します。

新しいレイアウトは元のレイアウトを拡張しますが、draw()機能をオーバーライドします。

元の draw 関数はdispatchDraw()which を呼び出しますdrawChild()- そして、draw()あなたはあなたの子供の に到達します。これは、draw()ネストごとに 3 つの関数呼び出しが追加されることを意味します。手動で 1 つの関数呼び出しに最小化しようとします。

これは嫌な部分です。inline関数に精通していますか?dispatchDraw()理論的には、drawChild()インラインで呼び出しを試みます。Javaは実際にはインラインをサポートしていないため、手動でインラインにするのが最善の方法です(実際には、内部のコードをコピーして単一の嫌な関数に貼り付けます)。

これはどこで少し複雑になりますか?私たちが採用する実装は、Android ソースから取得されます。問題は、これらのソースが Android のバージョン間でわずかに変更されている可能性があることです。したがって、これらの変更をマッピングし、元のコードとまったく同じように動作するようにコードに一連のスイッチを作成する必要があります。

あまりにも多くの仕事のようで、それはハックの1つの地獄です..

于 2013-06-09T10:53:18.407 に答える