2011-11-03 5 views
7

समस्या पहले:एंड्रॉइड - थ्रेड पूल रणनीति और क्या इसे लोड करने के लिए लोडर का उपयोग किया जा सकता है?

  • मैं एक स्वनिर्धारित FragmentStatePagerAdapter के भीतर कई FragmentLists का उपयोग करता है आवेदन पर काम कर रहा हूँ। हो सकता है, संभावित रूप से पर्याप्त संख्या में ऐसे टुकड़े 20 और 40 के बीच कहते हैं।
  • प्रत्येक खंड एक सूची है जिसमें प्रत्येक आइटम में टेक्स्ट या छवि हो सकती है।
  • छवियों एसडी वेब से एसिंक्रोनस रूप से अपलोड करने की और अस्थायी मेमोरी कैश करने के लिए कैश की गई है और यह भी जरूरत है, यदि उपलब्ध
  • टुकड़ा स्क्रीन किसी भी अपलोड और वर्तमान गतिविधि को रद्द कर दिया जाना चाहिए बंद हो जाता है जब (नहीं रोका गया)

मेरा पहला कार्यान्वयन Google से image loader code को अच्छी तरह से जाना जाता है। उस कोड के साथ मेरी समस्या यह है कि यह मूल रूप से AsyncTask प्रति छवि का एक उदाहरण बनाता है। जो मेरे मामले में ऐप को वास्तविक तेज़ मारता है।

चूंकि मैं v4 संगतता पैकेज का उपयोग कर रहा हूं, मैंने सोचा था कि AsyncTaskLoader तक विस्तारित कस्टम लोडर का उपयोग करने से मुझे आंतरिक रूप से थ्रेड पूल लागू होता है। हालांकि मेरे अप्रिय आश्चर्य के लिए यदि मैं इस कोड को कई बार निम्नलिखित निष्पादन निष्पादित करता हूं तो पिछले को बाधित कर देगा। मैं अपने ListView#getView विधि में इस राशि कहते हैं:

getSupportLoaderManager().restartLoader(0, args, listener); 

इस विधि प्रत्येक सूची आइटम उस दृश्य में आता है के लिए पाश में मार डाला गया है। और जैसा कि मैंने कहा - प्रत्येक निम्नलिखित आमंत्रण पिछले एक को समाप्त कर देगा। या कम से कम है कि क्या LogCat

11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}] 
11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 
11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader 

के आधार पर हो तो फिर मैंने सोचा कि शायद प्रत्येक लोडर के लिए अद्वितीय आईडी देने मामलों में मदद मिलेगी, लेकिन यह कोई फर्क नहीं लगती है। नतीजतन मैं प्रतीत होता है कि यादृच्छिक छवियों के साथ समाप्त होता है और ऐप मुझे जो भी चाहिए उसे 1/4 भी लोड नहीं करता है।

प्रश्न

  • क्या तरीका लोडर ठीक करने के लिए मैं क्या करना चाहते हैं (और वहाँ एक रास्ता है?)
  • होगा यदि नहीं एक अच्छा तरीका AsyncTask पूल बनाने के लिए क्या है और है शायद इसके कार्यान्वयन का काम कर रहे हैं?

आपको कोड के विचार को लोडर के छीनने वाले संस्करण के बारे में जानकारी देने के लिए जहां वास्तविक डाउनलोड/सहेजें तर्क अलग छवि प्रबंधक वर्ग में है।

