Androidのアプリケーションパフォーマンスを最適化する5つのステップ

この短い記事では、例としてゲーム「コロナアンバー」のデジタルバージョンを作成することにより、5つの簡単な手順でAndroidアプリケーションのパフォーマンスをプログラムで最適化する方法に関する経験を共有したいと思います。



ビューとレイアウトの複雑な構造を備えた本格的なアプリケーションを作成する前に、「外観、レイアウトからデザインをスケッチした」というスタイルのシンプルで論理的なアクションが、プログラム全体の作業を大幅に遅らせることを考えていませんでした。



とりわけ、「Crown of Amber」のタスクは、Androidプラットフォームに移行する予定のゲーム自体が、何らかの形で競技場やその隣に収まらなければならないさまざまなコンポーネントでかなり飽和しているという事実によって複雑になりました。



この記事では、成功した経験を収集し、便利で読みやすい形式にしました。「Android向けにアプリケーションをプログラムで最適化する方法」または「アプリケーションが遅れる理由」をGoogleで検索する人にとっては便利です。



だから。 開始点(最適化前の外観)



アプリケーションの設計は、約200個のビューと1ダースのレイアウトの「正しい」ヒープから作成されました。 競技場のロードには約5秒かかり、ほぼすべてのアクションがさらに1〜2秒間停止しました。 また、プログレッシブデバイスとエミュレータでラグがほとんど見えない場合、ほとんどの最新のデバイスでは状況がかなり悲しく見えます。



画像



これが私にも、私たちのチームにも、テスターに​​も適していないことは明らかです。 そして、すべてが最適化されていることを心から確信していましたが、情報を探し始めました。



私が共有したい知識の一部は、ロシア語字幕付きのクールなビデオチュートリアル(推奨)、一部-Habré、一部-同じGoogle Developersサイトのインターネットの奥深くにあります。



さらに、新しい部分を受け取って次の最適化を実行するたびに、「今はどこにも行けず、速くはありません」と確信しており、各ステップでより多くの新しいものを発見しました。



そして、これはそれが起こった方法です。



ステップ1.測定



実際、これはステップではなく、パフォーマンスを継続的に測定するための永続的な推奨事項です。 このために、Android Studioはいくつかの特別なプログラムを提供します。HierarhyViewer、SystemTracing、Method Profilingを備えたAndroidデバイスモニター。 メモリ、CPU、GPUモニターを備えたAndroidモニター。 この記事の目的は、この記事の目的ではありません。ここ、Habré、または言及されたビデオチュートリアルで簡単にガイドを見つけることができます。



一番下の行は、パフォーマンスを最適化するために、最初に「つまずく」場所を理解する必要があるということです。



ただし、測定しなくても、すべての開発者が注意を払う必要があるいくつかのことがあります。



ステップ2.階層の最適化と減量



表示されたレイアウトを再カウントおよび再描画すると、アプリケーションのパフォーマンスが主に失われます。 さらに、レイアウトが「重い」ほど、インジケーターの再計算が長くなります。 そして、ここで次の点に注意する必要があります。



-階層内で、パラメータ「重量」(重量)でLinearLayoutをネストしないでください。 このようなレイアウトの測定には、2つ(3つ、4つ!-入れ子に応じて)時間がかかります。 この構造をGridLayout(または21未満のAPIの場合はsupport.v7.GridLayout)に置き換えることをお勧めします。

-一部のLinearLayoutはRelativeLayoutに置き換えることができます。これにより、たとえば 、追加の添付ファイルを削除できます。

-RelativeLayoutも2回測定されることが判明しました。 可能な場合-より「軽量」なFrameLayoutに置き換える必要があります。



例:



マークアップを検討した結果、重みパラメーターを使用して最大4(!)回ネストされたLinearLayoutがあり、競技場はほぼ100のRelativeLayout(フィールドセル)で構成されており、一部のレイアウトはまったく必要ないことがわかりました。



(緑は受け入れ可能な投資を示し、黄色は望ましくない投資を示し、オレンジは危険な投資を示し、赤は「これをしない!」を示します。)



画像



その後、構造を少し作り直しました。 「文字」は、RelativeLayoutを使用して別のフラグメントで取り出され、ネストされたすべてのLinearは(1!)Support.v7.GridLayoutに置き換えられ、すべてのRelativeLayout(画面には表示されません-これらはフィールドセル)はFrameLayoutに置き換えられました。 よりきれいで生産性の高い結果が得られました。



画像



しかし、これは問題を最後まで解決しませんでした。



ステップ3.余分な再描画(オーバードロー)



Androidは、画面上の各ビューの各画像、各背景を慎重に描画することが判明しました。 ここで彼は、もちろん、よくやった。 しかし、一部の画像が他の画像と重なり、背景が重なり、このレンダリングの一部が完全に不要になった場合はどうでしょうか? そうです-表示されていない必要のないものはすべてオフにしてください。



