我正在研究Android上的一个小项目,并且在我的解决方案中实现一些多线程时遇到了严重的问题。下面是一个类,它是主界面选项卡中的一个活动,它显示一个自定义列表,其中包含从YouTube API下载的图片和数据。Android:自定义列表视图和线程问题
该类可以正常工作,但是当首先从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;
}
}
}
非常感谢任何指针。
凡是需要非确定性的时间量需要在非ui线程上。 – Falmarri 2011-05-03 21:06:35