Bitmaps Taking To Much Memory - android

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;
}

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;
}
}
}

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.

ImageLoader
You can use Imageloader class which load your images in background.
Imageloader

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.

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.

Related

Android - Listview adapter with asynctask to load images

I'm trying to handle an image loading at the background.
Now, I've look at the next link - here
And I've got few things I don't understand -
1) I've made the next CursorAdapter for the listview items-
public class ChatCursorAdapter extends CursorAdapter implements OnClickListener {
public ChatCursorAdapter(Context context, Cursor c) {
super(context, c, 0);
}
#Override
public int getCount() {
return getCursor() == null ? 0 : super.getCount();
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int _position) {
Cursor cursor = (Cursor) getItem(_position);
return getItemViewType(cursor);
}
private int getItemViewType(Cursor cursor) {
String sender = cursor.getString(2);
SharedPreferences userPref = PreferenceManager
.getDefaultSharedPreferences(MainChat.this);
String saveUser = userPref.getString("user", "");
if (saveUser.equalsIgnoreCase(sender)){
return 0;
}else{
return 1;
}
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
holder = (ViewHolder) view.getTag();
holder.mesg.setText(getSmiledText(MainChat.this,msg));
holder.mesg2.setText(getSmiledText(MainChat.this,msg2));
holder.myImage.setTag(picPath);
holder.myImage.setImageBitmap(setImageToImageView(picPath));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
View itemLayout = null;
switch(getItemViewType(cursor)){
case 0:
itemLayout = getLayoutInflater().inflate(R.layout.msg_item1,parent, false);
break;
case 1:
itemLayout = getLayoutInflater().inflate(R.layout.msg_item13, parent,false);
break;
}
itemLayout.setTag(holder);
holder.mesg = (TextView) itemLayout.findViewById(R.id.text_start);
holder.mesg2 = (TextView) itemLayout.findViewById(R.id.text_end);
holder.myImage = (ImageView) itemLayout.findViewById(R.id.imageView_msgpic);
return itemLayout;
}
Now i wnat to use the info from the link.
But i don't understand - What i need to pass into the and what to AsyncTask leave at CursorAdapter?
Also the sample code uses -
.execute(holder);
Can't I call to the AsyncTask like this -
new AsyncTask().execute();
And how and where should i call the AsyncTask, I don't understand it?
Thanks for any kind of help
You could always use an external lib like Universal-Image-Loader or Picasso to achieve what you are trying to do =)
Take a look at AsyncTask. You must Override doInBackground method. You may define a constructor to supply view in which you want to put downloaded image.
public class ImageDownloader extends AsyncTask<String, Void, List<Bitmap>> {
private ImageView ivImageHolder;
private Context context;
public ImageDownloader(Context context, ImageView imageHolder) {
this.ivImageHolder = imageHolder;
this.context = context;
}
...
#Override
protected List<Bitmap> doInBackground(String... params) {
//This happens in background
List<Bitmap> bitmaps = new ArrayList<Bitmap>();
for (String url : params) {
Bitmap bitmap = DownloadImage(url);
bitmaps.add(bitmap);
}
return bitmaps;
}
....
private Bitmap DownloadImage(String URL) {
Bitmap bitmap = null;
InputStream in = null;
try {
in = OpenHttpConnection(URL);
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return bitmap;
}
...
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;
}
#Override
protected void onPostExecute(List<Bitmap> bitmaps) {
super.onPostExecute(bitmaps);
for (int i = 0; i < bitmaps.size(); i++) {
final Bitmap bitmap = bitmaps.get(i);
ivImageHolder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new ImageViewActivity(context, bitmap).show();
}
});
// iv.setImageBitmap(bitmap);
ivImageHolder.setImageBitmap(bitmap);
ivImageHolder.setVisibility(ImageView.VISIBLE);
}
}
if you write your asyntask method I can say how can you use it, If it need to string value
you can use like this:
new your_async(context).execute(url) ;
But in my advice : you should use lazyadapter to use bitmaps on listview because there is a mermory issue if you do not pay attention properties of images.
here is link : stackoverfow

Terminate the running download async task in android