public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> { 
     private static final String TAG = ImageLoader.class.getName(); 
     /** Wrapper around BitmapDrawable that adds String field to id the drawable */ 
     TaggedDrawable img; 
     private final String url; 
     private final File cacheDir; 
     private final HttpClient client; 


    /** 
    * @param context 
    */ 
    public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) { 
     super(context); 
     this.url = url; 
     this.cacheDir = cacheDir; 
     this.client = client; 
    } 

    @Override 
    public TaggedDrawable loadInBackground() { 
     Bitmap b = null; 
     // first attempt to load file from SD 
     final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url)); 
     if (f.exists()) { 
      b = BitmapFactory.decodeFile(f.getPath()); 
     } else { 
      b = ImageManager.downloadBitmap(url, client); 
      if (b != null) { 
       ImageManager.saveToSD(url, cacheDir, b); 
      } 
     } 
     return new TaggedDrawable(url, b); 
    } 

    @Override 
    protected void onStartLoading() { 
     if (this.img != null) { 
      // If we currently have a result available, deliver it immediately. 
      deliverResult(this.img); 
     } else { 
      forceLoad(); 
     } 
    } 

    @Override 
    public void deliverResult(final TaggedDrawable img) { 
     this.img = img; 
     if (isStarted()) { 
      // If the Loader is currently started, we can immediately deliver its results. 
      super.deliverResult(img); 
     } 
    } 

    @Override 
    protected void onStopLoading() { 
     // Attempt to cancel the current load task if possible. 
     cancelLoad(); 
    } 

    @Override 
    protected void onReset() { 
     super.onReset(); 
     // Ensure the loader is stopped 
     onStopLoading(); 
     // At this point we can release the resources associated with 'apps' 
     // if needed. 
     if (this.img != null) { 
      this.img = null; 
     } 

    } 

} 
+0

'AsyncTask' पहले से ही पूल का उपयोग करता है। पूल आईआईआरसी 128 धागे तक जाता है, जो आपकी कठिनाई का स्रोत हो सकता है। आप 'java.util.concurrent' कक्षाओं का उपयोग करके अपने स्वयं के थ्रेड पूल को हमेशा कार्यान्वित कर सकते हैं। – CommonsWare

+1

यदि आपका विकास एंड्रॉइड 3.0 (एपीआई लेवल 11) को लक्षित कर रहा है, तो आप नए जोड़े गए एपीआई [AsyncTask.executeOnExecutor()] का उपयोग कर सकते हैं (http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor% 28java.util.concurrent.Executor,% 20 पैराम ...% 2 9) अपने थिंक पूल को अपने AsyncTask निर्माण जीवन चक्र के साथ ठीक से नियंत्रित करें। – yorkw

+0

AsynkTask फिर भी केवल एक बार निष्पादित किया जा सकता है इसलिए मुझे प्रति छवि एक उदाहरण बनाने की आवश्यकता है जिसे मैं लोड कर रहा हूं। इसे 60 बनाएं और यह बहुत सारी ऑब्जेक्ट्स – Bostone

उत्तर

11

ठीक है, तो पहले चीज़ें पहले। एंड्रॉइड के साथ आने वाले AsyncTask को आपके ऐप को डूबना नहीं चाहिए या इसे क्रैश नहीं करना चाहिए। AsyncTasks एक थ्रेड पूल में चलते हैं जहां वास्तव में एक ही समय में अधिकतम 5 धागे निष्पादित होते हैं। जबकि आप निष्पादित करने के लिए कई कार्यों को कतारबद्ध कर सकते हैं, उनमें से केवल 5 ही एक समय में निष्पादित कर रहे हैं। पृष्ठभूमि थ्रेडपूल में इन्हें निष्पादित करके उन्हें आपके ऐप पर कोई प्रभाव नहीं होना चाहिए, उन्हें बस सुचारू रूप से चलना चाहिए।

AsyncTaskLoader का उपयोग करना आपकी समस्या का समाधान नहीं करेगा यदि आप AsyncTask लोडर प्रदर्शन से नाखुश हैं। AsyncTaskLoader बस लोडर इंटरफ़ेस लेता है और इसे AsyncTask से विवाह करता है। तो यह अनिवार्य रूप से लोड हो रहा है लोड करें -> onPostExecute, onStart -> onLoadInBackground। तो यह वही सटीक बात है।

हम का कारण बनता है कि एक asynctask ThreadPool कतार पर हर बार है कि हम एक छवि लोड करने का प्रयास रखा जा करने के लिए हमारे अनुप्रयोग के लिए एक ही छवि लोडर कोड का उपयोग करें। Google के उदाहरण में वे छविदृश्य को अपने एसिंक कार्य के साथ जोड़ते हैं ताकि वे एसिंक कार्य को रद्द कर सकें यदि वे किसी प्रकार के एडाप्टर में छविदृश्य का पुन: उपयोग करने का प्रयास करते हैं। आपको यहां एक समान रणनीति लेनी चाहिए। आपको अपनी छविदृश्य को एसिंक कार्य के साथ पृष्ठभूमि में छवि लोड कर रहा है। जब आपके पास एक टुकड़ा दिखाई नहीं दे रहा है तो आप उस खंड से जुड़े अपने छवि दृश्यों के माध्यम से चक्र चला सकते हैं और लोडिंग कार्यों को रद्द कर सकते हैं। बस AsyncTask.cancel() का उपयोग करना काफी अच्छी तरह से काम करना चाहिए।