再描画オーバーレイの測定は非常に簡単です。デバイスの[開発者パラメーター]で[GPU超過を表示]を有効にし、アプリケーションを実行して画面を見る必要があります。



ビュー要素の色は、あなたがどれだけ良いか(または悪いか)を示します。



色はレベル0、「完璧」です

青色-レベル1、「通常。

緑色-レベル2、「許容」。

淡い赤色-レベル3、「望ましくない」。

赤色-レベル4+、「神は禁じられている」。



私たちの写真は再び私たちを怖がらせました:



画像



すべてのマークアップをもう一度調べて、バックグラウンドを無効にして、不要であることが判明しました。 アプリケーションの主要な裏付けを忘れていませんでした-1つの簡単な行で:



<item name="android:windowBackground">@null</item>
      
      





アプリケーションのトピックでは、彼らはそれをオフにしました。 そして、これが最後に起こったことです。 それはどこでも完璧ではないかもしれませんが、それはまったく受け入れられます。 (ほぼすべてのビューが3レベルに低下しました!)







ステップ4.キャッシュは成功の鍵です



また、必須ではないように思えたかなり明白なことは、キャッシュです。 特に(この場合)フォントのキャッシュ。

既にお気づきかもしれませんが、ログを含むすべての場所で、独自のフォントが使用されます。そして、(再び)恐ろしいことに、毎回、新しいテキストごとにAssetsからリロードされます。 毎回、カール! (黒が主な汗、茶色がAsssnagerの作品です)。







キャッシングに遭遇したことは誰もいませんでしたが、StackOverflowのある優秀な人が助けてくれて、簡単なコードを提案しました。



 private static final Hashtable<String, Typeface> cache = new Hashtable<String, Typeface>(); public static Typeface get(Context c, String name) { synchronized (cache) { if (!cache.containsKey(name)) { String path = "fonts/" + name; try { Typeface t = Typeface.createFromAsset(c.getAssets(), path); cache.put(name, t); } catch (Exception e) { e.printStackTrace(); } } return cache.get(name); } }
      
      





それを適用して、私はラグが何であるかをほとんど忘れていました。



ステップ5.「これはディル用です!」



冗談のように、農民は自分が測定できる限りの土地を受け取ると言われ、ジャンプ、走り、歩き、cい、最後に疲れ果て、帽子を脱ぎ、言葉でできる限り投げた。ディル」というように、パフォーマンスを最小の詳細に最適化しようとしました。



したがって、コードを最適化するときが来ました。 もちろん、コードの作成方法は、より良い習慣の問題であり、ここでは、いくつかの一般的な推奨事項のみを提供できます(ソフトウェアの最適化に関する記事で詳細を確認できます)。 チームが直面したことは次のとおりです。



-作成する「不要な」サイクルを減らします。 それらなしで何かができるなら、それらなしでそれをしてください。

-ループ内で変数を作成しないでください。 外部で作成し、ループ内で更新する方が良いでしょう。

-リソースに繰り返しアクセスする必要がある場合(gerResources()を使用するか、単にmyList.get(0)などの別のクラス/配列で)-新しい変数に結果を書き込んで使用する:int myItem = myList.get (0); -これはまた、アプリケーションにミリ秒の空き時間を与えます。

-可能であれば、静的メソッドを使用します(クラス自体にアクセスする必要がない場合)。

-まあ、古典的な方法:プログラミングの最終段階で、カプセル化(ゲッターとセッター)を削除し、変数に直接アクセスします。



まとめると



実行された手順は最終的なものではなく、常に「ディルの場所」が存在することは確かです。 あなたがコメントでそれらを共有したい場合、私は常にステップ番号6、7 ...そしてさらに番号157について学ぶ準備ができています。 さらに、私は「科学」に感謝します。



それまでの間、アプリケーションが「飛行」を開始するために必要なことを要約します。



1.アプリケーションのパフォーマンスを測定し、問題の原因を見つけます。 たぶんそれはあなたのためのフォントではないでしょうが、例えば、メモリリーク(Memory Monitorをオンにする);

2.ビューの構造と階層を最適化します(HierarhyViewerと常識を有効にします)。

3.バックグラウンドアプリケーションを含む不要なバックをすべて削除します(この場合、正しい表示のためには共通のバッキングが必要であることに注意してください)。

4.頻繁に使用されるリソース/ダウンロード/画像/フォントを調べてキャッシュします。

5.プログラムコードの最適化を行います。



その結果、これらすべての手順の後、アプリケーションのトレース画像は次のようになり始めました。







遅延はほとんどなくなりました(または見えなくなりました)。



そしてもちろん、すべての最適化の後、私の頭に浮かんだ最初の考えは次のとおりです。 メイン画面にさらにビューとレイアウトを追加できるようになりました!“



フェイスパーム、カーテン...



All Articles