2011-05-03 6 views
0

저는 Android에서 작은 프로젝트를 진행하고 있으며 내 솔루션에 멀티 스레딩을 구현하는 데 심각한 문제가 있습니다. 아래는 YouTube API에서 다운로드 한 그림 및 데이터가있는 사용자 정의 목록을 표시하는 기본 인터페이스의 탭 안에있는 활동 인 클래스입니다.Android : 맞춤 목록보기 및 스레딩 문제

클래스는 정상적으로 작동하지만 처음에는 데이터와 이미지가 인터넷에서로드 될 때 UI를 완전히 차단합니다. 내가 스레딩을 구현해야 할 필요가 있고 여러 가지를 시도했지만, 코드의 어느 부분을 별도의 스레드로 실행해야하는지 잘 모르겠습니다. 또한 내 코드 구조에 근본적으로 잘못된 점이 있습니다.

이상적으로는 UI에서 텍스트 데이터를로드하는 동안 UI 바로 위에 진행 대화 상자가있는 상태에서 사용자에게 표시되도록하고 싶습니다. 그런 다음 사용자는 UI를 제어해야하며 이미지는 백그라운드의 다른 스레드에로드됩니다.

public class VodsActivity extends ListActivity { 

private LayoutInflater mInflater; 
private Vector<RowData> data; 
RowData rd; 

//private Handler mHandler; 
private ProgressDialog dialog; 


//Generic names of custom ListView elements 
private static String[] title; 
private Vector<String> detail; 
private Vector<String> status;  
private Vector<String> imgurl; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.custom_list); 
    mInflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE); 

    title = getResources().getStringArray(R.array.yt_channels); 
    detail = new Vector<String>(); 
    status = new Vector<String>(); 
    imgurl = new Vector<String>(); 

    //mHandler = new Handler(); 

    //dialog = ProgressDialog.show(VodsActivity.this, "","Loading. Please wait...", true);   

    loadData(); 
    displayData(); 

    //dialog.dismiss(); 

} 

private void loadData() {   
    String[] values = {"error", "error", "http://www.ephotobay.com/thumb/message-error.jpg" }; 

    for (int i = 0; i < title.length; i++) { 
     values = getData(title[i]); 
     values[1] = getTodaysUploads(title[i]); 
     detail.add(i, values[0]); 
     status.add(i, values[1]); 
     imgurl.add(i, values[2]); 
    } 
} 

