最新のAndroidアプリケーションを作成するためのヒント。 ヤンデックス講義

2017年のモバイル開発学校の資料は引き続き公開されます。 次に、Yandex.MailチームのAndroid開発者Dmitry Nikitinによる大規模な講義です。 Dmitryは、多くのライブラリで迷子にならないようにゼロからプロジェクトの作成にアプローチする方法と、1つまたは別のソリューションを選択するときに何を探すべきかを指示します。


-みなさんは、少なくとも2〜3か月はAndroidをプログラミングしています。 おそらく、誰かが数年前からプログラミングをしていて、developer.android.comを最初から最後まで読んでいるのでしょう。 またはそうでないかもしれません。 しかし、少なくとも1つの方法で多くのことを実行する方法をご存知でしょう。 しかし、これらの方法の多くがあり、各チームが独自の方法を持つことができることは秘密ではありません。多くの場合、この方法またはその方法は歴史的な理由だけで選択されます。

今日は、どのような選択肢と開発ツールがあり、ライブラリを選択する際に何を強調すべきかについて簡単に説明したいと思います。

今日はどのツールについてお話しますか? まず第一に、アプリケーションの即時機能を実装するのに役立つものについて:これは、プロジェクトマネージャーが通常私たちに通常要求するものです。

第二に、それはコードの品質を制御し、より柔軟で拡張可能にするのに役立ちます。 そして、開発にも関連しているものの、前の2つのポイントに直接関連していないものを検討します。



このツールは、速度と消費されたRAMとディスクメモリの点でどれほど効果的ですか。

どちらが良いか、どれが悪いかをどのように選択しますか? まず、使いやすさ、このツールを使用して特定の問題を解決するのがどれほど簡単かを見ていきます。

原則として、よく知られているライブラリはすでにテストでカバーされており、統合されるとバグがより頻繁に発生するため、クライアントコードが簡潔で単純であればあるほど、ミスをする可能性は低くなります。

一方、最も直接的なビジネスロジックではない場合があるため、特定の何かが必要になる場合があります。ここで、選択したツールが松葉杖なしでこれを実行できるほど柔軟性がある場合、これは大きなプラスになります。

以下の基準は外部ライブラリのみに適用されます-これはライセンスです。 多くの場合、商用コードを記述するため、ライセンスはソフトウェアの使用を許可するだけでなく、ソースの開示を強制するものでもありません。

最も人気のあるライセンスはApache 2.0とMITです。 原則として、それらは概して、私たちに制限を課しません。 他のライセンスは注意して使用する必要があります。

さらに、接続するために必要なものはすべて、Android SDKと共にではなく、JCenterまたはMavenセントラルのどこかに置いてある場合は、既にコンパイルおよび配布されていることが望ましいです。

不明な点がある場合は、ドキュメントを参照するか、この製品をサポートしている人に質問することができます。

そして、最後の項目は順番に並んでいますが、重要度には関係ありません-これは、このツールが多くのメソッドをどの程度もたらすかです。



なぜこれが重要なのですか? アプリケーションがLollypop未満のAPIをサポートしている場合(これがアプリケーションの大部分です)、これらのデバイスではコードがDalvik仮想マシンで実行され、APKのビルド時にJavaファイルがバイトコードにコンパイルされてからDalvik実行可能ファイルにパックされます。 このようなファイルにはそれぞれ65,000を超えるメソッドを含めることはできません。この制限に達すると、アセンブリ中にこのようなエラーが発生します。




同時にいる方法は? 良い方法では、どのクラスが占有されているかを追跡し、不必要なことを避けるようにする必要があります。 つまり、これがもたらすメソッドの数に関して、この依存性またはその依存性がどのような肯定的な貢献をもたらすかを常に評価する必要があります。

たとえば、カーソルを安全に閉じるためだけにGuavaを使用する場合、ユーティリティメソッドを記述し、そのような大きな依存関係を放棄する必要があります。

テストでほとんどの部分に何かが使用されている場合、プロジェクトに直接含まれないように、この依存関係をテストコンパイルに追加できます。

また、ラムダ式や参照メソッドなど、さまざまな構文糖のコストが高いことにも注意する必要があります。 このトピックの詳細については、Jake Wharton クラスレポート、Exploring Hidden Java Costsをご覧ください。

