1. ホーム
  2. android

Androidでキャッシュディレクトリをクリアするタイミングは?

2023-07-17 01:07:12

質問

私は、インターネットから写真を表示するアプリケーションを持っています (デザイナーの仕事のためのショーケース)。私は内部キャッシュディレクトリに私のコンテンツをキャッシュし始めましたが、アプリのコンテンツは、キャッシュサイズで約150MBを取ることができます。また、android のドキュメントによると :

キャッシュファイルは常に自分で管理し、消費される容量の妥当な制限内にとどめるべきです。 1MB などの合理的な容量制限を守ってください。ユーザーがアプリケーションをアンインストールすると、これらのファイルは削除されます。 アプリケーションをアンインストールすると、これらのファイルは削除されます。

そこで、Currents アプリ (Galaxy Nexus) を見てみましたが、アプリケーションのキャッシュ サイズは 110 MB です。しかし、奇妙なことに、Google Currents & Google Maps のようなアプリケーションは、(USB Storage Data) と呼ばれるものにコンテンツをキャッシュしているのです。

では、先ほどのアプリケーションが使っている「USBストレージデータ」とは何なのでしょうか。また、アプリケーションにキャッシュを実装した場合、すべての キャッシュにあるアプリケーションのファイル をループして、何かを挿入するたびにサイズを取得し、比較・クリアするのでしょうか?それとも、Android がアプリケーションのキャッシュ ディレクトリを削除する時期が来たと判断するまで、コンテンツをキャッシュし続けますか?

Androidでキャッシュを管理する流れはどうなっているのか、少なくとも他のアプリケーションが大きなコンテンツをキャッシュするために何をしているのか、とても興味があります。

どのように解決するのですか?

ご質問の前に、2種類のストレージについて簡単に説明します。

キャッシュ

これは、ファイルシステム上のアプリケーション固有のディレクトリです。このディレクトリの目的は、アプリケーションがセッションの間に保持する必要があるかもしれない一時的なデータを保存することですが、それらを永遠に保持することは不可欠ではないかもしれません。通常、このディレクトリにアクセスするには Context.getCacheDir() . これは、アプリの設定に "Cache" として表示されます。

ファイル

キャッシュ ディレクトリと同様に、アプリには、ファイルを保持するためのアプリ固有のディレクトリがあります。このディレクトリのファイルは、アプリが明示的に削除するか、アプリがアンインストールされるまで存在します。通常、このディレクトリにアクセスするには Context.getFilesDir() . これはアプリの情報画面でさまざまなものとして表示されますが、スクリーンショットではこれは "USB Storage Data" と表示されています。

NOTEです。 明示的に外部メディア(通常はSDカード)に置きたい場合は Context.getExternalFilesDir(String type) .

違いについて

どちらのディレクトリも、あなたのアプリケーションにのみ固有のものです (他のアプリケーションはアクセスできません)。キャッシュとファイル ディレクトリの違いの 1 つは、システムがストレージ不足に陥った場合、最初にリソースを解放するのはキャッシュ ディレクトリからであるということです。システムは、files ディレクトリからデータをクリアすることはありません。もう一つの違いは、キャッシュディレクトリは通常、アプリ情報画面から手動でクリアすることができることです。ファイル ディレクトリも通常クリアできますが、ファイル ディレクトリをクリアすると、キャッシュ ディレクトリもクリアされます。

どちらを使用すればよいですか?

それは、そのデータがアプリの寿命と比較してどれほど重要であるかによります。1 回のセッションでデータが必要なだけで、そのデータを再び使用する必要があるかどうか疑わしい場合は、どちらも使用しないでください。不要になるまでメモリに保存しておけばいいのです。複数のセッションでデータを再利用する必要があると思われるが、そのようなことはしない場合 を持つ がない場合、キャッシュディレクトリを使用します。何があってもこのデータが必要な場合、または永続的な保存が必要なかなり大きなデータの場合は、files ディレクトリを使用します。以下は、私が思いつく例です。

  • キャッシュ - 最近開いた電子メール
    • 一度開いたデータをキャッシュしておけば、ユーザーがそのメールを再び読みたくなったときに、同じデータを取得するために再びネットワークを使用する代わりに、瞬時に読み込むことができます。最終的にユーザーはそのメールを読み終えるので、これを永遠に保持する必要はありません。
  • ファイル - 電子メールからダウンロードされた添付ファイル
    • これは、「必要なときにいつでも呼び出せるように、このデータを保管しておきたい」というユーザーの行動です。したがって、ユーザーが削除を望むまでこのファイルを削除することはないため、files ディレクトリに置きます。