/*** This function gets total number of uploads and thumbnail url for the user from a single feed ***/ 
private String[] getData (String username) { 
    String[] result = new String[3]; 
    String ytFeedUrl = "http://gdata.youtube.com/feeds/api/users/" + username + "?v=2"; 
    InputStream inStream = null; 

    try {   
     inStream = OpenHttpConnection(ytFeedUrl); 

     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
     DocumentBuilder db = dbf.newDocumentBuilder(); 
     Document dom = db.parse(inStream); 
     Element docEle = dom.getDocumentElement(); 

     inStream.close(); 

     NodeList nl = docEle.getElementsByTagName("entry"); 
     if (nl != null && nl.getLength() > 0) { 
      for (int i = 0; i < nl.getLength(); i++) { 
       Element entry = (Element) nl.item(i); 
       Element thumbnail = (Element) entry.getElementsByTagName("media:thumbnail").item(0); 
       String thumbUrl = thumbnail.getAttribute("url"); 
       Element feedLink = (Element) entry.getElementsByTagName("gd:feedLink").item(5); 
       String uploads = feedLink.getAttribute("countHint"); 

       result[0] = uploads + " videos"; 
       result[1] = ""; //not used here     
       result[2] = thumbUrl;       
      } 
     } 

    } catch (MalformedURLException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (ParserConfigurationException e) { 
     e.printStackTrace(); 
    } catch (SAXException e) { 
     e.printStackTrace(); 
    } 
    finally { 
     // 
    } 
    return result; 
} 

/*** This function gets a number of today's uploads of the user ***/ 
private String getTodaysUploads (String username) { 
    String result = null; 
    String ytFeedUrl = "http://gdata.youtube.com/feeds/api/videos?author=" + username + "&time=today&v=2"; 
    InputStream inStream = null; 

    try {   
     inStream = OpenHttpConnection(ytFeedUrl); 

     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
     DocumentBuilder db = dbf.newDocumentBuilder(); 
     Document dom = db.parse(inStream); 
     Element docEle = dom.getDocumentElement(); 

     inStream.close(); 

     NodeList nl = docEle.getElementsByTagName("feed"); 
     if (nl != null && nl.getLength() > 0) { 
      for (int i = 0; i < nl.getLength(); i++) { 
       Element entry = (Element) nl.item(i); 
       Element title = (Element)entry.getElementsByTagName("openSearch:totalResults").item(0);      

       result = title.getFirstChild().getNodeValue(); 
       result += " new today"; 
      } 
     } 

    } catch (MalformedURLException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } catch (ParserConfigurationException e) { 
     e.printStackTrace(); 
    } catch (SAXException e) { 
     e.printStackTrace(); 
    } 
    finally { 
     // 
    } 
    return result; 
} 

private void displayData() { 
    //Use vector instead of ArrayList for safe threading 
    data = new Vector<RowData>(); 

    for (int i = 0; i < title.length; i++) { //Loop needs to be changed based on results 
     try { 
      rd = new RowData(i, title[i], detail.get(i), status.get(i)); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     data.add(rd); 
    }   

    CustomAdapter adapter = new CustomAdapter (this, R.layout.custom_list_item, R.id.title, data); 
    setListAdapter(adapter); 
    getListView().setTextFilterEnabled(true); 
} 

private InputStream OpenHttpConnection(String strUrl) throws IOException { 
    InputStream inStream = null; 
    URL url = new URL(strUrl); 
    URLConnection conn = url.openConnection(); 

    try { 
     HttpURLConnection httpConn = (HttpURLConnection) conn; 
     httpConn.setRequestMethod("GET"); 
     httpConn.connect(); 

     if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) { 
      inStream = httpConn.getInputStream(); 
     } 
    } catch (Exception ex) { 
     ex.printStackTrace(); 
    } 
    return inStream; 
} 

//This is temporary 
public void onListItemClick(ListView parent, View v, int position, long id) { 
    CustomAdapter adapter = (CustomAdapter) parent.getAdapter(); 
    RowData row = adapter.getItem(position);     
    Builder builder = new AlertDialog.Builder(this); 
    builder.setTitle(row.mTitle); 
    builder.setMessage(row.mDetail + " -> " + position); 
    builder.setPositiveButton("ok", null); 
    builder.show(); 
} 

//Private class RowData - holds details of CustomAdapter item 
private class RowData { 
    protected int mId; 
    protected String mTitle; 
    protected String mDetail; 
    protected String mStatus; 

    RowData (int id, String title, String detail, String status) { 
     mId = id; 
     mTitle = title; 
     mDetail = detail; 
     mStatus = status; 
    } 

    @Override 
    public String toString() { 
     return mId + " " + mTitle + " " + mDetail + " " + mStatus; 
    } 
} 

//Custom Adapter for the custom list, overrides onView() method 
private class CustomAdapter extends ArrayAdapter<RowData> { 

    public CustomAdapter(Context context, int resource, int textViewResourceId, List<RowData> objects) { 
     super (context, resource, textViewResourceId, objects); 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder = null; 
     TextView title = null; 
     TextView detail = null; 
     TextView status = null; 
     ImageView image = null; 
     RowData rowData = getItem(position); 

     //Reuse existing row views 
     if(convertView == null) { 
      convertView = mInflater.inflate(R.layout.custom_list_item, null); 
      holder = new ViewHolder(convertView); 
      convertView.setTag(holder); 
     } 

     holder = (ViewHolder) convertView.getTag(); 

     title = holder.getTitle(); 
     title.setText (rowData.mTitle); 
     detail = holder.getDetail(); 
     detail.setText(rowData.mDetail); 
     status = holder.getStatus(); 
     status.setText(rowData.mStatus); 

     //add if statements here for colors 

     image = holder.getImage(); 

     /**** This loads the pictures ****/ 
     BitmapFactory.Options bmOptions; 
     bmOptions = new BitmapFactory.Options(); 
     bmOptions.inSampleSize = 1; 
     String imageUrl = imgurl.get(rowData.mId); 
     Bitmap bm = LoadImage(imageUrl, bmOptions); 
     image.setImageBitmap(bm); 

     return convertView; 
    } 

    //Load image from the URL 
    private Bitmap LoadImage(String url, BitmapFactory.Options options) { 
     Bitmap bitmap = null; 
     InputStream inStream = null; 
     try { 
      inStream = OpenHttpConnection(url); 
      bitmap = BitmapFactory.decodeStream(inStream, null, options); 
      inStream.close(); 
     } catch (IOException ioex) { 
      ioex.printStackTrace(); 
     } 
     return bitmap; 
    }      
} 

/*** Wrapper for row data ***/ 
private class ViewHolder { 
    private View mRow; 
    private TextView title = null; 
    private TextView detail = null; 
    private TextView status = null; 
    private ImageView image = null; 

    public ViewHolder (View row) { 
     mRow = row; 
    } 

    public TextView getTitle() { 
     if (title == null) { 
      title = (TextView) mRow.findViewById(R.id.title); 
     } 
     return title; 
    } 

    public TextView getDetail() { 
     if (detail == null) { 
      detail = (TextView) mRow.findViewById(R.id.detail); 
     } 
     return detail; 
    } 

    public TextView getStatus() { 
     if (status == null) { 
      status = (TextView) mRow.findViewById(R.id.status); 
     } 
     return status; 
    } 

    public ImageView getImage() { 
     if (image == null) { 
      image = (ImageView) mRow.findViewById(R.id.thumbnail); 
     } 
     return image; 
    } 
} 

}