ラムダに加えて、他の生成されたコードを見ることが重要です。 たとえば、1つのバージョンから始まるバターナイフでは、メソッドの生成が40%少なくなりました。



プロジェクトが占めるクラスの数を追跡するにはどうすればよいですか? これには、Dexcountプラグインが適しています。 誰がいくつのメソッドを持ち込んだのかがわかるように、このような美しいチャートが生成されます。

中毒を追加する前に、 methodscount.comサイトの費用がどれくらいかかるかを評価できます。 また、推移的な依存関係がもたらすメソッドの数も確認できます。

他のすべてが失敗した場合でも、いくつかのdexファイルを使用できますが、追加の問題が発生します。 このようなアプリケーションは通常、起動時間が長くなり、より多くのメモリを消費するという事実に加えて、場合によってはクラッシュすることもあります。 MultiDexを使用して構築する場合、アプリケーションを正常に起動してプライマリdexファイルに配置するために必要なクラスの分析が実行されます。

たとえば、ネイティブコードから呼び出しが行われるなど、依存関係が複雑な場合、そのようなクラスは他のdexファイルに分類され、起動時にNoClassDefFoundErrorが発生します。 これを回避するために、別の構成でそのようなクラスを指定する必要があります。

さらに、この分析によりビルドの速度が大幅に低下します。Lollypopバージョンから、DalvikはAndroidランタイムに置き換えられました。Androidランタイムは既にMultiDexをサポートしており、SDK 21のビルドははるかに高速です。



したがって、ハックとして、SDKの実際の最小バージョンがはるかに小さいという事実にもかかわらず、ローカルアセンブリに対して21のバージョンを指定するという事実を利用できます。



スタジオのコマンドラインパラメータからこの値を取得します。これにより、ローカルでより速く収集する機会が得られます。 同時に、スタジオ内のAPIレベルのテープチェックは壊れません。

まとめると。 メソッドの数で何をする必要がありますか? 健康な人の方法は、それらを自然に数え、減らすことです。 また、proguardを使用して、接続したが使用していないものを削除することもできます。 リリースコレクションには、すでにproguardが含まれているため、デバッグでのみMultiDexを有効にする必要がある場合は、通常の方法です。 しかし、他のすべてが失敗した場合、MultiDexをオンにするだけで、迅速なローカルアセンブリであなたの人生をわずかに明るくします。

アプリケーションで通常行う必要があるタスクは何ですか? 多くの場合、サーバーからデータを受信し、特定の方法で処理し、画面に表示します。たとえば、オフラインで作業する必要がある場合は、特定の方法で保存する必要があります。 たとえば、データベース、共有設定またはファイル。

GitHubからのユーザーデータを表示するサンプルアプリケーションを使用して、これらすべてのステップを見てみましょう。

ユーザーデータ自体はサーバー上にあり、そこから取得する必要があります。 どうすればこれができますか? たとえば、HttpUrlConnectionがあります。



StackOverflowに移動し、get-requestの例を取り上げて、URLを置き換えます。 接続を開きます。 InputStreamを取得します。 ここで必要なデータを文字列として読み取ります。 接続を閉じることを忘れないでください。 このリクエストでは、すべてが簡単です。 たとえば、ファイルをReadMeでサーバーにアップロードするなど、ささいなことをする必要がある場合は、問題が始まります。 これを行うには、独自のラッパーを作成する必要があります。



そして、すでにこのサイズですが、タスクはそれほど珍しくないように思えます。

また、このためのテストを作成する必要がありますが、実際には、他の誰かのコードに対しては行いたくありません。

HttpUrlConnectionの上で動作する多くのライブラリがあります。この場合、送信ファイルは単純化され、非同期呼び出しなどの他の機能が追加されます。

たとえば、Kevin SavinskiのHttpClientライブラリがあります。 しかし、箱から出してすぐにこれらすべての欠点がない代替があります-これはOkHttpです。



OkHttpを使用した同じファイルアップロードは次のようになります。 URLを提供するだけです。 RequestBodyを作成します。 そして、私たちは要求を満たします。

Kitkat以降、ネットワークレベルのHttpUrlConnection自体はOkHttpを使用します。 したがって、このオプションを選択した場合、Androidの異なるバージョンではライブラリの動作が異なるという事実から保護されます。

そして、リクエストの一部にuseragentを追加したり、リダイレクトの場合の動作を変更したりする場合を想像してください。 また、OkHttpインターセプターを使用してこれをかなり迅速に行うこともできます。



