3

У меня есть следующая задача Async, которая должна просто загрузить изображение с заданного URL-адреса. Изображения существуют и у меня есть доступ к нимIs BitmapFactory.decodeStream Thread безопасен? Иногда моя задача Async заканчивается, прежде чем она завершится

private class FetchVehicleImage extends AsyncTask<String, Integer, Bitmap> 
    { 

     private ProgressBar mSpinner; 
     private ImageView mImage; 
     private String imagesBaseUrl = "http://mywebsite.net/images/"; 
     private URL url = null; 

     @Override 
     protected void onPreExecute() 
     { 
      mImage = (ImageView) findViewById(R.id.vehicle_image); 
      mSpinner = (ProgressBar) findViewById(R.id.vehicle_image_progress_bar); 
      mSpinner.setIndeterminate(true); 
      mSpinner.setVisibility(View.VISIBLE); 
      mImage.setVisibility(View.GONE); 
     } 

     @Override 
     protected Bitmap doInBackground(String... strings) 
     { 
      Bitmap bm = null; 

      try 
      { 
       url = new URL(imagesBaseUrl + strings[0]); 

       HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
       conn.setDoInput(true); 
       conn.connect(); 
       InputStream is = conn.getInputStream(); 
       bm = BitmapFactory.decodeStream(is); 
      } 
      catch (Exception e) 
      { 
       e.printStackTrace(); 
      } 
      return bm; 
     } 

     protected void onPostExecute(final Bitmap result) 
     { 
      if (result != null) 
      { 
       mImage.setImageBitmap(result); 
      } 
      mImage.setVisibility(View.VISIBLE); 
      mSpinner.setVisibility(View.GONE); 
     } 
    } 

Я никогда не видел исключение в doInBackground, однако иногда bm возвращается как нуль, но ее очень прерывистый. У меня 4 изображения, 3 из этих нагрузок отлично подходят каждый раз, но один загружается, если я нахожусь в точке останова на задаче bm, предположительно предоставляя ему достаточно времени для выполнения своей работы?

Я думал, что doInBackground должен работать на фоновом потоке, поэтому я должен всегда либо получить изображение, либо получить исключение?

+0

Возможно, эта статья может пролить некоторый свет http://foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html, но в вашем случае только 4 изображения вряд ли применимы. Я бы пошел с обычной Thread (override run()) и runOnUiTread в этом случае возможно –

ответ

2

N.B. Если ваше приложение просто исчерпало собственную память для своих растровых изображений, то этот подход не поможет. Если у вас возникают проблемы с растровыми изображениями, особенно с предварительными сотами, я не могу переоценить важность понимания отношения кучи Dalvik к собственной памяти. Обсуждение этого вопроса было исключительно полезным для меня - стоит прослушать всю дорогу через Dubroy's Heap Presentation

И затем мой ответ на ваш вопрос выше. , , Я не могу это доказать, но у меня очень сильное подозрение, что оно не является потокобезопасным. Я ударил это, когда манипулирование изображениями после извлечения. Как и в примере выше, когда я запрашиваю несколько файлов изображений и обрабатываю их по мере их поступления, я получаю ошибки OutOfMemory, которые я улавливаю, только чтобы обнаружить, что и куча, и доступная собственная резервная память хороши (> 100 тыс. И > 100M соответственно). И, иногда, извлечения работает (как вы описали), но иногда нет. На некоторых устройствах он более устойчив, чем другие. Когда вы призвали придумать историю, почему это так, я полагаю себе, что на некоторых устройствах могут быть аппаратные средства обработки изображений (например, jpg-кодеры), а не другие, которые могут или не могут использоваться в собственных библиотеках ОС. Затем я сразу же обвиняю эти узкие места в аппаратных средствах за то, что они не являются потокобезопасными - все без малейшего клочка чего-либо, напоминающего доказательства. Во всяком случае, единственный подход, который я нашел, работает на всех устройствах моей тестовой стабильной (около дюжины) - надежно - заключается в том, чтобы изолировать части обработки растровых изображений и однопоточность.

В приведенном выше примере вы по-прежнему будете использовать AsyncTask для фактического извлечения файлов из сети и записи их в хранилище где-то (поток необработанного байта). Когда AsyncTask завершает работу (т. Е. Вызывает его делегат для), вы можете сделать что-то вроде моего класса Poster ниже.

В моей деятельности (где я сделать несколько запросов на загрузку), создать глобальный исполнитель в классе, который инстанцированный сначала в UI-Thread:

