이미지+텍스트 가 나오는 리스트 뷰를 만들고 있는 중입니다

 

xml파일을 파싱해서 이미지와 텍스트를 리스트뷰에 뿌려주고 있는데 이미지 로딩 시 성능저하 때문에

 

구글링 해서 적절한 소스를 구해다가 적용하였는데 원본 소스에서는 문제가 없는데

 

적용시 AndroidHttpClient 로 이미지를 다운받으면 다운을 받지 못하는 현상이 발생합니다

 package my.realtyServe.Pkg;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import android.widget.AbsListView.OnScrollListener;
public class ServeMaemulListActTemp extends Activity {
    /** Called when the activity is first created. */
 public  int      itemNum = 0;
 public  int      totalListNum;
 public  GoodListAdapter   mAdapter;
 public  TextView     mTestText;
 public  LinearLayout   mTestLayout;
 public  ArrayList<GoodData>  arrItems;
 public  GoodData     mi;
 public  String      mResult;
 private ListView     mListView;
 private boolean     mLockListView; 
 public Intent      intent; 
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.servemaemullist);
        mLockListView  = false; 
        arrItems   = new ArrayList<GoodData>();
        mAdapter   = new GoodListAdapter(this, R.layout.gooditem, arrItems);
        mListView   = (ListView) findViewById(R.id.list);
        mTestLayout  = (LinearLayout) View.inflate(this, R.layout.progressloading, null);
        
        mTestLayout.setVisibility(View.INVISIBLE);
        mListView.addFooterView(mTestLayout);
        mListView.setAdapter(mAdapter);
              
        mListView.setOnScrollListener(new OnScrollListener() {
   
   // @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
    // TODO Auto-generated method stub
    
   }
   
   // @Override
   public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
    // TODO Auto-generated method stub
    Log.v("testLog", "Start_onScroll");
    Log.v("testLog", "첫번째 firstVisibleItem = " + firstVisibleItem);
    Log.v("testLog", "뷰내에 보이는 아이템 visibleItemCount = " + visibleItemCount);
    Log.v("testLog", "총 아이템수totalItemCount = " + totalItemCount);
    
    
    int count = totalItemCount - visibleItemCount; 
    Log.v("testLog", "count = " + count);
    
    if(firstVisibleItem >= count && totalItemCount != 0 && mLockListView == false){
     Log.v("testLog", "excuteGetMoreItems");
     mTestLayout.setVisibility(View.VISIBLE);
     new getMoreItems().execute(arrItems);  
    }
    else{
     Log.v("testLog", "dontExcuteGetMoreItems");
     mTestLayout.setVisibility(View.INVISIBLE);
    }
     
   }
  });        
    }
    
    private class getMoreItems extends AsyncTask<ArrayList<GoodData>, Integer, Long> {
  @Override
  protected Long doInBackground(ArrayList<GoodData>... params) {
   // TODO Auto-generated method stub
   Long result = 0L;
      
   boolean initem = false;
   mLockListView = true;
   String imageUrl = ""; 
   String strGoodName = "";
   Log.v("testLog", "startGetMoreItems");
   try {
    URL url = new URL("http://mog.serve.co.kr/inc/lbs_maemul.asp");
    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
    if (conn != null) {
     conn.setConnectTimeout(10000);
     conn.setUseCaches(false);
     Log.v("testLog", "startConnect");
     if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
      Log.v("testLog", "---------------startLoadingXmlData"); 
      InputStream    xmlData = conn.getInputStream();
      XmlPullParserFactory  factory = XmlPullParserFactory.newInstance();
      XmlPullParser    parser  = factory.newPullParser();
      parser.setInput(xmlData, "utf-8");
      int eventType      = parser.getEventType();
      
      while (eventType != XmlPullParser.END_DOCUMENT) {
       switch (eventType) {
       case XmlPullParser.START_DOCUMENT:
        break;
       case XmlPullParser.END_DOCUMENT:
        break;
       case XmlPullParser.START_TAG:
        if (parser.getName().equals("item")) {
         imageUrl = parser.getAttributeValue(null,"image1");
         initem = true;
        }
        break;
       case XmlPullParser.END_TAG:
        break;
       case XmlPullParser.TEXT:
        if (initem) {
         itemNum +=1;
         mi = new GoodData("http://image.serve.co.kr/get_serve_image.asp?file_w=147&file_h=112&file="+imageUrl, itemNum+")"+parser.getText());
         arrItems.add(mi);
         Log.v("testLog", imageUrl+parser.getText());
         initem = false;
        }
        break;
       }
       eventType = parser.next();
      }
     }
     conn.disconnect();
     Log.v("testLog", "disconnect");
    }
   } 
   catch (Exception ex) {;}
    
   //try { Thread.sleep(3000); } catch (Exception e) { ; } // 3초 지연. 네트워크 작업 등을 시뮬레이션
   // dialog.dismiss();
   
   return null;
  }
  
 
  protected void onPostExecute(Long result){
   mAdapter.notifyDataSetChanged();
   mLockListView = false; 
            // 작업이 완료 된 후 할일
            // dialog.dismiss();
            // super.onPostExecute(result);
  }
  /*
        private ProgressDialog dialog;
        
  protected void onPreExecute() {
            // 작업을 시작하기 전 할일
            dialog = new ProgressDialog(Main.this);
            dialog.setTitle("Indeterminate");
            dialog.setMessage("Please wait while loading...");
            dialog.setIndeterminate(true);
            dialog.setCancelable(true);
            dialog.show();
            super.onPreExecute();
        }
  */
    }
    
    class GoodListAdapter extends BaseAdapter {
     Context maincon;
     LayoutInflater Inflater;
     ArrayList<GoodData> arrItems;
     int layout;
     private final ImageDownloader imageDownloader = new ImageDownloader();
    
     public GoodListAdapter(Context context, int alayout, ArrayList<GoodData> arItems) {
      maincon  = context;
      Inflater  = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      arrItems  = arItems;
      layout   = alayout;
     }
     public int getCount() {
      return arrItems.size();
     }
     public String getItem(int position) {
      return arrItems.get(position).goodName;
     }
     public long getItemId(int position) {
      return position;
     }
     // 각 항목의 뷰 생성
     public View getView(int position, View convertView, ViewGroup parent) {
      final int pos = position;
      GoodViewHolder viewHolder;
      
      if (convertView == null) {
       Log.v("testLog", "getView");
       Log.v("testLog", "getView_goodImage:"+arrItems.get(position).goodImage);
       Log.v("testLog", "getView_goodName:"+arrItems.get(position).goodName);
       Log.v("testLog", "getView_position:"+pos);
       convertView  = Inflater.inflate(layout, parent, false);
       viewHolder   = new GoodViewHolder();
       
       viewHolder.vGoodImage  = (ImageView) convertView.findViewById(R.id.img);
       viewHolder.vGoodName  = (TextView) convertView.findViewById(R.id.text);
       viewHolder.vOrderBtn  = (Button) convertView.findViewById(R.id.btn);
       
       
       convertView.setTag(viewHolder);
      } 
      else
      { Log.v("testLog", "convertView :"+(GoodViewHolder) convertView.getTag());
       viewHolder = (GoodViewHolder) convertView.getTag();
      }
      //Bitmap bm = LoadImage(arrItems.get(position).goodImage);     
      //viewHolder.vGoodImage.setImageBitmap(bm);
      imageDownloader.download(arrItems.get(position).goodImage, viewHolder.vGoodImage);
      
      
      viewHolder.vGoodName.setText(arrItems.get(position).goodName);
      viewHolder.vOrderBtn.setOnClickListener(new Button.OnClickListener(){
    public void onClick(View v) {
     String str = arrItems.get(pos).goodName + "를 주문합니다.";
     Toast.makeText(maincon, str, Toast.LENGTH_SHORT).show();
    }
   });;       
      return convertView;
     }
     public ImageDownloader getImageDownloader()
     {
      return imageDownloader;
     }
    }
    
    class GoodData {
     GoodData(String vGoodImage, String vGoodName){
      goodImage  = vGoodImage;
      goodName = vGoodName;
     }
     
     String goodImage;
     String goodName;
    }
    
    public class GoodViewHolder
    {
     public ImageView vGoodImage;
     public TextView  vGoodName;
     public Button  vOrderBtn;
    }
          
    public boolean onKeyDown(int keyCode, KeyEvent event) {
     
        if ((keyCode == KeyEvent.KEYCODE_BACK)){
         intent = new Intent(ServeMaemulListActTemp.this,ServeMainAct.class);
            startActivity(intent);
            finish();
         
        } 
        return super.onKeyDown(keyCode, event); 
    }
    
    public boolean onCreateOptionsMenu(Menu menu) {
        // 액티비티에서 XML 형식으로 구성된 메뉴를 실제 사용 가능한 Options Menu 로 
        // 재구성하게 해주는 MenuInflater 객체를 얻는다.
        MenuInflater inflater = getMenuInflater();
        // XML 형식으로 구성된 메뉴를 사용 가능한 Options Menu 로 재구성한다.
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }
    public boolean onOptionsItemSelected(MenuItem item) {
        // 텍스트뷰를 얻는다.
       
        switch (item.getItemId()) {
            case R.id.menu_1: 
             
            case R.id.menu_2:
             finish();
             Toast.makeText(ServeMaemulListActTemp.this, "프로그램을 종요합니다!", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.menu_3:return true;
            default :
                // 사용자가 선택에 대한 처리를 완료하지 못하면 false 를 반환해도 되지만 
                // 바로 false 를 반환하지 않고, 슈퍼 클래스의 onOptionsItemSelected메소드를 
                // 하여 해당 메소드가 반환하는 반환값을 반환해주는 것이 좋습니다. 
                return super.onOptionsItemSelected(item);
        }
    }
}

 

 package my.realtyServe.Pkg;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
 * This helper class download images from the Internet and binds those with the provided ImageView.
 *
 * <p>It requires the INTERNET permission, which should be added to your application's manifest
 * file.</p>
 *
 * A local cache of downloaded images is maintained internally to improve performance.
 */
