Reputation: 388
I'm getting an error saying that the Bitmaps are using to much memory.
I know that I should use bitmap.recyle() but I don't know where to put it, wherever I put it I get an error saying that I'm trying to use a recycled bitmap.
If anyone can help that would be great.
Here is my relevant code:
public class PictureViewer extends SherlockActivity implements
android.view.GestureDetector.OnGestureListener {
private ViewFlipper viewFlipper = null;
private GestureDetector gestureDetector = null;
ArrayList<Integer> number = new ArrayList<Integer>();
DownloadBitmap bit = new DownloadBitmap();
int j = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Remove title bar
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.pictureviewer);
viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper);
gestureDetector = new GestureDetector(this);
for (int i = 1; i <= 65; ++i)
number.add(i);
Collections.shuffle(number);
loadImage();
loadImage();
}
public void loadImage() {
if (j == 65) { // Change this number to exact ammount of pictures
j = 1;
}
int next = number.get(j);
j++;
ImageView image = new ImageView(this);
Bitmap bitmap = bit.createBitmapFromUrl("http://comedyzone.mobi/img" + next + ".jpg");
WeakReference<Bitmap> mBitmapReference = new WeakReference<Bitmap>(bitmap);
image.setImageBitmap(mBitmapReference.get());
image.setScaleType(ImageView.ScaleType.FIT_XY);
viewFlipper.addView(image, new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
}
@Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
if (arg0.getX() - arg1.getX() > 120) {
this.viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_in));
this.viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_left_out));
this.viewFlipper.showNext();
loadImage();
return true;
} else if (arg0.getX() - arg1.getX() < -120) {
this.viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_right_in));
this.viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.push_right_out));
this.viewFlipper.showPrevious();
loadImage();
return true;
}
return true;
}
@Override
public void onLongPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
float arg3) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public boolean onSingleTapUp(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return this.gestureDetector.onTouchEvent(event);
}
private InputStream OpenHttpConnection(String urlString) throws IOException {
InputStream in = null;
int response = -1;
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
if (!(conn instanceof HttpURLConnection))
throw new IOException("Not an HTTP connection");
try {
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
response = httpConn.getResponseCode();
if (response == HttpURLConnection.HTTP_OK) {
in = httpConn.getInputStream();
}
} catch (Exception ex) {
throw new IOException("Error connecting");
}
return in;
}
Upvotes: 1
Views: 3836
Reputation: 784
Generally speaking you should mark an object for clean up when it's no longer needed. In your situation the only time I see you won't be needing these bitmaps any longer is when the activity is no longer at the front.
What I do see is the potential for unnecessarily large bitmaps being used with your ViewFlipper. You should resize the bitmap before setting them inside of an ImageView rather than resizing the ImageView after adding them. Try using inSampleSize as an option with BitmapFactory and resize the bitmap with Bitmap.createScaledBitmap() before loading it into a ImageView.
Upvotes: 0
Reputation: 30814
I know that I should use bitmap.recyle()
Calling recycle
is not necessary.
Last year at Google IO, there was a talk given precisely on this topic.
Google I/O 2011: Memory management for Android Apps - You should definitely watch all of this, it's worth the time.
Making a WeakReference
to your Bitmap
object is a good start towards better Bitmap
management. For example:
Bitmap bitmap = DownloadImage("http://comedyzone.mobi/img" + next + ".jpg");
WeakReference<Bitmap> mBitmapReference = new WeakReference<Bitmap>(bitmap);
image.setImageBitmap(mBitmapReference.get());
Displaying Bitmaps Efficiently
These are Android training classes you should read through too.
Also, this is a class I wrote to download an image from a URL. You should consider using it in place of your DownloadImage
method, it's much more efficient.
DownloadBitmap
public class DownloadBitmap {
private static String LOG_TAG = DownloadBitmap.class.getName();
/**
* @param url
* @return Bitmap image from the interwebs
*/
static Bitmap createBitmapFromUrl(String url) {
final Bitmap mBitmap = readBitmapFromNetwork(url);
final WeakReference<Bitmap> mBitmapReference = new WeakReference<Bitmap>(mBitmap);
if (mBitmapReference.get() != null)
return mBitmapReference.get();
return null;
}
/**
* @param urlString The URL to read the bitmap from.
* @return A Bitmap image or null if an error occurs.
*/
private static Bitmap readBitmapFromNetwork(String urlString) {
InputStream mInputStream = null;
FlushedInputStream mFlushedInputStream = null;
Bitmap mBitmap = null;
WeakReference<Bitmap> mBitmapReference = null;
try {
final BitmapFactory.Options mOptions = new BitmapFactory.Options();
mOptions.inPurgeable = true;
mOptions.inDither = false;
final URL mUrl = new URL(urlString);
final URLConnection mConnection = mUrl.openConnection();
mConnection.connect();
mInputStream = mConnection.getInputStream();
mFlushedInputStream = new FlushedInputStream(mInputStream);
mBitmap = BitmapFactory.decodeStream(mFlushedInputStream, null, mOptions);
mBitmapReference = new WeakReference<Bitmap>(mBitmap);
} catch (MalformedURLException e) {
if (BuildConfig.DEBUG)
Log.e(LOG_TAG, "Bad image URL", e);
return null;
} catch (IOException e) {
if (BuildConfig.DEBUG)
Log.e(LOG_TAG, "Could not get remote image", e);
return null;
} finally {
try {
if (mInputStream != null)
mInputStream.close();
if (mFlushedInputStream != null)
mFlushedInputStream.close();
} catch (IOException e) {
if (BuildConfig.DEBUG)
Log.e(LOG_TAG, "Error closing stream.");
return null;
}
}
if (mBitmapReference.get() != null)
return mBitmapReference.get();
return null;
}
/**
* An InputStream that skips the exact number of bytes provided, unless it
* reaches EOF.
*/
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int bytes = read();
if (bytes < 0) {
break;
} else {
bytesSkipped = 1;
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
}
Upvotes: 6
Reputation: 1975
You need to reduce the sample size of the bitmap before using it. This can be used to do so.
private Bitmap decodeFile(File file)
{
try
{
//********************* decode image size ********************
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file), null, options);
// ********************** Find the correct scale value. It should be the power of 2. ********************
options.inSampleSize = BitmapConverter.calculateInSampleSize(options, 145, 105);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(new FileInputStream(file), null, options);
}
catch(FileNotFoundException eFileNotFoundException)
{
return null;
}
}
And then you can call Bitmap.recycle() if required, although I feel that it wouldn't be necessary.
Upvotes: 1
Reputation: 7087
You can write bitmap.recycle()
just after this statement image.setImageBitmap(bitmap);
which is in loadImage()
function, since bitmap is no longer required after this statement.
This error occurs because either you have used a bitmap which is of very large size, or you have a memory leak in your code.
Whenever you are using a bitmap, always try to use all possible Options to use as minimum memory as possible.
For more info please see my answer on same issue.
Upvotes: 0
Reputation: 9507
ImageLoader
You can use Imageloader class which load your images in background.
Imageloader
Upvotes: 0