Reputation: 1906
i've problem with my app:
java.lang.OutOfMemoryError: Failed to allocate a 1136368 byte allocation with 127432 free bytes
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
at android.content.res.Resources.loadDrawable(Resources.java:2110)
at android.content.res.Resources.getDrawable(Resources.java:700)
at android.widget.ImageView.resolveUri(ImageView.java:636)
at android.widget.ImageView.setImageResource(ImageView.java:366)
at com.scoutapp.lite.ImageLoader.DisplayImage(ImageLoader.java:40)
at com.scoutapp.lite.CustomArrayAdapter.getView(CustomArrayAdapter.java:49)
at android.widget.AbsListView.obtainView(AbsListView.java:2242)
at android.widget.ListView.makeAndAddView(ListView.java:1790)
at android.widget.ListView.fillDown(ListView.java:691)
at android.widget.ListView.fillGap(ListView.java:655)
at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5148)
at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4259)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:543)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5019)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
basically i have an activity where there is a list with some image. They load correctly but when i scroll down i have a force close with this error. Here i post my two class:
public class CustomArrayAdapter extends ArrayAdapter<String> {
private final Context context;
private final String[] values;
private final String[] img;
public ImageLoader imageLoader;
String t;
public CustomArrayAdapter(Context context, String[] values,String[]img) {
super(context, R.layout.activity_spec, values);
this.context = context;
this.values = values;
this.img=img;
imageLoader=new ImageLoader(getContext().getApplicationContext());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_row, parent, false);
TextView textView = (TextView) rowView.findViewById(R.id.title);
ImageView imageView = (ImageView) rowView.findViewById(R.id.list_image);
textView.setText(values[position]);
// Change icon based on name
String s = values[position];
// System.out.println(t);
//Log.d(t,img[position]);
if(img==null){
}else{
int resourceId = context.getResources().getIdentifier(img[position],"drawable", context.getPackageName());
// int drawableId = Integer.parseInt(img[position]);
Bitmap bm;
// bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath),100, 100, true);
//mPicture = new ImageView(context);
//mPicture.setImageBitmap(bm);
imageLoader.DisplayImage(resourceId, imageView);
//imageView.setImageResource(resourceId);
//img.recycle();
//img = null;
}
/* if (s.equals("Lupetti")) {
imageView.setImageResource(R.drawable.lupetti1);
} else if (s.equals("Reparto")) {
imageView.setImageResource(R.drawable.reparto1);
} else if (s.equals("Clan")) {
imageView.setImageResource(R.drawable.clan1);
}
*/
return rowView;
}
}
and:
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.ic_stub;
public void DisplayImage(int url, ImageView imageView)
{
imageViews.put(imageView, "url");
//Bitmap bitmap=memoryCache.get(url);
if(url!=0)
imageView.setImageResource(url);
else
{
queuePhoto("url", imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
File f=fileCache.getFile(url);
//from SD cache
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try {
Bitmap bitmap=null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex){
ex.printStackTrace();
return null;
}
}
//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
try {
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i){
url=u;
imageView=i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
@Override
public void run() {
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
Any idea how can i resolve this? Thanks!
Upvotes: 2
Views: 9490
Reputation: 1062
There can be many problems with bitmaps in android but first of all your adapter implementation is really bad, you don't re-use list rows and I'm sure it's pretty laggy and this might be source of your problems.
What you need to do first is to read about convertView pattern, use it and then tell us if the problem still occurs.
UPDATE:
First of wall you don't need to get LayoutInflater every time getView()
is called, you can set it in adapters constructor and then reuse it.
Next listView has a mechanism to create only number of rows which are visible on the screen, when one row gets of the screen then it's reused. What you need to do is to check if you have to create a new row from scratch or reuse existing one and fill it with new data.
public class CustomArrayAdapter extends ArrayAdapter<String> {
// Variable declarations
LayoutInflater inflater;
public CustomArrayAdapter(Context context, String[] values,String[]img) {
// your code
this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// convertView is null when new row is needed
View rowView = convertView;
if(rowView == null) {
// new row is needed to inflate new row
rowView = inflater.inflate(R.layout.list_row, parent, false);
}
TextView textView = (TextView) rowView.findViewById(R.id.title);
ImageView imageView = (ImageView) rowView.findViewById(R.id.list_image);
textView.setText(values[position]);
// rest of your code
return rowView;
}
}
Of course you can do some further optimalization using Holder patter.
Try to implement all of this and then tell us if you still have problem with OutOfMemory error
Upvotes: 3
Reputation: 632
I think you should read some at Androids Developer page, specially here: http://developer.android.com/training/displaying-bitmaps/index.html
Read all 5 lessons and rewrite your code again. If it still don't work we will be happy to see what you've done wrong with the tutorial material.
Upvotes: 2