Android:自定义ListView和线程问题

时间:2011-05-03 20:57:34

标签: android multithreading listview youtube-api

我正在开发Android上的一个小项目,并且在我的解决方案中实现一些多线程存在严重问题。下面是一个类,它是主界面选项卡中的一个活动,它显示一个自定义列表,其中包含从YouTube API下载的图片和数据。

该类工作正常,但它完全阻止了UI,首先是数据,然后是从Internet加载的图像。我知道我需要实现一些线程,我已经尝试了各种各样的东西,但我不太确定我必须将哪些部分代码作为单独的线程启动。我的代码结构也有可能出现根本性的错误。

理想情况下,我希望在应用程序启动后立即向用户显示用户界面上的进度对话框,同时从YouTube加载文本数据。然后,用户应该控制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;
    }
}   

}

非常感谢任何指针。

2 个答案:

答案 0 :(得分:2)

查看AsyncTask。这样您就可以在显示UI时显示长时间运行的进程。

另外,您可以在Android线程here.

上找到好的/官方教程

答案 1 :(得分:1)

我最终使用标准的Java Thread在后台加载来自API的数据,并创建了一个单独的类,用于在不同的线程中加载图像。万一你想知道它现在看起来像这样,似乎工作正常。

加载数据:

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();
        }
    }
};

加载图片(在CustomAdapter中):

        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);
}
}