将图像从 url 加载到视图页 - 内存不足



viewpager 的每个视图都有一个图像视图,其中包含从 url 获取的位图。

如果我加载小图像 - 100 X 80 px - 即使我加载 10000 个,我也不会内存不足。 如果我加载更大的图像 800 X 60 像素 - 我在 28 -30 张图像后内存不足。

我看到视图寻呼机回收视图中的图像,这些图像已经被滑动了。(当我快速向后滑动时,我看到图像再次加载。

我无法弄清楚的是 - 为什么 10000 张小图像不会使应用程序崩溃,但只有 30 张大图像可以崩溃?

请看下面的代码:

<PRE>
public class MainActivity extends FragmentActivity implements OnClickListener {
final String appurl = "http://drafts.bestsiteeditor.com/cgi-bin/bookcalendar/promoters.pl";
final String imgurl = "http://drafts.bestsiteeditor.com/promoters/";
ArrayList<Event> events = new ArrayList<Event>();
ViewPager mPager;
GetServerData mt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.set(Calendar.HOUR_OF_DAY, 0);cal.set(Calendar.MINUTE, 0);cal.set  (Calendar.SECOND, 0);cal.set(Calendar.MILLISECOND, 0);
int monday = (int) (cal.getTimeInMillis() / 1000);
if (cal.get(Calendar.DAY_OF_WEEK) == 2) {} else {for (int d = 1; d <= 7; d++) {monday = monday - 86400;cal.setTimeInMillis((long) monday * 1000);if (cal.get(Calendar.DAY_OF_WEEK) == 2) {break;}}}
makeWeek(monday);
mPager = (ViewPager) findViewById(R.id.pager);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return ActionBar.HandleMenu(this, item.getItemId());
}
@Override
public void onClick(View v) {
// if (v == show_calendar) {
// Intent openMenu;
// openMenu = new Intent(this, WeekCalendar.class);
// startActivity(openMenu);
// }
}
public class CustomPagerAdapter extends PagerAdapter {
ArrayList<Event> events;
LayoutInflater inflater;
Context c;
public CustomPagerAdapter(Context context, ArrayList<Event> events) {
this.inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.events = events;
this.c = context;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((RelativeLayout) object);
}
@Override
public int getCount() {
return events.size();
}
@Override
public void destroyItem(View container, int position, Object object) {
// ((ViewPager) container).removeView((View)object);
System.out.println("DESTROY destroying view at position "
+ position);
View view = (View) object;
((ViewPager) container).removeView(view);
view = null;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView;
itemView = inflater.inflate(R.layout.first_frag, container, false);
Event e = events.get(position);
TextView topTextItem = (TextView) itemView.findViewById(R.id.tvFragFirst);
TextView bottomTextItem = (TextView) itemView.findViewById(R.id.tv2);
ImageView iv = (ImageView) itemView.findViewById(R.id.imageView1);
e.setImageView(iv);
//if (position == 0) {
ShowImage shim = new ShowImage(imgurl + "th" + e.getId()+ "1.jpg", iv,c);
shim.execute();
//}
Button btn = (Button) itemView.findViewById(R.id.button1);
final String showtoast = String.valueOf(events.size());
btn.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getBaseContext(),
"Event expired before:" + showtoast,
Toast.LENGTH_LONG).show();
}
});

topTextItem.setText(e.getDsc());
bottomTextItem.setText(String.valueOf(position) + e.getTitle());
((ViewPager) container).addView(itemView);
return itemView;
}
}