インターセプトメソッドを再定義します。 元のリクエストを取得します。 何らかの方法でそれを変更し、チェーンのさらに下に渡すことができます。 そして、このインターセプターをクライアントに追加します。 同時に、リダイレクトの場合にリクエストを繰り返しインターセプトする場合、addInterceptorではなくaddNetworkInterceptorを呼び出すことができます。

さらに、OkHttpは要求を非同期的に実行し、Webソケットを操作し、他の多くの優れた機能を実行できます。



ただし、欠点が1つあります。 それにもかかわらず、それはサードパーティのライブラリであり、2,500のメソッドを描画します。 これから私たちを救う組み込みソリューションはありますか?

Androidの夜明けにはApacheHttpClientがあり、以前のバージョンではより安定していましたが、GoogleチームはAPIが今後の開発には大きすぎると判断し、廃止されることを宣言しました。



useLibrary“ org.apache.http.legacy”を明示的に指定して、プロジェクトで使用することはできますが、APKに既にパッケージ化されているため、目標を達成できません。



検討されているオプションに加えて、GoogleがサポートするVoleyライブラリもあります。 以前の選択肢よりも高いレベルです。 OkHttpがリクエストを非同期に実行できるように。 彼女は優れた内部キャッシュを備えており、画像のアップロード方法も知っています。これだけのために、imageviewはネットワークimageviewを継承する必要がありますが、これは非常に強い制限です。

残念ながら、VoleyはJCenterおよびMaven Centralにそれを配置せず、ソースコードと共にプロジェクトにインポートされます。運が悪ければ、このソースコードも同じコミットで変更されますが、これは非常に悪いことです。

Voleyは、同じOkHttpとは異なり、優れたドキュメントを自慢できません。



それで、私たちは何を取るべきですか? メソッドの数に厳密な制限がある場合、またはネットワークでの作業がそれほど複雑ではないため、いくつかの重要なことが必要な場合は、HttpUrlConnectionまたはそのラッパーを使用できます。 それ以外の場合、OkHttpが最適なオプションのようです。 (質問と回答については、ビデオを参照してください-約エド。)

データをダウンロードすることになりました。 これはJSON文字列です。 そして今、Javaの観点からどのように便利なビューを取得できますか?



十分なデータがない場合は、組み込みのJSONObjectを使用して必要な値を取得できます。 興味のある分野に出会うまで、構造全体を順番に回ります。 値を読み取り、次に進みます。

大量のデータがある場合、このルーチン作業を維持するのが難しくなり、特定のライブラリを使用するのが理にかなっています。



たとえば、元々Android用に作成されたSquareのMoshiがあります。これにより、メモリ消費がより経済的になります。 しかし、彼はかなり遅いです。

GoogleのGsonもありますが、高速ですが、RAMをより多く消費しますが、メソッドの数の点では多少魅力的です。

そして、ジャクソンがいます。これは、リフレクションのカウンターパートの中で最速です。 彼は、ストリーミングパートと直接マッピング用のDatabingを別々に持っています。 しかし、同時に彼は太っている。

さらに、コンパイル時に解析するためのアダプターを生成するLoganSquare( GitHubへのリンク )があります。 JacksonストリーミングAPIを使用し、コンパイル時間の生成により、競合他社の速度を大幅に上回ります。



比較は、作成者のベンチマークで確認できます。 提示されたオプションのLoganSquareが最速であることがわかります。



ただし、コンパイル時の生成では、構造に特定の制限が課されます。 エンティティ自体は何も継承できません。 ジェネリックにも問題があります。

Instagramからのリフレクションのない別のパーサー、IgJsonParcerがあります。 彼はすべてのフィールドに直接注釈を付けることを義務付けており、それを処理する必要があります。 このライブラリにはまだ安定したバージョンがありません。また、GuavaとApache Commonsの両方に依存しているため、メソッドの数に問題があります。

したがって、提示されたソリューションの中で、最速はLoganSquareであり、速度が非常に重要な場合は、それを使用できます。 それ以外の場合は、速度、メモリ消費、またはメソッドの数がさらに必要であるという事実に基づいて、以前のオプションから選択します。



救済策を選択しました。 テストすることを忘れないでください。解析はテスト駆動開発に非常に適しています。 ドキュメントからJSONをコピーし、テストを作成してから、テストが緑色になるまで解析自体を作成します。

