viewpager的每个视图都有一个图像视图,其中一个Bitmap取自url。
如果我加载小图像 - 100 X 80 px - 即使我加载10000,我也永远不会忘记内存。 如果我加载更大的图像800 X 60 px - 我在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;
}
}
}
}
答案 0 :(得分:1)
我认为你应该缩小你的图像,否则他们的处理需要大量的内存来导致OOM。对于更大的图像,更难找到长的自由地址空间,对于较小的图像更容易,你可以在这个SO中找到样本解决方案:
答案 1 :(得分:0)
我认为在destroyItem
中您正在分离ImageView
,但Event
对象仍然引用它,因此无法进行垃圾回收。你真的需要这个参考吗? (每次都创建一个新的ImageView
。
顺便说一下,缩小图像和使用缓存可能是个好主意。相当多的图书馆可以为您做到这一点。
答案 2 :(得分:0)
我肯定不是ViewPager
内部机制的专家,但我们暂时假设它确实正确地回收Bitmap
附加到ImageView
的对象您为每个片段创建的实例。在这种情况下,我最好的猜测是,当您加载较小的图像时,回收机制会在您触及内存上限之前发挥其魔力,因此您永远不会体验到OOME;但是,当您加载更大的图像时,回收机制无法阻止OOME,因为您请求的内存块消耗的可用空间要快得多。
除了令人不满意的猜测之外,在Android中加载图像是一项可怕的任务,我会尝试委托给库或一些现成的代码解决方案,而不是自己忍受。如果您想要了解有关该任务的更多信息,请参阅专门的官方培训文档中的一部分,标题为Displaying Bitmaps Efficiently,我会阅读,并不时再次阅读,以避免忘记复杂程度可以成为这个东西。附加到文档的代码甚至更复杂,所以它也是一个很好的阅读。
然后,有几个库可以用于图像加载的一些用例:你可以查看Picasso,Volley和Android Universal Image Loader。我相信他们都会负责下载图片,将Bitmap
设置为ImageView
,正确调整大小和回收,特别是在内存和磁盘上进行缓存。我个人只使用了毕加索,并且发现它足以满足我手头的任务。
答案 3 :(得分:0)
你应该参考android培训Displaying Bitmaps Efficiently 在那里,他们告诉你如何加载图像,没有内存不足的例外。
希望有所帮助