最適化されていないダウンロードを避ける

アプリの一部のユーザーは、インターネットに断続的にアクセスする場合や、デバイスにダウンロードできる情報の量に制限を設けています。アプリがダウンロードする必要があるデータの量を減らすことで、ユーザーがより頻繁にアプリを操作できるようになります。

ダウンロードを削減する最も基本的な方法は、必要なものだけをダウンロードすることです。データの観点では、これは REST API を実装することを意味します。REST API を実装することで、最終更新時刻などのパラメータを使用して、返されるデータを制限するクエリ条件を指定できます。

同様に、画像をダウンロードするときは、フルサイズの画像をダウンロードしてクライアント上でサイズを削減するのではなく、サーバー側で画像のサイズを小さくすることをおすすめします。

HTTP レスポンスをキャッシュに保存する

もう 1 つの重要な手法は、重複データのダウンロードを回避することです。キャッシュを使用すると、同じデータが繰り返しダウンロードされる可能性を減らすことができます。アプリのデータとリソースをキャッシュに保存することで、アプリが参照する必要がある情報のローカルコピーを作成できます。アプリが同じ情報に短期間に複数回アクセスする必要がある場合は、キャッシュへのダウンロードは 1 回だけで済みます。

ダウンロードするデータの合計量を減らすために、可能な限り積極的にキャッシュに保存することが重要です。合理的に可能な範囲で、静的リソース(フルサイズの画像などのオンデマンド ダウンロードを含む)は常にキャッシュに保存してください。オンデマンド キャッシュを定期的にフラッシュしてサイズを管理できるように、オンデマンド リソースは個別に保存する必要があります。

キャッシュ保存によってアプリに古いデータが表示されないようにするには、ETag ヘッダーや Last-Modified ヘッダーなど、適切な HTTP ステータス コードとヘッダーを使用します。これにより、関連コンテンツを更新する必要があるタイミングを判断できます。次に例を示します。

Kotlin

// url represents the website containing the content to place into the cache.
val conn: HttpsURLConnection = url.openConnection() as HttpsURLConnection
val currentTime: Long = System.currentTimeMillis()
val lastModified: Long = conn.getHeaderFieldDate("Last-Modified", currentTime)

// lastUpdateTime represents when the cache was last updated.
if (lastModified < lastUpdateTime) {
    // Skip update
} else {
    // Parse update
    lastUpdateTime = lastModified
}

Java

// url represents the website containing the content to place into the cache.
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
long currentTime = System.currentTimeMillis();
long lastModified = conn.getHeaderFieldDate("Last-Modified", currentTime);

// lastUpdateTime represents when the cache was last updated.
if (lastModified < lastUpdateTime) {
    // Skip update
} else {
    // Parse update
    lastUpdateTime = lastModified;
}

一部のネットワーキング ライブラリは、これらのステータス コードとヘッダーを自動的に尊重するように構成できます。たとえば、OkHttp を使用する場合、次のコードサンプルに示すように、クライアントのキャッシュ ディレクトリとキャッシュ サイズを構成すると、ライブラリで HTTP キャッシュを使用できます。

Kotlin

val cacheDir = Context.getCacheDir()
val cacheSize = 10L * 1024L * 1024L // 10 MiB
val client: OkHttpClient = OkHttpClient.Builder()
    .cache(Cache(cacheDir, cacheSize))
    .build()

Java

File cacheDir = Context.getCacheDir();
long cacheSize = 10L * 1024L * 1024L; // 10 MiB
OkHttpClient client = new OkHttpClient.Builder()
    .cache(new Cache(cacheDir, cacheSize))
    .build();

キャッシュを構成すると、完全にキャッシュされた HTTP リクエストをローカル ストレージから直接処理できるため、ネットワーク接続を開く必要がなくなります。条件付きでキャッシュに保存されたレスポンスでは、サーバーからのレスポンスの鮮度が検証されるため、ダウンロードに関連する帯域幅のコストを回避できます。キャッシュに保存されていないレスポンスは、今後のリクエストに備えてレスポンス キャッシュに保存されます。

Context.getExternalCacheDir() を使用すると、機密性の低いデータを非マネージド外部キャッシュ ディレクトリにキャッシュに保存できます。また、Context.getCacheDir() を使用して、マネージドで安全なアプリケーション キャッシュにデータをキャッシュに保存することもできます。システムの空き容量が少なくなると、この内部キャッシュはフラッシュされる可能性があります。

リポジトリを使用する

より高度なキャッシュ アプローチについては、リポジトリの設計パターンを検討してください。これには、特定のデータまたはリソースに対して API を抽象化する、リポジトリと呼ばれるカスタムクラスの作成が含まれます。リポジトリは、最初にリモート ウェブサービスなどのさまざまなソースからデータをフェッチしますが、後続の呼び出しで、データのキャッシュ バージョンを呼び出し元に提供します。この間接的なレイヤにより、アプリに固有の堅牢なキャッシュ戦略を提供できます。アプリ内でリポジトリ パターンを使用する方法について詳しくは、アプリ アーキテクチャ ガイドをご覧ください。