多くの場合、追加のフィールドをAPIに追加できます。

解析が失敗するのを防ぐために、事前にテストで不明なフィールドを追加できます。 したがって、私たちはそのような変更の準備ができていることが保証されています。



反対に、データをサーバーに送信するか、文字列として保存する場合は、逆のプロセス-シリアル化が必要です。テストはその正確性を検証するのにも役立ちます。

たとえば、ここではJsonUnitライブラリーが使用されます。 このような比較は順序やインデントなどに対して不安定であるため、裸線を比較しないように使用されました。そして、取得した値を直接比較します。

そのため、Retrofitライブラリ( GitHubへのリンク )を使用して、オンラインにする方法、解析する方法、これを組み合わせる方法を選択しました。



したがって、ベースURLを設定する単一のアクセスポイントがあります。 その場合、たとえばQA環境の場合、ここですばやく変更できます。 Moshi、Gson、Jackson、またはProtobufからコンバーターを選択できます。 そして、私たちが電話をかける方法。 これは同期的に実行できます。



または非同期で。



または、Rxを使用します。 これを行うには、APIメソッドが単一の完了可能または監視可能を返すことを示す必要があります。 そして、適切なCallFactoryを選択します。



ユーザーデータの逆シリアル化に取り組みました。 Javaでそれらをどのように紹介しますか?



このようにすることができます。 しかし、何が悪いのでしょうか? 将来、ログインフィールドがコール中に直接計算されるか、これらすべてのコールを記録するとします。

したがって、可視性をプライベートに変更し、ゲッターとセッターを追加します。





HashMapに追加する場合は、equalsとhashCodeが必要です。





ここに文字列に美しいを追加します。 また、意図して別のアクティビティに転送したり、画面が反転したときにバンドルで保存したりする場合は、このオブジェクトをParcelableにする必要があります。

そして、新しいフィールドがクラスに追加されていることを知ると、これらの円を繰り返し、何も忘れないようにする必要があります。 幸いなことに、この問題を解決できるライブラリがあります。



たとえば、AutoValue( GitHubへのリンク )。 AutoValueアノテーションと抽象フィールドアクセスメソッドを作成しています。 フィールド自体は、アセンブリ中にライブラリによって生成されます。 また、前にリストしたこれらすべてのメソッドを生成します。

AutoValueのキラー機能は、拡張機能を使用するのに十分簡単であることです。 たとえば、コンパイル時アダプターを生成できるGsonおよびJackson拡張機能があり、これにより、同じLoganSquareの速度をキャッチできます。 したがって、1つの石で2羽の鳥を殺します。解析とモデルの内部表現の両方の問題を解決します。

AutoValue Parcel拡張機能もあります。 接続すると、Parcelableの実装を記述するだけで、解析をサポートするメソッドが生成されます。 さらに、クラスのフィールドの1つがシリアル化可能でない場合、コンパイル中にエラーが発生します。



別のAutoValue拡張機能もあります-Auto-Parcelも解析しますが、AutoValue Parcelとは異なり、オブジェクトがランタイムで排他的にシリアル化されることをチェックし、あまり人気のないEPL 1.0ライセンスも持っています。

AutoValueに代わるものがあります-これは不変です。 MoshiおよびGson用のアダプターもありますが、Parcelable用の拡張機能はありません。

そして、他とは異なり、相続人を生成せず、元のクラスを置き換えるLombokがあります。

このすべての魔法をサポートするには、IDEプラグインと忍耐が必要です。これはすべて非常にバグが多いためです。

したがって、考慮されるオプションの中で、AutoValueとそのAuto-Parcel拡張機能が最も有益に見えます。 (質問と回答については、ビデオを参照してください-約エド。)

ユーザーの年齢に応じてさまざまな種類のバナーを表示する必要があるとします。 すべてがシンプルなようです。 現在の日付を取得し、生年月日を計算して年齢を取得します。



しかし、Javaの日付とカレンダーを標準として使用する場合、驚きを期待する必要があります。 たとえば、getYearを呼び出すと何が得られますか? 正解は117年です。1年ではないからです。

それはちょうど1年ではないからです。



また、Java.docからわかるように、これは1900年から1年後です。Java.docを事前に読んでいないことを非難することも一部あります。

または、たとえば、GetInstanceカレンダーの最も有名なメソッドが実際に毎回新しいカレンダーを作成することは、最も明白なポイントではありません。