public class ImageDownloader {
    private static final String LOG_TAG = "ImageDownloader";
    public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT }
    private Mode mode = Mode.CORRECT;
    
    /**
     * Download the specified image from the Internet and binds it to the provided ImageView. The
     * binding is immediate if the image is found in the cache and will be done asynchronously
     * otherwise. A null bitmap will be associated to the ImageView if an error occurs.
     *
     * @param url The URL of the image to download.
     * @param imageView The ImageView to bind the downloaded image to.
     */
    public void download(String url, ImageView imageView) {
     Log.v("testLog", "url"+url);
     Log.v("testLog", "imageView"+imageView);
        resetPurgeTimer();
        Bitmap bitmap = getBitmapFromCache(url);
        if (bitmap == null) {
            forceDownload(url, imageView);
        } else {
            cancelPotentialDownload(url, imageView);
            imageView.setImageBitmap(bitmap);
        }
    }
    /*
     * Same as download but the image is always downloaded and the cache is not used.
     * Kept private at the moment as its interest is not clear.
       private void forceDownload(String url, ImageView view) {
          forceDownload(url, view, null);
       }
     */
    /**
     * Same as download but the image is always downloaded and the cache is not used.
     * Kept private at the moment as its interest is not clear.
     */
    private void forceDownload(String url, ImageView imageView) {
        // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.
        if (url == null) {
            imageView.setImageDrawable(null);
            return;
        }
        if (cancelPotentialDownload(url, imageView)) {
            switch (mode) {
                case NO_ASYNC_TASK:
                    Bitmap bitmap = downloadBitmap(url);
                    addBitmapToCache(url, bitmap);
                    imageView.setImageBitmap(bitmap);
                    break;
                case NO_DOWNLOADED_DRAWABLE:
                    imageView.setMinimumHeight(156);
                    BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
                    task.execute(url);
                    break;
                case CORRECT:
                    task = new BitmapDownloaderTask(imageView);
                    DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
                    imageView.setImageDrawable(downloadedDrawable);
                    imageView.setMinimumHeight(156);
                    task.execute(url);
                    break;
            }
        }
    }
    /**
     * Returns true if the current download has been canceled or if there was no download in
     * progress on this image view.
     * Returns false if the download in progress deals with the same url. The download is not
     * stopped in that case.
     */
    private static boolean cancelPotentialDownload(String url, ImageView imageView) {
        BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
        if (bitmapDownloaderTask != null) {
            String bitmapUrl = bitmapDownloaderTask.url;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                bitmapDownloaderTask.cancel(true);
            } else {
                // The same URL is already being downloaded.
                return false;
            }
        }
        return true;
    }
    /**
     * @param imageView Any imageView
     * @return Retrieve the currently active download task (if any) associated with this imageView.
     * null if there is no such task.
     */
    private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
        if (imageView != null) {
            Drawable drawable = imageView.getDrawable();
            if (drawable instanceof DownloadedDrawable) {
                DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
                return downloadedDrawable.getBitmapDownloaderTask();
            }
        }
        return null;
    }
    Bitmap downloadBitmap(String url) {
        final int IO_BUFFER_SIZE = 4 * 1024;
        // AndroidHttpClient is not allowed to be used from the main thread

//이부분에서  Mode.NO_ASYNC_TASK 가 아닌경우는 AndroidHttpClient.newInstance("Android") 을 사용하도록 
되어 있고 statusCode 가 오류코드를 밸생시켜 그이후 작업이 진행 되지 않더군요
 DefaultHttpClient()  을 쓰도록 하면 아무 문제가 없습니다
원문에보니  AndroidHttpClient 메인스레드에서 실행이 안된다 이런 글귀가 있던데 그문제와 상관있는지 모르겠습니다
이미지 다운로더 글래스를 리스트뷰에서 쓸때 어떤 문제가 있는지 파악이 안되는군요
        final HttpClient client = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() :
            AndroidHttpClient.newInstance("Android");
        final HttpGet getRequest = new HttpGet(url);

        try {
            HttpResponse response = client.execute(getRequest);
            final int statusCode = response.getStatusLine().getStatusCode();
           if (statusCode != HttpStatus.SC_OK) {
                Log.w("ImageDownloader", "Error " + statusCode +
                        " while retrieving bitmap from " + url);
                return null;
            }
            final HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream inputStream = null;
                try {
                    inputStream = entity.getContent();
                    // return BitmapFactory.decodeStream(inputStream);
                    // Bug on slow connections, fixed in future release.
                    return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                    entity.consumeContent();
                }
            }
        } catch (IOException e) {
            getRequest.abort();
            Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
        } catch (IllegalStateException e) {
            getRequest.abort();
            Log.w(LOG_TAG, "Incorrect URL: " + url);
        } catch (Exception e) {
            getRequest.abort();
            Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
        } finally {
            if ((client instanceof AndroidHttpClient)) {
                ((AndroidHttpClient) client).close();
            }
        }
        return null;
    }
    /*
     * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
     */
    static class FlushedInputStream extends FilterInputStream {
        public FlushedInputStream(InputStream inputStream) {
            super(inputStream);
        }
        @Override
        public long skip(long n) throws IOException {
            long totalBytesSkipped = 0L;
            while (totalBytesSkipped < n) {
                long bytesSkipped = in.skip(n - totalBytesSkipped);
                if (bytesSkipped == 0L) {
                    int b = read();
                    if (b < 0) {
                        break;  // we reached EOF
                    } else {
                        bytesSkipped = 1; // we read one byte
                    }
                }
                totalBytesSkipped += bytesSkipped;
            }
            return totalBytesSkipped;
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    /**
     * The actual AsyncTask that will asynchronously download the image.
     */
    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
        private String url;
        private final WeakReference<ImageView> imageViewReference;
        public BitmapDownloaderTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }
        /**
         * Actual download method.
         */
        @Override
        protected Bitmap doInBackground(String... params) {
            url = params[0];
            return downloadBitmap(url);
        }
        /**
         * Once the image is downloaded, associates it to the imageView
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }
            addBitmapToCache(url, bitmap);
            if (imageViewReference != null) {
                ImageView imageView = imageViewReference.get();
                BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                // Change bitmap only if this process is still associated with it
                // Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode)
                if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {
                    imageView.setImageBitmap(bitmap);
                }
            }
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    /**
     * A fake Drawable that will be attached to the imageView while the download is in progress.
     *
     * <p>Contains a reference to the actual download task, so that a download task can be stopped
     * if a new binding is required, and makes sure that only the last started download process can
     * bind its result, independently of the download finish order.</p>
     * <p>다운로드 중인 이미지가 있는 경우, ImageView에 검은 화면을 표시하기 위해 ColorDrawable을 상속</p>
     * 
     */
    static class DownloadedDrawable extends ColorDrawable {
        private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
        public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
            super(Color.BLACK);
            bitmapDownloaderTaskReference =
                new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
        }
        public BitmapDownloaderTask getBitmapDownloaderTask() {
            return bitmapDownloaderTaskReference.get();
        }
    }
    
    
    
    
    
    
    
    
    
    
    public void setMode(Mode mode) {
        this.mode = mode;
        clearCache();
    }
    
    /*
     * Cache-related fields and methods.
     * 
     * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
     * Garbage Collector.
     */
    
    private static final int HARD_CACHE_CAPACITY = 10;
    private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
    // Hard cache, with a fixed maximum capacity and a life duration
    private final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            if (size() > HARD_CACHE_CAPACITY) {
                // Entries push-out of hard reference cache are transferred to soft reference cache
                sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                return true;
            } else
                return false;
        }
    };
    // Soft cache for bitmaps kicked out of hard cache
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
        new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
    private final Handler purgeHandler = new Handler();
    private final Runnable purger = new Runnable() {
        public void run() {
            clearCache();
        }
    };
    /**
     * Adds this bitmap to the cache.
     * @param bitmap The newly downloaded bitmap.
     */
    private void addBitmapToCache(String url, Bitmap bitmap) {
        if (bitmap != null) {
            synchronized (sHardBitmapCache) {
                sHardBitmapCache.put(url, bitmap);
            }
        }
    }
    /**
     * @param url The URL of the image that will be retrieved from the cache.
     * @return The cached bitmap or null if it was not found.
     */
    private Bitmap getBitmapFromCache(String url) {
        // First try the hard reference cache
        synchronized (sHardBitmapCache) {
            final Bitmap bitmap = sHardBitmapCache.get(url);
            if (bitmap != null) {
                // Bitmap found in hard cache
                // Move element to first position, so that it is removed last
                sHardBitmapCache.remove(url);
                sHardBitmapCache.put(url, bitmap);
                return bitmap;
            }
        }
        // Then try the soft reference cache
        SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
        if (bitmapReference != null) {
            final Bitmap bitmap = bitmapReference.get();
            if (bitmap != null) {
                // Bitmap found in soft cache
                return bitmap;
            } else {
                // Soft reference has been Garbage Collected
                sSoftBitmapCache.remove(url);
            }
        }
        return null;
    }
 
    /**
     * Clears the image cache used internally to improve performance. Note that for memory
     * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
     */
    public void clearCache() {
        sHardBitmapCache.clear();
        sSoftBitmapCache.clear();
    }
    /**
     * Allow a new delay before the automatic cache clear is done.
     */
    private void resetPurgeTimer() {
        purgeHandler.removeCallbacks(purger); //반복을 중단
        purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE); //delay해줌
    }
}

고수님들 답변 부탁드립니다 2틀째 삽질만 했더니 피로가 엄청 쌓이는군요 ㅠㅠ