Android-设置异步线程从internet下载图像阵列时遇到问题
我已经读了几十篇关于这方面的帖子,并尝试了同样多的解决方案,但我似乎什么都做不到。我通过JSON响应填充listview,该响应包含许多(有时超过100)行。每一行都有一个关联(和不同)的图像 在性能测试中,当我没有下载/显示图像时,JSON响应中的134行在不到2秒钟的时间内被处理和显示。令人惊叹的!然而,当我重新打开图像下载/显示时,花了大约10年的时间 很明显,我需要使用后台线程来下载图像,但我在网上找到的每个解决方案都不成功(假设我在这一点上出现了一些错误) 我想我有一个想法,我需要在哪里处理背景中的图像,但我不确定如何设置它 现在,图像(以及所有其他数据)被加载到一个数组中。每个图像现在都可以在线下载,因此出现了令人难以置信的性能噩梦 下面是我的主要活动类的一些代码摘录Android-设置异步线程从internet下载图像阵列时遇到问题,android,image,background,Android,Image,Background,我已经读了几十篇关于这方面的帖子,并尝试了同样多的解决方案,但我似乎什么都做不到。我通过JSON响应填充listview,该响应包含许多(有时超过100)行。每一行都有一个关联(和不同)的图像 在性能测试中,当我没有下载/显示图像时,JSON响应中的134行在不到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;
}
}
我真的,真的希望有人能帮我解决这个问题,因为我已经筋疲力尽,几乎没有咖啡了……:) 我认为最好是将第二个片段中的图像下载放到一个线程中 有关线程的更多详细信息,请参见,但基本上您将创建一个类似“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();
该代码未经测试,但应该可以工作:)我认为最好是将第二个代码段中的图像下载放到一个线程中 有关线程的更多详细信息,请参见,但基本上您将创建一个类似“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();
该代码未经测试,但应该可以工作:)这是需要加载图像的ListView的经典问题。接受的模式是“延迟加载”映像,这本质上意味着在getView(在适配器中)中启动一个加载映像的异步任务。视图将返回,UI线程将继续,因此不会出现性能问题。稍后,AsyncTask完成,并将下载的映像添加到先前返回的视图中 谷歌搜索“lazy-load-listview”将返回大量结果 这也是一个更好的解决方案,因为不会同时加载所有图像,当列表太大时,可能会导致内存不足问题 通过对图像进行缓存,可以实现额外的性能提升,这样,如果图像在缓存中,就不需要重新加载图像。这可能有些过分,但imagename=>SoftReference(位图)的哈希映射可以提供此功能。如果缓存中存在密钥,并且SoftReference仍然有效,则使用那里的位图;否则,使用异步任务重新加载映像(并将其保存到缓存…)
最后,所有这些都有一个有趣的问题。如您所见,getView()有时会循环使用视图——也就是说,当某个特定列表项从显示中滚动出来时,它有时会被重用(以避免重新创建新视图的成本)。因此,可能正在运行一个异步任务,该任务引用了当前正在用于其他新对象的视图。延迟加载模式通常会在视图上设置标记,如果异步任务返回时该标记保持不变,则会继续并将图像添加到视图中。否则,意味着不再需要图像。这是需要加载图像的列表视图的典型问题。接受的模式是“延迟加载”映像,这本质上意味着在getView(在适配器中)中启动一个加载映像的异步任务。视图将返回,UI线程将继续,因此不会出现性能问题。稍后,AsyncTask完成,并将下载的映像添加到先前返回的视图中 谷歌搜索“lazy-load-listview”将返回大量结果 这也是一个更好的解决方案,因为不会同时加载所有图像,当列表太大时,可能会导致内存不足问题 通过对图像进行缓存,可以实现额外的性能提升,这样,如果图像在缓存中,就不需要重新加载图像。这可能有些过分,但imagename=>SoftReference(位图)的哈希映射可以提供此功能。如果缓存中存在密钥,并且SoftReference仍然有效,则使用那里的位图;否则,使用异步任务重新加载映像(并将其保存到缓存…)
最后,所有这些都有一个有趣的问题。如您所见,getView()有时会循环使用视图——也就是说,当某个特定列表项从显示中滚动出来时,它有时会被重用(以避免重新创建新视图的成本)。因此,可能正在运行一个异步任务,该任务引用了当前正在用于其他新对象的视图。延迟加载模式通常会在视图上设置标记,如果异步任务返回时该标记保持不变,则会继续并将图像添加到视图中。否则,这意味着图像不再需要。非常感谢Todd和Elijah提供的信息。我最终使用的是nostra13提供的UniversalImageLoader库 我的代码实现是在上面第一个代码片段的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提供的奇妙图书馆 非常感谢托德和以利亚提供的信息。我最终使用的是nostra13提供的UniversalImageLoader库 我的代码实现是在上面第一个代码片段的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);
}
});
}
它工作得很好!非常感谢诺斯特拉13