最も人気のあるJavaライブラリの1つはJodaTime( GitHubへのリンク )です。 これを使用すると、ユーザーの問題を簡単に解決できます。 たとえば、新年までの日数を計算することもできます。

さらに、JodaTimeにはTzdataファイルの最新バージョンが含まれています。 このファイルには、さまざまな地域のタイムゾーンが時間とともにどのように変化したか、およびUTCに基づいて現地時間を取得する方法の履歴が含まれています。

このファイルは、おそらく山全体のソリューションをカプセル化します。 トム・スコットのビデオからそれらについてさらに学ぶことができます。 Tzdataファイルもシステム自体にありますが、APKに埋め込む場合、ベンダーが古いデバイスの更新をリリースしない場合、問題から身を守ります。 代わりに、たとえば、ベンダーは、HTCがよく行うように、新しい顧客向けに美しい3次元タイムゾーン選択モデルを作成できます。



しかし、APKにTzdataファイルを含めることは無駄ではありません。サイズが増加しているという事実に加えて、最大5 MBのRAMを大量に消費し始めています。ダンルー 4年前にこれに気付きましたライブラリはJava専用であり、このファイルにアクセスするときにClassLoader.getResourceAsStreamを使用すると、このような大量のデータをキャッシュするだけでした。

彼はこの呼び出しをAssetManagerの使用に置き換え、Joda Time Androidと呼ばれるライブラリをリリースしました。



JDK , Java - , , Joda Time JSR 310. API Android , back-port ThreeTenBP, , ThreeTenABP, « » Tzdata.



まとめると。 ? APK, API. ThreeTenABP , Joda Time - Android , , .

, .



UI- , AsyncTask. ? , , - .



Picasso. , placeholders, , .

, , , , URL, . Picasso .



Glide, Picasso , , .



Fetcher, URL, .



, Glide . StreamModelLoader, .



Loader Module, Module Manifest, , . , , . proguard.



, Glide, .

, activity , Glide , , .



, Glide , , ImageView . Picasso, , .

Glide .

Universal ImageLoader, , , deprecated.

Fresco, ashmem, , .

gif. , Glide.



, ? , , , , Picasso. - gif, Glide. Picasso Glide , API. ( . — . .)

? , dependency injection, MVP, MVVM . .



dependency injection, reflection-based RoboGuice, , , , . . Java , , , , reflection , Android , , .

RoboGuice deprecated. - Dagger1, , reflection , , . Dagger2. , .

, , . , - scope, . Toothpick, , , , . reflection free, scope. , , .



Dagger, . ( GitHub .)



, RoboGuice .



, , . , , , API, TootPick . — , .

MVP MVVM , , , , , , , , . , . , - .



. , , , . , GreenDAO, ORMLite NoSQL Realm.

, — .

, , . , , - , .

, SQLDelight, Java- SQL-. , , , , Rx, , StorIO SQLBrite.

Room, , , , , .

, ? , , -, . , — .



— checkstyle ( SourceForge ). , , . Override , , , , . . .



checkstyle, . , , .



, , , , , , .



. Java-.



Lint, Android- , , , .



. , nullability, , , , , , .



. baseline. Lint . , , .



Lint. .



XML.



— PMD ( SourceForge ). Java. .



.



.



, PMD, Java-, -. FindBugs ( SourceForge ). , , .



, , . , hashCode, , . ?

FindBugs , hashCode int, - abs , IndexOutOfBounds.



, , FindBugs .



FindBugs. , XML, HTML-, , , .



- , Findbugs:annotations.

, FindBugs , SpotBugs, , , Android, .



, , , , , dashboard. dashboard .



.



, -.



まとめると。 ? — . , , , . ( GitHub .)



, , , ? JitPack, , - fork, .



— Kotlin. Google , , . NullPointerException ClassCastException. data-, , , AutoValue, , . — , Parcelable.

pattern matching, . , , Kotlin , , proguard .

, final everywhere, final, . Mockito — , . — — Mockito 2.

any. , matcher, null Mockito. Kotlin- null, . Mockito Kotlin. , Spek — JetBrains. , , Roboelectric. — Detekt SonarQube. これで私はすべてを持っています。 ご清聴ありがとうございました。

著者の連絡先:メール電報

Source: https://habr.com/ru/post/J344898/


All Articles