7

SDカードに保存されたファイルとAPKにアセットとして保存されたファイルの混合物をListViewに取り込もうとしています。を使用すると、SD カードにファイル名フィルタを使用しているにもかかわらず、のパフォーマンスがに比べて悪いTraceViewことがわかります。AssetManager.list()File.listFiles()

SD カードのフォルダーからすべての png ファイルを返す簡単な方法を次に示します。

// The folder on SDcard may contain files other than png, so filter them out
private File[] getMatchingFiles(File path) {
File[] flFiles = path.listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
    name = name.toLowerCase();
    return name.endsWith(".png");
    }
});  
return flFiles;
}

ここでそのメソッドを呼び出します。16 個のファイルを取得するのに約 12 ミリ秒かかります。

final String state = Environment.getExternalStorageState();           
if (Environment.MEDIA_MOUNTED.equals(state)||Environment.MEDIA_SHARED.equals(state)) {
    File path = Environment.getExternalStoragePublicDirectory(getResources().getString(R.string.path_dir));
if (path.exists()){
    File[] files = getMatchingFiles(path); 
        ... 

一方、am.list メソッドでは、約 6 つのファイルの名前だけを取得するのに 49 ミリ秒かかります。

// Get all filenames from specific Asset Folder and store them in String array
AssetManager am = getAssets();
String path = getResources().getString(R.string.path_dir);
String[] fileNames = am.list(path);  
...

パフォーマンスが非常に悪い理由を誰か説明できますか? パフォーマンスは APK に保存されているアセットの数に比例しますか? アセットが圧縮されていることは承知していますが、アセットの名前のみを取得しているため、どこかのテーブルに保存されると思います。

4

4 に答える 4

4

「どこかのテーブルに保存されている」という Coverdriven のコメントは、私がしばらく先延ばしにしてきた自分の問題を解決するきっかけになりました。

これはOPには答えませんが、別のアプローチを提供し、再帰しない限りCommonsWareのソリューションが処理しないサブフォルダーを処理します(もちろん、これは別の可能な解決策です)。これは、サブフォルダーに多数のアセットがあるアプリを特に対象としています。

このコマンドを実行するために ANT プレビルド ターゲットを追加しました (私は Windows を使用しています)。

dir assets /b /s /A-d > res\raw\assetfiles

これにより、資産フォルダー内のディレクトリ エントリ (/Ad) を除く、すべてのファイルの再帰 (/s) ベアボーン (/b) リストが作成されます。

次に、このクラスを作成して、アセットファイルの内容をハッシュマップに静的にロードします。ハッシュマップのキーはファイル名で、値はフル パスです。

public class AssetFiles {

// create a hashmap of all files referenced in res/raw/assetfiles

/*map of all the contents of assets located in the subfolder with the name specified in FILES_ROOT
the key is the filename without path, the value is the full path relative to FILES_ROOT
includes the root, e.g. harmonics_data/subfolder/file.extension - this can be passed
directly to AssetManager.open()*/
public static HashMap<String, String> assetFiles = new HashMap<String, String>();
public static final String FILES_ROOT = "harmonics_data";

static {

    String line;
    String filename;
    String path;

    try {

        BufferedReader reader = new BufferedReader(new InputStreamReader(TidesPlannerApplication.getContext().getResources().openRawResource(R.raw.assetfiles)));

        while ((line = reader.readLine()) != null) {
            // NB backlash (note the escape) is specific to Windows
            filename = line.substring(line.lastIndexOf("\\")+1);
            path = line.substring(line.lastIndexOf(FILES_ROOT)).replaceAll("\\\\","/");;
            assetFiles.put(filename, path);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

}

public static boolean exists(String filename){
    return assetFiles.containsKey(filename);
}

public static String getFilename(String filename){
    if (exists(filename)){
        return assetFiles.get(filename);
    } else {
        return "";
    }

}

}

これを使用するには、AssetFiles.getFilename(filename) を呼び出すだけで、AssetManager.open() に渡すことができるフル パスが返されます。はるかに高速です!

注意。このクラスはまだ完成しておらず、まだ強化されていないため、適切な例外キャッチとアクションを追加する必要があります。また、すべてのアセットがサブフォルダーにあり、サブフォルダーがアセット フォルダーのサブフォルダー (FILES_ROOT を参照) に配置されているという点で、私のアプリにも非常に固有ですが、状況に簡単に適応できます。

また、Windows はアセットファイルのリストをスラッシュで生成するため、バックスラッシュを置き換える必要があることにも注意してください。OSX および *nix プラットフォームではこれを排除できます。

于 2012-09-28T11:42:26.483 に答える
2

パフォーマンスが非常に悪い理由を誰かが説明できますか?

明らかに、ZIPアーカイブ(アセットが配置されているAPK)の内容を読み取ることは、ファイルシステム上のディレクトリの内容を読み取るよりも遅くなります。要約すると、これは特に驚くべきことではありません。これは、すべての主要なオペレーティングシステムに当てはまると思われるからです。

そのlist()データを一度読み込んでから、別の場所に保存してアクセスを高速化します(たとえば、データベース)。特に、将来のルックアップ用に最適化された形式(たとえば、単純なデータベースクエリで必要なものが得られる場合とロードする必要がある場合)で保存します。そして「再帰的に検索」します)。

于 2012-09-28T11:15:08.153 に答える
1

アセットにディレクトリの深いツリーがある場合は、最初にアイテムがファイルかディレクトリかを検出し、それに対して .list() を呼び出すことができます (ツリー内の移動が非常に高速になります)。これは私が発見した私の解決策です:

try {
    AssetFileDescriptor desc = getAssets().openFd(path);  // Always throws exception: for directories and for files
    desc.close();  // Never executes
} catch (Exception e) {
    exception_message = e.toString();
}

if (exception_message.endsWith(path)) {  // Exception for directory and for file has different message
    // Directory
} else {
    // File
}
于 2015-03-03T21:00:08.750 に答える