덕분에 어떤 포인터에 대한 많은.

+0

뭐든지 시간에 필요 비 - ui 스레드에 있어야합니다. – Falmarri

답변

1
내가 백그라운드에서 API에서 데이터를로드하는 표준 자바 스레드를 사용하여 종료뿐만 아니라 별도의 스레드에서 이미지로드에 대해 별도의 클래스를 만들었

. 당신이 궁금해하는 경우에 지금은 이렇게 보입니다. 그리고 잘 작동하는 것 같습니다.

로드 데이터 (CustomAdapter)에

public void onCreate(...) { 
    //... 

    mHandler = new Handler(); 
    dialog = ProgressDialog.show(VodsActivity.this, "","Loading. Please wait...", true); 
    getData.start();   
} 

private Thread getData = new Thread() { 
    public void run() { 
     try { 
      loadData();    
      mHandler.post(showData); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 
}; 

private Runnable showData = new Runnable() { 
    public void run() { 
     try { 
      displayData(); 
      dialog.dismiss(); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
     } 
    } 
}; 

이미지로드 :

 String imageUrl = imgurl.get(rowData.mId); 
     final ImageView image = holder.getImage(); 

     //Reuse downloaded images or download new in separate thread      
     image.setTag(imageUrl); 
     Drawable cachedImage = imageLoader.loadDrawable(imageUrl, new ImageCallback() { 
      public void imageLoaded(Drawable imageDrawable, String imageUrl) { 
       ImageView imageViewByTag = (ImageView) image.findViewWithTag(imageUrl); 
       if (imageViewByTag != null) { 
        imageViewByTag.setImageDrawable(imageDrawable); 
       } 
      } 
     }); 
     image.setImageDrawable(cachedImage); 

하여 ImageLoader 클래스 : 비 결정적 량을 얻어

public class ImageLoader { 
private HashMap<String, SoftReference<Drawable>> imageCache; 
private static final String TAG = "ImageLoader"; 

public ImageLoader() { 
    imageCache = new HashMap<String, SoftReference<Drawable>>(); 
} 

//Loads image from the cache if it exists or launches new thread to download it 
public Drawable loadDrawable(final String imageUrl, final ImageCallback imageCallback) { 
    Log.d(TAG, "loadDrawable(" + imageUrl + ")"); 
    if (imageCache.containsKey(imageUrl)) { 
     SoftReference<Drawable> softReference = imageCache.get(imageUrl); 
     Drawable drawable = softReference.get(); 
     if (drawable != null) { 
      return drawable; 
     } 
    } 
    final Handler handler = new Handler() { 
     @Override 
     public void handleMessage(Message message) { 
      imageCallback.imageLoaded((Drawable) message.obj, imageUrl); 
     } 
    }; 
    new Thread() { 
     @Override 
     public void run() { 
      Drawable drawable = loadImageFromUrl(imageUrl); 
      imageCache.put(imageUrl, new SoftReference<Drawable>(drawable)); 
      Message message = handler.obtainMessage(0, drawable); 
      handler.sendMessage(message); 
     } 
    }.start(); 
    return null; 
} 

//Downloads image from the url 
public static Drawable loadImageFromUrl(String url) { 
    Log.d(TAG, "loadImageFromUrl(" + url + ")"); 
    InputStream inputStream; 
    try { 
     inputStream = new URL(url).openStream(); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
    return Drawable.createFromStream(inputStream, "src"); 
} 

public interface ImageCallback { 
    public void imageLoaded(Drawable imageDrawable, String imageUrl); 
} 
} 
+0

이 프로젝트가 성장하면 Volley 라이브러리도 좋습니다. 캐싱 및 실패한 다운로드 재 시도 등을 관리합니다. – wblaschko

2

AsyncTask을 확인하십시오. 이렇게하면 UI를 표시하면서 장기 실행 프로세스에 대한 배경 지식을 얻을 수 있습니다.

또한, 스레딩 안드로이드에 좋은/공식 자습서를 찾을 수 있습니다 here.

+0

감사합니다. 나는 기초를 읽었다. 난 그냥 내 코드에서 그들을 적용 문제가 있습니다. 문제는 별도의 스레드에서 코드의 일부를 실행하더라도 모든 데이터가 다운로드되고 진행 대화 상자가 전혀 표시되지 않을 때까지 UI가 표시되지 않는다는 것입니다. TextView에서 다운로드 한 데이터를 표시하는 간단한 클래스에서 작동하도록 만들었지 만 ListView에서도 동일한 결과를 얻을 수는 없습니다. – Marian

+0

AsyncTask가 작동하지 않으면'Service'를 시도 할 수 있습니다. http://developer.android.com/reference/android/app/Service.html – Haphazard