2012-09-28 34 views
7

मैं एसडीकार्ड पर संग्रहीत फ़ाइलों के मिश्रण के साथ एक सूची दृश्य को पॉप्युलेट करने और एपीके में संपत्ति के रूप में संग्रहीत करने की कोशिश कर रहा हूं। TraceView का उपयोग करके, मैं देख सकता हूं कि AssetManager.list() का प्रदर्शन File.listFiles() की तुलना में खराब है, भले ही मैं SDcard के लिए फ़ाइल नाम फ़िल्टर का उपयोग कर रहा हूं।AssetManger.list() इतनी धीमी क्यों है?

यहाँ एक सरल विधि है कि एसडी कार्ड पर एक फ़ोल्डर से सभी 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 फ़ाइलों को पुनः प्राप्त करने के लिए 12ms के बारे में लेता है:

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 फाइलों के नाम पुनर्प्राप्त करने के लिए 49ms लगते हैं!

// 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); 
... 

क्या कोई समझा सकता है कि प्रदर्शन इतना खराब क्यों होगा? क्या एपीके में संग्रहीत संपत्तियों की संख्या के अनुपात आनुपातिक है? मुझे पता है कि संपत्ति संपीड़ित होती है, लेकिन मैं केवल संपत्तियों के नाम ला रहा हूं, जिसे मैंने सोचा था कि कहीं किसी तालिका में संग्रहीत किया जाएगा।

+0

पर प्रतिबद्ध आप काफी पता है कि आपकी संपत्ति फ़ोल्डर में है अपेक्षा की जाती है – njzk2

+1

AssetManager बेकार है और प्रदर्शन गरीब है। मेरे पास subfolders में संपत्तियों में लगभग 7.5K फ़ाइलें हैं। वे संपत्ति में हैं क्योंकि वे बाहरी स्रोत और रखरखाव ओवरहेड (फाइलों का नामकरण) आदि से हैं और उन्हें संसाधनों में डालने का प्रदर्शन हिट स्वीकार्य नहीं है। इस संरचना में फ़ाइलों को खोजने के लिए, मुझे इसे फिर से खोजना होगा और प्रदर्शन, भयानक है, जैसा कि आप कहते हैं, मुख्य रूप से .list() के आसपास। ऐसा लगता है कि डिजाइनरों ने कभी भी उन ऐप्स के बारे में सोचा नहीं जो बड़ी मात्रा में स्थिर डेटा का उपयोग करते हैं। मैं इसे ब्याज के साथ देखूंगा। – Simon

उत्तर

1

क्या कोई यह समझा सकता है कि प्रदर्शन इतना खराब क्यों होगा?

ज़िप संग्रह (एपीके जहां संपत्ति स्थित है) की सामग्री पढ़ना स्पष्ट रूप से फाइल सिस्टम पर निर्देशिका की सामग्री को पढ़ने से धीमा है। संक्षेप में, यह विशेष रूप से आश्चर्यजनक नहीं है, क्योंकि मुझे संदेह है कि यह सभी प्रमुख ऑपरेटिंग सिस्टम के लिए सच होगा।

उस list() डेटा में एक बार पढ़ें, फिर इसे तेजी से एक्सेस (उदाहरण के लिए, डेटाबेस) के लिए कहीं और सहेजें, विशेष रूप से भविष्य में लुकअप के लिए अनुकूलित एक रूप में (उदाहरण के लिए, जहां एक साधारण डेटाबेस क्वेरी आपको जो चाहती है, बनाम लोड करना और "इसे फिर से खोजना" फिर से)।

+0

धन्यवाद, यह एक विचारशील उत्तर है, लेकिन अगर सूची को कैशिंग करना इतना उपयोगी है, तो सिस्टम पहले से ही ऐसा क्यों नहीं करता है? एपीके में पैक किए जाने पर पहले से ही संसाधनों की गणना की जाती है, संपत्तियों की एक सूची एक ही समय में क्यों संग्रहीत नहीं की जा सकती? अनुमोदित, मैं क्या कर सकता हूं CommonsWare सुझाव देता है और स्टार्टअप पर इसे एक बार पढ़ता है, लेकिन क्या यह एसेटमैनेजर के भीतर कैश किए जाने के लिए और अधिक समझ में नहीं आता है? – coverdriven

+2