I am working on a image downloader , which is a async downloader. I call it whenever need to display the image on the internet
Image async downloader (Input are the target imageview, image url)
public class ImageLoader extends AsyncTask<Object, Void, Bitmap> {
private static String TAG = "ImageLoader";
public InputStream input;
public ImageView view;
public String imageURL;
#Override
protected Bitmap doInBackground(Object... params) {
try {
view = (ImageView) params[0];
imageURL = (String) params[1];
URL url = new URL(imageURL);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#Override
protected void onPostExecute(Bitmap result) {
if (result != null && view != null) {
view.setImageBitmap(result);
view.setBackgroundResource(android.R.color.transparent);
view.getBackground().setAlpha(255);
}
}
}
And the structure of my app is a tabhost , when I switch to a particular tab e.g. at the section 4 , there is a gridview and it will trigger the imageloader asynctask
The fragment:
gridView.setAdapter(new GalleryAdapter(getActivity() , images));
The adapter:
public class GalleryAdapter extends BaseAdapter {
private Context mContext;
public ArrayList<GalleryImage> images;
// Constructor
public GalleryAdapter(Context c, ArrayList<GalleryImage> _images) {
mContext = c;
images = _images;
}
#Override
public int getCount() {
return images.size();
}
#Override
public Object getItem(int position) {
return images.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if( convertView == null ){
convertView = (ImageView)new ImageView(mContext);
int size = (int)(Utility.getScreenWidth(mContext) / 3) - 1;
AbsListView.LayoutParams params = new AbsListView.LayoutParams(size, size);
convertView.setLayoutParams(params);
convertView.setBackgroundResource(android.R.color.darker_gray);
convertView.getBackground().setAlpha(204); // = 0.8 alpha
}
new ImageLoader().execute(convertView,images.get(position).thumbUrl);
return convertView;
}
}
I would like to know are there any way to cancel the downloading task when I change the tab? (Since the user leave the tab before all the download is finish , so it is unnecessary to download anymore)
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
In your case, there is no loop in your doInBackground() code. In fact it would be better if you turn it into a loop by reading the response stream buffered. This would be more performant and enable you to call isCancelled() in the loop.
Example: Use ByteArrayOutputStream to read bytes in chunks and check isCancelled() periodically in the loop.
protected Bitmap doInBackground(Object... params) {
try {
URL url = new URL(imageURL);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
input = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] byteChunk = new byte[4096];
int n;
while ( (n = input.read(byteChunk)) > 0 ) {
if(isCancelled()) {
return null;
}
baos.write(byteChunk, 0, n);
}
Bitmap myBitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.size());
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Of course you should keep a reference to your AsyncTask object to be able to call cancel method on it.
private ImageLoader loader;
...
...
loader = new ImageLoader();
loader.execute();
...
...
loader.cancel()

Why my application crashes when I scroll quickly (or a lot) my GridView?

I am filling a GridView with my facebook friends' photo.
When I use my account of tester with few friends my application works good. But when I use my main account and I scroll quickly my application I get This error:
AndroidRuntime(6131): java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3#42230f20 rejected from java.util.concurrent.ThreadPoolExecutor#4206af70[Running, pool size = 128, active threads = 128, queued tasks = 10, completed tasks = 61]
otherwise
If i scroll a lot i get this error:
java.lang.RuntimeException: An error occured while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:299)
Caused by: java.lang.NullPointerException at it.alfonso.utils.GetImageFromUrlAsyncTask.downloadImage(GetImageFromUrlAsyncTask.java:62)
if (facebookAdapter == null) {
facebookAdapter = new ImageAdapterFacebook(this, facebookResponses);
gridview.setAdapter(facebookAdapter);
}
else {
gridview.setAdapter(facebookAdapter);
}
My adapeter for my GridView
public class ImageAdapterFacebook extends BaseAdapter {
private Context mContext;
private FacebookResponses facebookFrinds;
public ImageAdapterFacebook(Context c, FacebookResponses facebookFrinds) {
mContext = c;
this.facebookFrinds = facebookFrinds;
}
public int getCount() {
return facebookFrinds == null ? 0 : facebookFrinds.getData().length;
}
public Object getItem(int position) {
return facebookFrinds == null ? null
: facebookFrinds.getData()[position];
}
public long getItemId(int position) {
return position;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View amico, ViewGroup parent) {
final ImmageViewHolder viewHolder;
if (amico == null) { // if it's not recycled, initialize some attributes
LayoutInflater li = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
amico = li.inflate(R.layout.details_img_facebook_user, parent,
false);
viewHolder = new ImmageViewHolder();
viewHolder.userImage = (ImageView) amico
.findViewById(R.id.userLikesimg);
amico.setTag(viewHolder);
} else {
viewHolder = (ImmageViewHolder) amico.getTag();
}
if (facebookFrinds != null) {
viewHolder.userImage.setImageResource(R.drawable.image_loader);
String imgUserurl = facebookFrinds.getData()[position]
.getPic_square();
// Create an object for subclass of AsyncTask
GetImageFromUrlAsyncTask task = new GetImageFromUrlAsyncTask(
mContext, new DownloadImageLister() {
#Override
public void onDownloadImageSucces(Bitmap immagine) {
viewHolder.userImage.setImageBitmap(immagine);
}
#Override
public void onDownloadImageFail() {
System.out.print("errore");
}
});
task.execute(imgUserurl);
}
return amico;
}
public class ImmageViewHolder {
ImageView userImage;
}
}
My AsyncTask
public class GetImageFromUrlAsyncTask extends AsyncTask<String, Void, Bitmap> {
private Context contesto;
private DownloadImageLister listenerImage;
public GetImageFromUrlAsyncTask(Context context,
DownloadImageLister listener) {
contesto = context;
listenerImage = listener;
}
#Override
protected Bitmap doInBackground(String... urls) {
Bitmap map = null;
for (String url : urls) {
map = downloadImage(url);
}
return map;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (result != null ) {
listenerImage.onDownloadImageSucces(result);
}
if (result == null ) {
listenerImage.onDownloadImageFail();
}
}
// Creates Bitmap from InputStream and returns it
private Bitmap downloadImage(String url) {
Bitmap bitmap = null;
InputStream stream = null;
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize = 1;
try {
stream = getHttpConnection(url);
bitmap = BitmapFactory.decodeStream(stream, null, bmOptions);
stream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return bitmap;
}
// Makes HttpURLConnection and returns InputStream
private InputStream getHttpConnection(String urlString) throws IOException {
InputStream stream = null;
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
try {
HttpURLConnection httpConnection = (HttpURLConnection) connection;
httpConnection.setRequestMethod("GET");
httpConnection.connect();
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
stream = httpConnection.getInputStream();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return stream;
}
}
Since you are performing so many requests for each image load your app is crashing. You could use the Volley Android library and its NetworkImageView. This is what several Google apps are using for async image loading and http requests. There is a good tutorial explaining how to use it here: http://www.captechconsulting.com/blog/clinton-teegarden/android-volley-library-tutorial.
Hope that helps!

List View Images change position automatically when I tap?

I am working on an application in which I have a listview, which is getting populated through an ArrayAdapter. All the things are working fine except an issue which has now become quite irritating.
My list view has a custom layout infalted that contains image and then text. The problem is with image part. When I tap on the list after it is populated the images in the list swap there position. Say, for example image associated to 1st cell goes to 3rd and vice versa and so on. It happens only with Images. Text remains at its position. I don't know what the issue is. Please help me out of this severe problem.
Following is my Adapter code:
public class PListAdapter extends ArrayAdapter<Product> {
Context context;
ArrayList<Product> products;
LayoutInflater vi;
ProgressBar mSpinner;
private ImageView imageView;
public void setItems(ArrayList<Product> items) {
this.products = items;
}
public ProductListAdapter(Context context, ArrayList<Product> productList) {
super(context, 0, productList);
// TODO Auto-generated constructor stub
this.context = context;
this.products = productList;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View v = convertView;
final Product p = products.get(position);
if (p != null) {
if (v == null) {
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.sample_singlerow, null);
}
imageView = (ImageView) v.findViewById(R.id.product_image);
TextView title = (TextView) v.findViewById(R.id.product_title);
TextView summary = (TextView) v.findViewById(R.id.product_summary);
TextView price = (TextView) v.findViewById(R.id.product_price);
TextView type = (TextView) v.findViewById(R.id.product_type);
ImageView pImage = (ImageView) v.findViewById(R.id.persons);
if (imageView != null) {
if (p.getImage() == null) {
if (p.getImageURL() != null) {
new AsyncImageLoader(imageView, p.getImageURL());
}
}
}
if (title != null) {
Log.i("Title: ", p.getName());
title.setText(p.getName());
}
if (summary != null) {
Log.i("Summary: ", p.getDescription());
summary.setText(p.getDescription().substring(0, 110) + "...");
}
if (price != null) {
Log.i("Price: ", p.getPrice());
price.setText(p.getPrice());
}
if (type != null) {
Log.i("Type: ", p.getType());
type.setText(p.getType() + " Person");
}
if (pImage != null) {
try {
if (p.getType().equals("1")) {
pImage.setImageResource(R.drawable.one_person);
} else if (p.getType().equals("2")) {
pImage.setImageResource(R.drawable.two_person);
}
} catch (NotFoundException e) {
// TODO Auto-generated catch block
pImage.setImageDrawable(null);
e.printStackTrace();
}
}
}
return v;
}
Edit:
public class AsyncImageLoader {
private final WeakReference imageViewReference;
public AsyncImageLoader(ImageView imageView,String imageUrl) {
imageViewReference = new WeakReference<ImageView>(imageView);
String[] url={imageUrl};
new BitmapDownloaderTask().execute(url);
}
// static int counter = 0;
// int imageNum = 0;
/**
* This Interface in used by {#link AsyncImageLoader} to return a response
* by after loading image
*/
public interface ImageCallback {
public Drawable temp = null;
/**
* Load the Image in imageDrable, Image is loaded form imageUrl
*
* #param imageDrawable
* Image in drawable format
* #param imageUrl
* URL of image to be load
*/
public void imageLoaded(Drawable imageDrawable, String imageUrl);
}
private String LOG_TAG;
class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null && !(bitmap==null)) {
imageView.setImageBitmap(bitmap);
}
}
}
}
Bitmap downloadBitmap(String url) {
final int IO_BUFFER_SIZE = 4 * 1024;
// AndroidHttpClient is not allowed to be used from the main thread
final HttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode +
" while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
// return BitmapFactory.decodeStream(inputStream);
// Bug on slow connections, fixed in future release.
return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (IOException e) {
getRequest.abort();
Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
} catch (IllegalStateException e) {
getRequest.abort();
Log.w(LOG_TAG, "Incorrect URL: " + url);
} catch (Exception e) {
getRequest.abort();
Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
} finally {
if ((client instanceof AndroidHttpClient)) {
((AndroidHttpClient) client).close();
}
}
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 b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
}
Please let me know what is the issue. Anxiously waiting for your response.
As I can see, you have some troubles with Views reusage in Adapter and asynchronous loading.
To optimize performance and memory, instead of inflating views repeatedly, Android tries to "cache" list items in ListView's recycler - that means that the same View (list item) will be used several times, each time for different data item.
If so - let's check what will happen if you'll scroll the existing list: some view will be infated, filled with data and will start to download image for ImageView. In separate thread.
And now what if this View will be reused for another data item, before previous Image was successfully loaded? Currently, a new AsyncTask will be started for this image. And here comes a racing condition, noone knows in which order the result will be returned from the AsyncTask.
So I would recommend you either to store AsyncTask with a View (in Tag, for example), or to make some hashmap.
The main purpose - to determine when bitmap loading is complete - was this image used for another bitmap request or not, if it was - skip image setting, as it's outdated.
This approach will prevent imageViews from displaying wrong image for another item.
Hope it helps, good luck
you need to add bydefault image if the image url is null, Just add else check and add any place holder