public class Event {
String id;
String title;
String description;
ImageView iv;
public Event(String id, String ttl, String dsc) {
this.id = id;
this.title = ttl;
this.description = dsc;
}
public void setImageView(ImageView niv) {
this.iv = niv;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDsc() {
return description;
}
public ImageView getIV() {
return iv;
}
}
public class Pair {
public String isonline;
public ArrayList<Event> events;
}
private class GetServerData extends AsyncTask<Void, Void, Pair> {
Context context;
String targetUrl;
String imgUrl;
public GetServerData(Context context, String url, String imgurl) {
this.context = context;
this.targetUrl = url;
this.imgUrl = imgurl;
}
@Override
protected Pair doInBackground(Void... params) {

ArrayList<Event> eventsar = new ArrayList<Event>();
String isonline = "no";
Event newevent = null;
Document doc;
try {
doc = Jsoup.connect(targetUrl).get();
isonline = doc.select("div#isonline").text();
Elements promoters = doc.select("div.promoters");
Elements events = doc.select("div.events");
Elements eventsfull = doc.select("div.eventsfull");
if (eventsfull.size() > 0) {
for (Element event : eventsfull) {
String temp = event.text().toString();
String title = event.select("div.title").text();
String event_id = event.select("div.event_id").text();
String promoter_id = event.select("div.promoter_id")
.text();
String promoter_name = event.select("div.promoter_name").text();
String promoter_email = event.select("div.promoter_email").text();
String promoter_phone = event.select("div.promoter_phone").text();
String promoter_dsc = event.select("div.promoter_dsc").text();
Integer imgs = Integer.parseInt(event.select("div.event_images").text());
String[] eventSplit = temp.split("\|");
newevent = new Event(event_id, title, promoter_dsc);
eventsar.add(newevent);
}
}
} catch (IOException e) {
e.printStackTrace();
}
Pair p = new Pair();
p.isonline = isonline;
p.events = eventsar;
return p;
}
@Override
// protected void onPostExecute(ArrayList<Integer> rows) {
protected void onPostExecute(Pair p) {
String isonline = p.isonline;
events = p.events;
if (isOnline()) {
if (isonline.equals("yes")) {
Calendar clt = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Long nowt = clt.getTimeInMillis();
CustomPagerAdapter customPagerAdapter = new CustomPagerAdapter(context, events);mPager.setAdapter(customPagerAdapter);
// mPager.setOffscreenPageLimit(4);
} else {
Toast.makeText(getApplicationContext(),
"No Internet Connection with this page.",Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(getApplicationContext(),"No Internet Connection at all.", Toast.LENGTH_LONG).show();
}
}
}
public boolean isOnline() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
}
return false;
}
public void makeWeek(Integer start_day) {
try {
Random rand = new Random();
int myRandom = rand.nextInt() % 3;
mt = new GetServerData(MainActivity.this, appurl+ "?action=getevents&weekmonday=" + start_day + "&rand="+ myRandom, imgurl);
mt.execute();
} catch (Exception e) {
}
}
private class ShowImage extends AsyncTask<Void, Void, Bitmap> {
ImageView imgV;
String imgsrc;
Bitmap d;
Context c;

public ShowImage(String src, final ImageView v,Context cntx) {
this.imgV = v;
this.imgsrc = src;
this.c=cntx;
}
@Override
protected Bitmap doInBackground(Void... params) {
//InputStream is = null;
//try {
// is = (InputStream) new URL(imgsrc).getContent();
//URL url = new URL(imgsrc);
//d = BitmapFactory.decodeStream(url.openConnection()
//.getInputStream());
//} catch (MalformedURLException e) {
//e.printStackTrace();
//} catch (IOException e) {
//e.printStackTrace();
//}
InputStream in = null;
try{
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(new HttpGet(imgsrc));
in = response.getEntity().getContent();
} catch(Exception e){
e.printStackTrace();
}
try {

d = BitmapFactory.decodeStream(in);
} finally {
if (in != null) { try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}


return d;
}
@Override
protected void onPostExecute(Bitmap dr) {
imgV.setImageBitmap(dr);
if (dr != null) {
dr=null;
} 


}
}

}

我认为您应该缩小图像,否则它们的处理需要大量内存,从而导致 OOM。对于较大的图像,很难找到较长的可用地址空间,对于较小的图像则更容易,您可以在以下SO中找到示例解决方案:

使用AsyncTask和大图像时的内存不足

我绝对不是ViewPager内部机制的专家,但让我们暂时假设它确实正确地回收了附加到您为每个片段创建的ImageView实例Bitmap对象。在这种情况下,我最好的猜测是,当您加载较小的图像时,回收机制会在您达到内存上限之前发挥其魔力,因此您永远不会遇到 OOME;但是,当您加载更大的图像时,回收机制无法阻止 OOME,因为您请求的内存块消耗可用空间的速度要快得多。

撇开这个不令人满意的猜测不谈,在Android中加载图像是一项可怕的任务,我会尝试委托给库或一些现成的代码解决方案,而不是自己忍受。如果你想了解更多关于这个任务的信息,官方培训文档中有一整节专门介绍它,标题为"有效地显示位图",我会阅读,不时再读一遍,只是为了避免忘记这些东西有多复杂。附加到文档的代码涉及更多,因此也很好读。

然后,有几个库适用于图像加载的一些用例:您可以检查毕加索、凌空和 Android 通用图像加载器。我相信他们都负责下载图像,将Bitmap设置为ImageView,正确调整大小和回收,尤其是缓存,在内存和磁盘上。我个人只使用过毕加索,发现它足以满足我手头的任务。

我认为在destroyItem中您正在分离ImageViewEvent对象仍然保留对它的引用,因此不能对其进行垃圾回收。你真的需要这个参考吗?(每次都创建一个新ImageView)。

顺便说一下,缩小图像并使用缓存可能是个好主意。相当多的图书馆可以为您做到这一点。

你应该参考安卓 训练 高效显示位图 在那里,他们告诉您如何加载图像并且没有 内存不足异常 .

希望有帮助

最新更新