तुम भी सरल छवि कैशिंग तंत्र async छवि दृश्य उदाहरण बाहर मंत्र को लागू करने की कोशिश करनी चाहिए। हम बस एक स्थैतिक हैशप बनाते हैं जो यूआरएल से निकलता है -> कमजोर पड़ना। इस तरह छवियों को पुनर्नवीनीकरण किया जा सकता है जब उन्हें होने की आवश्यकता होती है क्योंकि वे केवल एक कमजोर संदर्भ के साथ आयोजित होते हैं।

यहाँ छवि लोड की एक रूपरेखा है कि हम

public class LazyLoadImageView extends ImageView { 
     public WeakReference<ImageFetchTask> getTask() { 
     return task; 
    } 

    public void setTask(ImageFetchTask task) { 
     this.task = new WeakReference<ImageFetchTask>(task); 
    } 

    private WeakReference<ImageFetchTask> task; 

     public void loadImage(String url, boolean useCache, Drawable loadingDrawable){ 

     BitmapDrawable cachedDrawable = ThumbnailImageCache.getCachedImage(url); 
     if(cachedDrawable != null){ 
      setImageDrawable(cachedDrawable); 
      cancelDownload(url); 
      return; 
     } 

     setImageDrawable(loadingDrawable); 

     if(url == null){ 
      makeDownloadStop(); 
      return; 
     } 

     if(cancelDownload(url)){ 
      ImageFetchTask task = new ImageFetchTask(this,useCache); 
      this.task = new WeakReference<ImageFetchTask>(task); 
      task.setUrl(url); 
      task.execute(); 
     } 


     ...... 

     public boolean cancelDownload(String url){ 

     if(task != null && task.get() != null){ 

      ImageFetchTask fetchTask = task.get(); 
      String downloadUrl = fetchTask.getUrl(); 

      if((downloadUrl == null) || !downloadUrl.equals(url)){ 
       fetchTask.cancel(true); 
       return true; 
      } else 
       return false; 
     } 

     return true; 

      } 
    } 

करना तो बस अपनी छवि को देखा गया है कि अपने टुकड़ा में हैं के माध्यम से बारी बारी से और फिर उन्हें जब अपने टुकड़ा खाल रद्द करने और वे दिखाने के अपने टुकड़ा दिखाई दे रहा है है।

+0

यह, संक्षेप में, जिस रणनीति का मैं पहले से उपयोग करता हूं, हालांकि मैं आधुनिक पूल के साथ ModernAsyncTask का उपयोग करता हूं, कमजोर संदर्भ और गैर-प्रदर्शित अपलोड को रद्द करता हूं। लेकिन मुझे लगता है कि लोडर AsyncTask के बस सरल पुन: उपयोग से अधिक है। उदाहरण के लिए, मैं देख सकता हूं कि यदि मैं तेजी से उत्तराधिकार में एकाधिक निष्पादन निर्धारित करता हूं तो बाद में आमंत्रण पिछले एक को रद्द कर देता है। मुझे लगता है कि मुझे एक रात बस सैंपल प्रोजेक्ट की स्थापना करना होगा और पूरी तरह समझने के लिए कोड लोड करना होगा कि लोडर – Bostone

+2

लोडर कैसे काम करता है। AsyncTaskLoader AsyncTask का उपयोग कर लोडर का कार्यान्वयन है। जहां तक ​​थ्रेडिंग जाता है, यह * कुछ भी नहीं * जोड़ता है जो पहले से ही AsyncTask में नहीं है। जब इसे कुछ रद्द करने की आवश्यकता होती है, तो यह केवल AsyncTask को रद्द कर देता है जिसे आप स्वयं को AsyncTask के साथ कर सकते हैं। – hackbod

+0

ठीक है अगर डियान ने ऐसा कहा :) मैंने कहा - मैं अभी भी उस व्यवहार को देखता हूं जहां कई कार्यों को कतारबद्ध नहीं किया जाता है बल्कि बदले में केवल आखिरी ही रहता है। क्या यह सही व्यवहार है? – Bostone

संबंधित मुद्दे