Android - GridView's onResume

I am developing Android apps,
once part of this apps is creating a GridView that contain some image (the image is loaded from URL),
then another activity that show the selected image in full screen will be appeared when one of image in GridView is onClick.
Problem:
When I enter the GridView activity, it takes some second to load all image in gridview normally.
Then I click one of image to enter the full screen activity and click back button to go back to GridView,
but, it takes some second to loading when go back to gridview, just like loading all image again.
I wonder why the gridview activity will loading for a few second when onResume?
For example, in Google Play, the full screen view of sample image in any apps can be back to previous view immediately.
Enclosed code:
GridView:
public class ManagePhoto extends Activity {
ImageAdapter ia;
GridView gridview;
InputStream inputStream;
private static final int SELECT_PICTURE = 1;
private String selectedImagePath;
TextView tvRunningMark;
boolean bRunning;
String[] purl;
Bitmap[] bm;
String the_string_response;
TouchImageView touch;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.photo_manage);
//gv is Gobal Value
final Gvalue gv = ((Gvalue) getApplicationContext());
gridview = (GridView) findViewById(R.id.gv_photo);
gridview.setOnItemClickListener(new GridView.OnItemClickListener() {
public void onItemClick(AdapterView adapterView, View view,int position, long id) {
gv.setbm(bm[position]);
Intent myIntent = new Intent(adapterView.getContext(), FullScreenImage.class);
startActivityForResult(myIntent, 0);
}
});
new GridTask().execute();
}
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return purl.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some
// attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(200,200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
try {
bm[position] = loadBitmap(purl[position]);
imageView.setImageBitmap(bm[position]);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return imageView;
}
}
class GridTask extends AsyncTask<Void, String, Void> {
#Override
protected void onPostExecute(Void result) {
gridview.setAdapter(ia);
final LinearLayout llo_probar = (LinearLayout)findViewById(R.id.llo_probar);
llo_probar.setVisibility(LinearLayout.GONE);
gridview.setVisibility(GridView.VISIBLE);
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(String... values) {
}
#Override
protected Void doInBackground(Void... params) {
getphoto();
bm = new Bitmap[purl.length];
ia = new ImageAdapter(ManagePhoto.this);
return null;
}
}
private Bitmap loadBitmap(String url) throws MalformedURLException,IOException {
return BitmapFactory.decodeStream(new FlushedInputStream(
(InputStream) new URL(url).getContent()));
}
class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(final InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(final long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int bytesRead = read();
if (bytesRead < 0) { // we reached EOF
break;
}
bytesSkipped = 1;
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
public void getphoto(){
final Gvalue gv = ((Gvalue) getApplicationContext());
final TextView tv_fn = (TextView) findViewById(R.id.tv_fn);
String result = "";
ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("sql",
("select * from personal_photo where member_id = " + gv.getuid())));
InputStream is = null;
// http post
try {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://" + gv.getserverIP()
+ "/android_getdata.php");
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
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());
}
// parse json data
try {
List url = new ArrayList();
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
url.add(json_data.getString("save_location"));
}
int size = url.size();
purl = new String[size];
for (int j = 0; j < size; j++) {
purl[j] = (String) url.get(j);
}
} catch (JSONException e) {
Log.e("log_tag", "Error parsing data " + e.toString());
}
}
public void toast(String text) {
Toast.makeText(ManagePhoto.this, text, 5).show();
}
}
Full screen:
public class FullScreenImage extends Activity {
TouchImageView touch;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
new ShowPhotoTask().execute();
}
class ShowPhotoTask extends AsyncTask<Void, String, Void> {
#Override
protected void onPostExecute(Void result) {
setContentView(touch);
}
#Override
protected void onPreExecute() {
setContentView(R.layout.full_image);
}
#Override
protected void onProgressUpdate(String... values) {
}
#Override
protected Void doInBackground(Void... params) {
final Gvalue gv = ((Gvalue) getApplicationContext());
touch = new TouchImageView(FullScreenImage.this);
touch.setMaxZoom(4f); // change the max level of zoom, default is 3f
touch.setImageBitmap(gv.getbm());
return null;
}
}
}
From your getView code in your ImageAdapter, everytime you will load the image from the internet again.
You should download the image to local, next time, when your set the image, you try to get it from local firstly.
As well you should put the get bitmap in the thread as you have putting the parse JSON in the thread.
Here is an demo, i think it will help you.
In your gridview onclick you are starting activity with startActivityForResults(intent,0); replace it with startActivity(intent);
also when you are finishing the FullScreenActivity just use finish();
might solve your problem
It's so slow because you don't perform your operations with background threads, caching..
A simple and better solution could be a collection that contains all your bitmaps inserted by AsyncTasks that you'll execute to download the pictures.
There are better solutions but they are more difficult to implement.
For example you can consider the possibility to keep a thread pool that resolves your runnables represented by "download the http://jhon.doe.jpg" and then "show now on the UI thread".
As you have written below function inside the getView() method:
bm[position] = loadBitmap(purl[position]);
I would say you should implement code to load Image Asynchronously. In this logic, image is synced in your memory card once its downloading is done. So next time it will load directly from memory card instead of loading it from web again.
Here is a code example you can give a try: Android - Universal Image Loader

Categories

Resources