Android - проблема с настройкой асинхронного потока для загрузки массива изображений из Интернета - PullRequest
0 голосов
/ 06 января 2012

Я прочитал десятки постов об этом и перепробовал столько же решений, но, похоже, я не могу заставить что-либо работать. Я заполняю представление списка из ответа JSON, который включает в себя множество (иногда более 100) строк. С каждой строкой связано (и отличается) изображение.

В тесте производительности, когда я не загружал / не отображал изображения, 134 строки из ответа JSON были обработаны и отображены менее чем за 2 секунды. Потрясающие! Однако когда я снова включил загрузку / отображение изображений, это заняло около 10 лет.

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

Мне кажется, у меня есть идея, где мне нужно обрабатывать изображения в фоновом режиме, но я не совсем уверен, как его настроить.

Прямо сейчас изображения (вместе со всеми другими данными) загружаются в массив. Каждое изображение загружается прямо сейчас, отсюда и невероятный кошмар производительности.

Вот некоторые фрагменты кода из моего основного занятия ...

InputStream is = null;
        //http post
        try{
            postQuery = "my api path";
            HttpClient httpclient = new DefaultHttpClient();
            HttpPost httppost = new HttpPost(postQuery);
            HttpResponse response = httpclient.execute(httppost); 
            HttpEntity entity = response.getEntity();
            is = entity.getContent();
        }catch(Exception e){
            Log.e("log_tag", "Error in http connection "+e.toString());
        }

      //convert response to string
        try{
            BufferedReader reader = new BufferedReader(new InputStreamReader(is,"iso-8859-1"),8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
        }
        is.close();
        result=sb.toString();
        }catch(Exception e){
        Log.e("log_tag", "Error converting result "+e.toString());
        }

        m_bottles = new ArrayList<Bottles>();
        this.m_adapter = new BottleAdapter(this, R.layout.bottlelistimagelayout, m_bottles);
                setListAdapter(this.m_adapter);

        viewBottles = new Runnable(){
            public void run() {
                getBottles();
            }
        };
    Thread thread =  new Thread(null, viewBottles, "MagentoBackground");
        thread.start();
        m_ProgressDialog = ProgressDialog.show(SpiritsBottles.this,    
              "Please wait...", "Retrieving data ...", true);
public class Bottle{
        public String name_abbrArray;
    }
    private void getBottles(){
        try{
            JSONObject row = new JSONObject(result);
            array = row.getJSONArray("bottles");
            m_bottles = new ArrayList<Bottles>();
            for(int i=0;i<array.length();i++){
                row = array.getJSONObject(i);
                bottleID = row.getInt("id");
                name_abbr = row.getString("name_abbr");
                bottlePicture = row.getString("image");
                Bottles o = new Bottles();
                o.setbottleID(bottleID);
                o.setname_abbr(name_abbr);
                o.setbottlePicture(bottlePicture);
                m_bottles.add(o);
                Log.i("ARRAY", "" + m_bottles.size() + " - " + i + " / " + array.length()+"m_bottles size = "+m_bottles.size());
            }
          } catch (Exception e) {
            Log.e("PROC - bottleid = "+bottleNamesMap.get("bottlePicture2"), e.getMessage());
          }
          runOnUiThread(returnRes);
      }

    private Runnable returnRes = new Runnable() {

    public void run() {
        if(m_bottles != null && m_bottles.size() > 0){
            m_adapter.notifyDataSetChanged();
            for(int i=0;i<m_bottles.size();i++)
            m_adapter.add(m_bottles.get(i));
        }
        m_ProgressDialog.dismiss();
        m_adapter.notifyDataSetChanged();
        }
      };
private class BottleAdapter extends ArrayAdapter<Bottles> {

        private ArrayList<Bottles> items;

        public BottleAdapter(Context context, int textViewResourceId, ArrayList<Bottles> items) {
                super(context, textViewResourceId, items);
                this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
                View v = convertView;
                if (v == null) {
                    LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    v = vi.inflate(R.layout.bottlelistimagelayout, null);
                }
                Bottles o = items.get(position);
                if (o != null) {
                        final TextView bottlenametv = (TextView) v.findViewById(R.id.bottlename);
                        final ImageView iv = (ImageView) v.findViewById(R.id.icon);
                        if (bottlenametv != null) {
                            bottlenametv.setText(o.getname_abbr());
                            bottlenametv.setOnClickListener(new View.OnClickListener() {
                                public void onClick(View v) {
                                    Intent intent = new Intent(getApplicationContext(), SingleBottleDisplay.class);
                                    intent.putExtra("name", bottlenametv.getText());
                                    startActivityForResult(intent,0);
                                }
                            });
                        }
                        if(iv != null){
                            iv.setImageBitmap(o.getbottlePicture());
                            iv.setOnClickListener(new View.OnClickListener() {
                                public void onClick(View v) {
                                    Intent intent = new Intent(getApplicationContext(), SingleBottleDisplay.class);
                                    intent.putExtra("name", bottlenametv.getText());
                                    startActivityForResult(intent,0);
                                }
                            });
                        }
                }
                return v;
        }
    }