public ExecutorService mImagePipelineTask = null; // Thread to use for pipelining images (overlays, etc.) 

А затем инициализировать его:

 mImagePipelineTask = Executors.newSingleThreadExecutor(); 

Затем я обходится без использования AsyncTask, чтобы получить контроль над количеством потоков в пуле Thread. Мои асинхронные биты выглядят следующим образом:

public class PosterImage extends HashMap<String, Object> { 

     private final String TAG = "DEBUG -- " + ClassUtils.getShortClassName(this.getClass()); 
     private PosterImageDelegate mPosterDelegate = null; 
     private Drawable mBusyDrawable = null; 
     private Drawable mErrorDrawable = null; 
     private ExecutorService mImagePipelineTask = null; 

     /* 
     * Globals 
     */ 
     Context mContext = null; 

     /* 
     * Constructors 
     */ 
     public PosterImage() { 
     } 

     public PosterImage(PlaygroundActivity aContext) { 
      mContext = aContext; 
      mImagePipelineTask = aContext.mImagePipelineTask; 
      mBusyDrawable = mContext.getResources().getDrawable(R.drawable.loading); 
      mErrorDrawable = mContext.getResources().getDrawable(R.drawable.load_error); 
     } 

Затем некоторые фрагменты, которые вам, вероятно, не волнуют. , ,а затем некоторые инициализации вещи, например, как установить наш делегат (вы хотите интерфейс PosterImageDelegate, конечно):

public void setPosterDelegate(PosterImageDelegate aPosterDelegate) { 
     mPosterDelegate = aPosterDelegate; 
    } 

и затем биты, которые делают манипуляции с изображениями, а также в качестве побочного эффекта, используйте BitmapFactoryDrawable) классов. Для того, чтобы использовать эту функцию, вы экземпляр объекта PosterImage, установить себя в качестве делегата, а затем вызвать этот парень:

public Drawable getPreformattedFileAsync() { 
     if(mFetchFileTask == null) { 
      Log.e(TAG, " -- Task is Null!!, Need to start an executor"); 
      return(mErrorDrawable); 
     } 
     Runnable job = new Runnable() { 
      public void run() { 
       Thread.currentThread().setPriority(Thread.MIN_PRIORITY); 
       Thread.currentThread().yield(); 
       if(mPosterDelegate != null) { 
        Drawable retDrawable = getPreformattedFile(); 
        if(retDrawable != null) { 
          mPosterDelegate.onDrawableRequest(retDrawable); 
        } else { 
         mPosterDelegate.onDrawableRequest(mErrorDrawable); 
        } 
       } 
      } 
     }; 
     mImagePipelineTask.execute(job); 
     return(mBusyDrawable); 
    } 

    public Drawable getPreformattedFile() { 
     Drawable ret = null; 
     try { 
      FileInputStream in = new FileInputStream(preformattedFileName()); 
      ret = Drawable.createFromStream(in, null); 
        // do something interesting with the Drawable 
     } catch(OutOfMemoryError e) { 
      System.gc(); 
      e.printStackTrace(); 
         // Will return null on its own 
     } catch(Exception e) { 
      Log.e(TAG, "Trouble reading PNG file ["+e+"]"); 
     } 
     return(ret); 
    } 

Когда это возвращается, вызывающий объект (в UI-Thread) имеет «занята» вытяжку. Когда делегат получает вызов (после загрузки файла и преобразования в Drawable этим потоком, он готов к загрузке в любой назначаемый вами Drawable-приемник. Любое количество изображений может быть загружено параллельно, и это гарантирует, что фоновый поток буду только когда-либо процесс один образом в то время. к счастью, он делает не подвязать нить пользовательского интерфейса, чтобы сделать процесс обработки изображений

(NB вам все еще нужен в своем классе вызывающего в Handler (тот, который ставит перед собой как делегат), чтобы поток пользовательского интерфейса фактически помещал Drawable в принимающий View/Layout/независимо от того, что угодно). Для попытки завершенности это может выглядеть так:

mHandler.post(new Runnable() { 
    @Override 
    public void run() { 
     aItem.getButton().setBackgroundDrawable(aDrawable); 
     aItem.getButton().postInvalidate(); 
} 
}); 

Возможно, все это помогает, возможно, нет. Но я бы люблю, чтобы услышать окончательный ответ на отличный вопрос, который вы ставите.