キャッシュ ディレクトリはいつクリアすればよいですか。

キャッシュディレクトリにある Context.getCacheDir() javadocs を参照してください。

注意: システムがこれらのファイルを削除してくれることに依存してはいけません。 キャッシュ ファイルで消費する容量には、常に 1 MB などの妥当な最大値を設定する必要があります。 キャッシュ ファイルで消費する容量に 1 MB などの妥当な最大値を設定し、その容量を超えたらファイルを削除する必要があります。 を削除する必要があります。

これは 1 MB の例を使用していますが、それがあなたのアプリケーションにとって妥当かどうかはわかりません。いずれにせよ、ハードな最大値を設定する必要があります。この理由は、単純に、責任あるアプリを設計することに帰着します。では、いつチェックすればよいのでしょうか。私は、キャッシュディレクトリに何かを置きたいと思うたびにチェックすることをお勧めします。以下は、非常にシンプルなキャッシュマネージャです。

public class CacheManager {

    private static final long MAX_SIZE = 5242880L; // 5MB

    private CacheManager() {

    }

    public static void cacheData(Context context, byte[] data, String name) throws IOException {

        File cacheDir = context.getCacheDir();
        long size = getDirSize(cacheDir);
        long newSize = data.length + size;

        if (newSize > MAX_SIZE) {
            cleanDir(cacheDir, newSize - MAX_SIZE);
        }

        File file = new File(cacheDir, name);
        FileOutputStream os = new FileOutputStream(file);
        try {
            os.write(data);
        }
        finally {
            os.flush();
            os.close();
        }
    }

    public static byte[] retrieveData(Context context, String name) throws IOException {

        File cacheDir = context.getCacheDir();
        File file = new File(cacheDir, name);

        if (!file.exists()) {
            // Data doesn't exist
            return null;
        }

        byte[] data = new byte[(int) file.length()];
        FileInputStream is = new FileInputStream(file);
        try {
            is.read(data);
        }
        finally {
            is.close();
        }

        return data;
    }

    private static void cleanDir(File dir, long bytes) {

        long bytesDeleted = 0;
        File[] files = dir.listFiles();

        for (File file : files) {
            bytesDeleted += file.length();
            file.delete();

            if (bytesDeleted >= bytes) {
                break;
            }
        }
    }

    private static long getDirSize(File dir) {

        long size = 0;
        File[] files = dir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                size += file.length();
            }
        }

        return size;
    }
}

もちろん、これは高価な操作になり得るので、バックグラウンドスレッドでのキャッシュを計画する必要があります。

また、これはあなたが必要とするほど複雑になる可能性があります。私の例では、すべてのキャッシュ ファイルがキャッシュ ディレクトリのルートに配置されると仮定しているので、潜在的なサブディレクトリをチェックしません。ファイルを削除するルーチンは、最も古いアクセス日付でファイルを削除するなど、より洗練されたものにすることも可能です。

データをキャッシュすることを決定するときに心に留めておくべきことの 1 つは、キャッシュされたデータがもはや存在しない場合を常に計画する必要があるということです。キャッシュにデータが保存されていない場合、外部手段でデータを取得するための手順を常に用意してください。同様に、外部からデータを取得する前に、必ずキャッシュを確認すること。キャッシュの目的は、ネットワークアクティビティを削減し、長い処理を削減し、アプリで応答性の高いUIを提供することです。ですから、責任を持って使用してください:)