Вот мой класс бутылок, в который я в настоящее время загружаю встроенное изображение каждой строки (что является моей проблемой производительности).

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class Bottles {

    private int bottleID;
    private String name_abbr;
    private Bitmap bottlePicture;

    public int getbottleID() {
        return bottleID;
    }
    public void setbottleID(Integer bottleID) {
        this.bottleID = bottleID;
    }
    public String getname_abbr() {
        return name_abbr;
    }
    public void setname_abbr(String name_abbr) {
        this.name_abbr = name_abbr;
    }
    public void setbottlePicture(String bottlePicture) throws MalformedURLException, IOException {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize=6;
        Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(bottlePicture).getContent(), null, options);
        this.bottlePicture = bitmap;
    }
    public Bitmap getbottlePicture() {
        return bottlePicture;
    }
}

Я действительно, очень надеюсь, что кто-то может помочь мне с этим, так как я нахожусь в конце своей веревки и почти вне кофе ...:)

Ответы [ 3 ]

2 голосов
/ 06 января 2012

Я думаю, было бы лучше, если бы вы поместили загрузку изображения из второго фрагмента в поток.

См. http://developer.android.com/resources/articles/painless-threading.html для более подробной информации о потоках, но в основном вы бысоздайте метод наподобие 'private Bitmap downloadImage (url) {...}' внутри которого будет что-то вроде этого:

new Thread(new Runnable() {
public void run() {
  final Bitmap b = BitmapFactory.decodeStream((InputStream)new URL(url).getContent());
  mImageView.post(new Runnable() {
    public void run() {
      return b;
    }
  });
}
}).start();

Этот код не проверен, но он должен работать:)

1 голос
/ 06 января 2012

Большое спасибо за информацию, Тодд и Элайджа.В итоге я использовал библиотеку UniversalImageLoader, предоставленную nostra13 here .

Моя реализация кода была в getView () первого приведенного выше фрагмента и в итоге выглядела так:.

final ImageView iv = (ImageView) v.findViewById(R.id.icon);

if(iv != null){
         //iv.setImageBitmap(o.getbottlePicture());
         ImageLoader imageLoader = ImageLoader.getInstance();
         imageLoader.init(ImageLoaderConfiguration.createDefault(getContext()));
         imageLoader.displayImage(o.getbottlePicture(), iv);
         iv.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), SingleBottleDisplay.class);
                intent.putExtra("name", bottlenametv.getText());
                startActivityForResult(intent,0);
            }
         });
}

Работает отлично!Большое спасибо nostra13 за фантастическую библиотеку !!

1 голос
/ 06 января 2012

Это классическая проблема со списками, которые должны загружать изображения.Принятый шаблон - это «отложенная загрузка» изображений, что по сути означает, что в getView (в вашем адаптере) вы запускаете AsyncTask, который загружает изображение.Представление возвращается и поток пользовательского интерфейса продолжается, поэтому у вас нет проблем с производительностью.Позже AsyncTask завершает работу и добавляет загруженное изображение в представление, которое было возвращено ранее.

Поиск в «ленивом представлении загрузки» вернет массу результатов.

Это также лучшерешение, потому что не все изображения загружаются одновременно, что может вызвать проблемы нехватки памяти, когда ваш список становится слишком большим.

Дополнительный выигрыш в производительности можно получить, создав кэш изображений, чтобы вам не приходилось перезагружать изображение, если оно находится в кеше.Это может быть излишним, но HashMap imagename => SoftReference (Bitmap) может обеспечить эту функциональность.Если ключ существует в кэше, а SoftReference все еще действует, используйте там Bitmap;в противном случае используйте асинхронную задачу, чтобы перезагрузить образ (и сохранить его в кеше ...)

Наконец - есть интересная складка для всего этого.Как вы можете видеть, getView () иногда перезапускает представление, то есть когда конкретный элемент списка прокручивается за пределы экрана, он иногда используется повторно (чтобы избежать затрат на воссоздание нового представления).Таким образом, может выполняться асинхронная задача, которая ссылается на представление, которое в настоящее время используется для какого-либо другого нового объекта.шаблон отложенной загрузки обычно устанавливает тег в представлении, и если этот тег не изменяется при возврате асинхронной задачи, он идет вперед и добавляет изображение в представление.В противном случае подразумевается, что изображение больше не нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...