@coverdriven: "सिस्टम पहले से ही ऐसा क्यों नहीं करता है?" - शायद क्योंकि आपके उपयोग के मामले में डेवलपर्स की संख्या बल्कि छोटे AFAIK है। Google में इंजीनियरिंग समय की असीमित मात्रा नहीं है, और एंड्रॉइड डिवाइसों में स्टोरेज स्पेस और रैम की असीमित मात्रा नहीं है। इसलिए, डेवलपर्स खुद को हल करने वाली हर समस्या को ओएस स्तर पर हल नहीं किया जाएगा। – CommonsWare

4

कवरड्रिवेन की टिप्पणी "कहीं किसी तालिका में संग्रहीत" ने मुझे अपनी समस्या को हल करने के लिए प्रेरित किया जो मैं थोड़ी देर के लिए बंद कर रहा हूं।

यह ओपी का जवाब नहीं देता है लेकिन यह एक अलग दृष्टिकोण प्रदान करता है और यह उपफोल्डर को संभालता है जो कॉमन्सवेयर का समाधान तब तक नहीं करता जब तक आप रिकर्सिव नहीं जाते (जो निश्चित रूप से एक और संभावित समाधान है)। यह विशेष रूप से उन ऐप्स पर लक्षित है जिनके पास सबफ़ोल्डर में बड़ी संख्या में संपत्तियां हैं।

मैं सभी फ़ाइलों की सूची इस आदेश (मैं Windows पर हूँ)

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

इस बनाता है एक पुनरावर्ती (/ s), barebones चलाने के लिए एक चींटी पूर्व निर्माण लक्ष्य जोड़ा (ख /), को छोड़कर मेरी संपत्ति फ़ोल्डर में निर्देशिका प्रविष्टियां (/ विज्ञापन)।

मैं तो इस वर्ग के स्थिर एक hashmap में assetfiles की सामग्री को लोड करने के लिए बनाया है, कुंजी जिनमें से, फ़ाइल नाम और मूल्य पूरा पथ

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 (फ़ाइल नाम) को कॉल करें जो पूरा पथ देता है जिसे मैं AssetManager.open() पर भेज सकता हूं। बहुत तेज़!

एनबी।मैंने इस कक्षा को समाप्त नहीं किया है और यह अभी तक कठोर नहीं है इसलिए आपको उपयुक्त अपवाद कैच और क्रियाएं जोड़ने की आवश्यकता होगी। यह मेरे ऐप के लिए भी काफी विशिष्ट है कि मेरी सभी संपत्ति उपफोल्डर्स में हैं जो बदले में संपत्ति फ़ोल्डर के उपफोल्डर में स्थित हैं (FILES_ROOT देखें) लेकिन आपकी स्थिति को अनुकूलित करने में आसान है।

बैकस्लाश को प्रतिस्थापित करने की आवश्यकता भी ध्यान दें, क्योंकि Windows आगे स्लैश के साथ एसेटफ़ाइल सूची उत्पन्न करता है। आप ओएसएक्स और * निक्स प्लेटफार्मों पर इसे खत्म कर सकते हैं।

0

यदि आपके पास संपत्तियों में निर्देशिकाओं का गहरा पेड़ है, तो आप पहली बार पता लगा सकते हैं कि कोई आइटम फ़ाइल या निर्देशिका है या फिर उस पर .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 
} 
1

आप APK पैकेज संपर्क कर सकते हैं के रूप में यह एक ज़िप फ़ाइल है और सभी प्रविष्टियों को जावा के builtin zipfile का उपयोग कर पढ़ें: यह मेरा समाधान मैं इस के लिए खोज की है है। यह आपको अपने सभी पथों के साथ सभी फाइल नाम देगा। शायद यह पता लगाना मुश्किल नहीं है कि आपके पास कौन सी निर्देशिका है।

अभी तक यह परीक्षण किया गया सबसे तेज़ तरीका है।

क्रेडिट @ को जाता है के obastemur jxcore-android-basics sample project

+0

अस्वीकरण: स्व पदोन्नति। मैंने विशेष रूप से संकलन समय सुरक्षा जोड़ने के लिए एक ग्रेडल प्लगइन बनाया है, जिसमें प्रत्येक फ़ोल्डर के लिए स्वचालित रूप से एक सूची उत्पन्न करने का अतिरिक्त लाभ होता है। Github.com/oriley-me/crate पर इसे देखें –