What is the right coding about Image Loading? - android

I'm solving my problem about Image Loader and I have some problems..
What I want is to show many images (about 400) in GridView(or ListView).
I don't want to use the Library like Picasso, Glide like that.
and Here is the problem.
When I call the method which convert from url to bitmap?
3.1. before setAdapter, then pass the bitmap array.
3.2. while getView.
two things are working well. but too much slow... maybe cuz of the times to call URLConnection..
Could anyone help me about these problem? How can I speed up? or are there any other solution without Open Source.
Here is my Source.
Now, 3-1.
ShowImage
private void showImages(ArrayList<String> imgUrls) {
ArrayList<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < imgUrls.size(); i++) {
try {
String img_path = imgUrls.get(i);
Bitmap bitmap = new UriToBitmapAsyncTask().execute(img_path).get();
bitmaps.add(bitmap);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
CustomAdapter adapter = new CustomAdapter(getApplicationContext(),R.layout.row,bitmaps);
gridView.setAdapter(adapter);
}
and This is the customAdapter's GetView
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflator.inflate(rowLayout, parent, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setImageBitmap(bitmaps.get(position));
return convertView;
}

You should really take Reinventing the wheel to heart but if you really want to toture yourself an Approach could be:
use a ThreadPoolExecutor to fetch more images at once, you should read up how to use them
implement a way to cancel threads who load a img for a griditem which isn't displayed anymore
use two sets of data a thumbnail which loads faster for the grid view and a real image which gets loaded when the user clicks on the grid
dont't forget to use a LRU caching method or your device will run out of memory depending on the images

Don't use ArrayList to store bitmaps. Bitmaps usually take consumes a lot of memory. Try using LRUCache like this way,
public class TCImageLoader implements ComponentCallbacks2 {
private TCLruCache cache;
public TCImageLoader(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
int maxKb = am.getMemoryClass() * 1024;
int limitKb = maxKb / 8; // 1/8th of total ram
cache = new TCLruCache(limitKb);
}
public void display(String url, ImageView imageview, int defaultresource) {
imageview.setImageResource(defaultresource);
Bitmap image = cache.get(url);
if (image != null) {
imageview.setImageBitmap(image);
}
else {
new SetImageTask(imageview).execute(url);
}
}
private class TCLruCache extends LruCache<String, Bitmap> {
public TCLruCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(ImagePoolKey key, Bitmap value) {
int kbOfBitmap = value.getByteCount() / 1024;
return kbOfBitmap;
}
}
private class SetImageTask extends AsyncTask<String, Void, Integer> {
private ImageView imageview;
private Bitmap bmp;
public SetImageTask(ImageView imageview) {
this.imageview = imageview;
}
#Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
bmp = getBitmapFromURL(url);
if (bmp != null) {
cache.put(url, bmp);
}
else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
return 1;
}
#Override
protected void onPostExecute(Integer result) {
if (result == 1) {
imageview.setImageBitmap(bmp);
}
super.onPostExecute(result);
}
private Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection
= (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(int level) {
if (level >= TRIM_MEMORY_MODERATE) {
cache.evictAll();
}
else if (level >= TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.size() / 2);
}
}
}
get a instance of TCImageLoader and call display method appropriately.

Related

Lazy Loading of image in listview by its position

i am trying lazy loading with LruCache as shown here : Example using Androids lrucache.
public class TCImageLoader implements ComponentCallbacks2 {
private TCLruCache cache;
Context cv;
public TCImageLoader(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
int maxKb = am.getMemoryClass() * 1024;
int limitKb = maxKb / 8; // 1/8th of total ram
cache = new TCLruCache(limitKb);
cv = context;
}
public void display(String url, ImageView imageview, int defaultresource) {
imageview.setImageResource(defaultresource);
Bitmap image = cache.get(url);
if (image != null) {
imageview.setImageBitmap(image);
}
else {
new SetImageTask(imageview).execute(url);
}
}
private class TCLruCache extends LruCache<String, Bitmap> {
public TCLruCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(String key, Bitmap value) {
int kbOfBitmap = value.getByteCount() / 1024;
return kbOfBitmap;
}
}
private class SetImageTask extends AsyncTask<String, Void, Integer> {
private ImageView imageview;
private Bitmap bmp;
public SetImageTask(ImageView imageview) {
this.imageview = imageview;
}
#Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
//bmp = BitmapFactory.decodeResource(cv.getResources(), url);
bmp = getBitmapFromURL(url);
if (bmp != null) {
cache.put(url, bmp);
}
else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
return 1;
}
#Override
protected void onPostExecute(Integer result) {
if (result == 1) {
imageview.setImageBitmap(bmp);
}
//super.onPostExecute(result);
}
private Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection
= (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(int level) {
/* if (level >= TRIM_MEMORY_MODERATE) {
cache.evictAll();
}
else if (level >= TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.size() / 2);
}*/
}
}
the TCimageloader is as shown in above code. my getview is as shown below :
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(10,10,10,10);
} else {
imageView = (ImageView) convertView;
}
imagloader = new TCImageLoader(mContext);
imagloader.display(mThumbIds[position], imageView, R.drawable.ic_launcher);
//Picasso.with(mContext).load(mThumbIds[position]).placeholder(R.drawable.ic_launcher).into(imageView);
imageView.setImageResource(R.drawable.ic_launcher);
return imageView;
}
but the problem i am facing is that when I scroll fast the getView has already been called and display method is already called. so the image views keep changing until the last called is implemented.
i am trying to make a custom gallery and this will also help me in downloading images from urls. I will have to use same method . will just have to change the file address to url address

Drawables won't load using AsyncTask

I have the queryAppIcon() method that queries and stores images in the array appIconDrawable. However, I'm only getting blank where images should pop up. Please let me know if I should post any other relevant code
This is the relevant code inside the ViewActivity:
// global vars
final Drawable[] appIconDrawable = null;
int i;
public Drawable[] queryAppIcon() throws ParseException, IOException {
ParseQuery<ParseObject> query = ParseQuery.getQuery("AndroidStoreContent");
query.whereExists("appIcon");
List<ParseObject> ParseResult = query.find();
// Drawable array
appIconDrawable = new Drawable[ParseResult.size()];
for (i = 0; i < ParseResult.size(); i++) {
ParseFile pf = (ParseFile) ParseResult.get(i).get("appIcon");
startDownload(pf);
}
return appIconDrawable;
}
public void startDownload(ParseFile pf) {
new DownloadImageTask(this).execute(pf);
}
public class DownloadImageTask extends AsyncTask<ParseFile, Void, Drawable> {
private AsyncResponse ar;
DownloadImageTask(AsyncResponse ar) {
this.ar = ar;
}
#Override
protected Drawable doInBackground(ParseFile... pf) {
return fetchDrawable(pf[0]);
}
protected void onPostExecute(Drawable result) {
ar.processFinish(result);
}
public Drawable fetchDrawable(ParseFile pf) {
InputStream is;
try {
is = (InputStream) new URL(pf.getUrl()).getContent();
return Drawable.createFromStream(is,null);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void processFinish(Drawable d) {
appIconDrawable[i] = d; // i also tried testing appIconDrawable[1] = d and the app loaded with all blank images and then crashes
}
This is the interface, AsyncResponse:
public interface AsyncResponse {
void processFinish(Drawable d);
}
It seems like you need a bit of refactor...
You are expecting Drawable[] from queryAppIcon() but you will always get an empty set because you start the download which takes place in a separate thread to then update the return value.
You should be setting the Drawable to the ImageView from within processFinish.
Signs of things done wrong: Async methods such as downloading images should never have a return value.
UPDATE
Here is a very simple download AsyncTask but there are many checks, optimizations, etc. missing, like CACHE! Also, ImageView inside DownloadImageTask should be held by a WeakReference (Google it) otherwise it WILL leak your activity.
public class DownloadImageTask extends AsyncTask<String, Void, Drawable> {
private static final String TAG = DownloadImageTask.class.getSimpleName();
private ImageView mImageView;
DownloadImageTask(ImageView imageView) {
mImageView = imageView;
}
#Override
protected Drawable doInBackground(String... url) {
return fetchDrawable(url[0]);
}
#Override
protected void onPostExecute(Drawable result) {
if (result != null) {
mImageView.setImageDrawable(result);
} else {
Log.w(TAG, "Could download image!");
}
}
public static Drawable fetchDrawable(String url) {
Log.v(TAG, "Downloading: " + url);
InputStream is;
try {
is = (InputStream) new URL(url).getContent();
return Drawable.createFromStream(is, null);
} catch (MalformedURLException e) {
Log.e(TAG, e.getMessage(), e);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
return null;
}
}
Adapter:
public class ImageDownloadAdapter extends ArrayAdapter<String>{
public ImageDownloadAdapter(Context context, String[] objects) {
super(context, R.layout.item_image_download, R.id.txt_url, objects);
}
#SuppressLint("NewApi")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
String url = getItem(position);
ImageView imageView = (ImageView) view.findViewById(R.id.img_download);
DownloadImageTask downloadImageTask = new DownloadImageTask(imageView);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
downloadImageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
} else {
downloadImageTask.execute(url);
}
return view;
}
}
Activity:
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(new ImageDownloadAdapter(this, new String[]{
"http://www.beautystat.com/site/wp-content/uploads/2011/02/happy-faces-small.jpg",
"http://www.ducthide.com/new_wallet_pics/happy_face.JPG"
}));

How to cancel an Async task in gallery View in android using position index?

i have an galleyview which loads images from server album. but my album has many images [more than 500]. so once i scroll the galleyview more fast the number of background task is getting high so the app is getting crashed.so i am planning to kill [cancel] some old tasks based on the position in the galleryview. so please suggest some solution.The source code is provided below.
Task invoking:
DownloadImageTask downloadTask = new DownloadImageTask(
ShowGallery.this, view,position);
// cancel some task to avoid the crash - need to implement
// cancelPotentialDownload(position);
downloadTask.execute( THUMB_PREFIX + picture.getFileName(),
picture.getForceExtension(), thumbUrl,albumName, bitmapsCache, position, picture,null);
private static boolean cancelPotentialDownload(int position) {
// need to implement.
}
Downloadimage task
public class DownloadImageTask extends AsyncTask<Object, Void, Bitmap> {
Context activity;
private ImageView view;
public int position;
public DownloadImageTask(Context context, ImageView imageView, int imagePosition) {
super();
activity = context;
view = imageView;
position = imagePosition;
}
#Override
protected Bitmap doInBackground(Object... parameters) {
String fileName = (String) parameters[0];
String extension = (String) parameters[1];
String thumbUrl = (String) parameters[2];
Integer currentAlbumName = (Integer) parameters[3];
Map<Integer, Bitmap> bitmapsCache = (Map<Integer, Bitmap>) parameters[4];
Integer position = (Integer) parameters[5];
Picture picture = (Picture) parameters[6];
Album album = (Album) parameters[7];
Bitmap downloadImage = null;
File imageFileOnExternalDirectory = null;
try {
imageFileOnExternalDirectory = FileUtils.getInstance()
.getFileFromGallery(activity, fileName, extension,
thumbUrl, true, currentAlbumName);
downloadImage = BitmapFactory
.decodeFile(imageFileOnExternalDirectory.getPath());
if (picture != null) {
// only for showgallery activity
picture.setThumbImageCachePath(imageFileOnExternalDirectory
.getPath());
bitmapsCache.put(position, downloadImage);
} else if (album != null) {
// only for albumadapter
album.setAlbumCoverCachePath(imageFileOnExternalDirectory
.getPath());
}
} catch (GalleryConnectionException e) {
// Log.v(TAG, e.getMessage());
} catch (FileHandlingException e) {
// Log.v(TAG, e.getMessage());
}
return downloadImage;
}
#Override
protected void onPostExecute(Bitmap downloadImage) {
if (downloadImage != null) {
view.setImageBitmap(downloadImage);
}
}
}
Look at the example on this link. You are not downloading images from the web, so just replace this functionality with reading the image from gallery

Gridview performance with HttpResponseCache

I'm using a gridview to display hundreds of images (perhaps even a few thousand). The images are located on a server and I'm caching the images using HttpResponseCache. The problem I'm having is that when I swipe down through the gridview, the recycled views are showing 3 or more images, per child view, before finally settling on the correct image. It seems to be a result of the callback methods returning all the requested images. How can I get a gridview to not have this giant swoosh of activity when scrolling up/down.
getView method of my custom adapter
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = li.inflate(R.layout.folder_button, null);
} else {
v = convertView;
}
TextView tv = (TextView)v.findViewById(R.id.tvFolderButtonTitle);
tv.setText(mBaseItems[position].Name);
tv.setTextColor(Color.WHITE);
ImageView iv = (ImageView)v.findViewById(R.id.ivFolderButtonImage);
iv.setLayoutParams(new LinearLayout.LayoutParams(folderWidth_, folderHeight_));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
String imageUrl = "http://path.to.image";
api_.GetImageAsync(imageUrl, new GetImageStreamCallback(iv), false);
return v;
}
callback method that sets the image.
public class GetImageStreamCallback implements IApiCallback {
private ImageView currentImageView;
public GetImageStreamCallback(ImageView imageView) {
currentImageView = imageView;
}
public void Execute(Object data) {
if (data != null) {
try {
Bitmap image = (Bitmap) data;
currentImageView.setImageBitmap(image);
} catch (Exception e) {
Log.i("Exception", "Error getting image");
}
}
}
}
custom AsyncTask called from api_.GetImageAsync above
public class AsyncRequestImage extends AsyncTask<String,String,Object > {
HttpURLConnection connection_;
InputStream inStream_;
IApiCallback callback_;
boolean ignoreCache_;
public AsyncRequestImage(IApiCallback callback, boolean ignoreCache) {
this.callback_ = callback;
this.ignoreCache_ = ignoreCache;
}
#Override
protected Object doInBackground(String... uri) {
Bitmap image;
if (ignoreCache_) {
image = acquireImage(uri[0], true);
} else {
image = acquireImage(uri[0], false);
if (image == null)
image = acquireImage(uri[0], true);
}
return image;
}
#Override
protected void onPostExecute(Object image) {
callback_.Execute(image);
}
private Bitmap acquireImage(String url, boolean ignoreCache) {
try {
URL _url = new URL(url);
connection_ = (HttpURLConnection) _url.openConnection();
connection_.addRequestProperty("Accept-Encoding", "gzip");
if (ignoreCache) {
connection_.setRequestProperty("Cache-Control", "max-age=0");
} else {
connection_.addRequestProperty("Cache-Control", "only-if-cached");
}
connection_.connect();
String encoding = connection_.getContentEncoding();
// Determine if the stream is compressed and uncompress it if needed.
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
try {
inStream_ = new GZIPInputStream(connection_.getInputStream());
} catch (FileNotFoundException e) {
}
} else {
try {
inStream_ = connection_.getInputStream();
} catch (FileNotFoundException e) {
}
}
if (inStream_ != null) {
try {
Bitmap image = BitmapFactory.decodeStream(inStream_);
return image;
} catch (java.lang.OutOfMemoryError oom) {
FileLogger.getFileLogger().ReportInfo("UrlConnection: Bitmap creation failed. Out of memory");
}
}
} catch (IOException e) {
if (e != null && e.getMessage() != null) {
Log.i("AsyncRequestImage doInBackground:",e.getMessage());
}
} finally {
connection_.disconnect();
}
return null;
}
}
Part of the issue I was having was due to an unoptimized BaseAdapter.GetView
Also when the user initiated a fling gesture, I was still trying to load all the images as the views passed by.
This article! Provided a detailed description and solution for each of the mistakes I was making. Also in that article is a link to source code that provides a method to stop loading images until the fling gesture has finished.

asynchronus image loading in gridview

I'm loading images in gridviev asynchronusly.But my gridview displaying only a single image in the last cell of gridview.My adapter class and asynchronus class is given below, thanks.
Adapter class:
class OrderAdapter extends ArrayAdapter<String>
{
LayoutInflater inflater;
String name3[];
public OrderAdapter(Context context,int resource,LayoutInflater inflater,String name2[])
{
super(context, resource,R.id.img,name2);
this.inflater=inflater;
this.name3=name2;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View row=inflater.inflate(R.layout.row,parent,false);
final ImageView img=(ImageView)row.findViewById(R.id.img);
String imgurl=name3[position];
Log.e("urlchandan",name3[position]);
AsyncImageLoaderv asyncImageLoaderv=new AsyncImageLoaderv();
Bitmap cachedImage = asyncImageLoaderv.loadDrawable(imgurl, new AsyncImageLoaderv.ImageCallback()
{
public void imageLoaded(Bitmap imageDrawable, String imageUrl) {
img.setImageBitmap(imageDrawable);
}
});
img.setImageBitmap(cachedImage);
return row;
}
}
Asynchronous class
public class AsyncImageLoaderv {
private HashMap<String, SoftReference<Bitmap>> imageCache;
public AsyncImageLoaderv() {
imageCache = new HashMap<String, SoftReference<Bitmap>>();
}
public Bitmap loadDrawable(final String imageUrl, final ImageCallback imageCallback) {
if (imageCache.containsKey(imageUrl)) {
SoftReference<Bitmap> softReference = imageCache.get(imageUrl);
Bitmap drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Bitmap) message.obj, imageUrl);
}
};
new Thread() {
#Override
public void run() {
try{
Log.d("ur",imageUrl);
Bitmap drawable = loadImageFromUrl(imageUrl);
imageCache.put(imageUrl, new SoftReference<Bitmap>(drawable));
Message message = handler.obtainMessage(0, drawable);
handler.sendMessage(message);
}catch(Exception e){Log.e("thread stellent",e.toString());}
}
}.start();
return null;
}
public static Bitmap loadImageFromUrl(String url) {
InputStream inputStream;Bitmap b;
try {
inputStream = (InputStream) new URL(url).getContent();
BitmapFactory.Options bpo= new BitmapFactory.Options();
bpo.inSampleSize=2;
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );
return b;
} catch (IOException e) {
throw new RuntimeException(e);
}
//return null;
}
public interface ImageCallback {
public void imageLoaded(Bitmap imageBitmap, String imageUrl);
}
}
You can't do it the way you're trying. You need to have your asynchronous loader store the resulting image in some data structure your adapter can access by position (e.g. a list, a hashmap, whatever). Your getView() should then simply pull the image from the correct position. Your asynchronous loader will populate the data structure and perform a notifyDataSetChanged() to have the list redraw itself with the newly loaded image.
I got the solution by making the ImageView img in adatper inflater as final because it avoids
the images to display at a single cell in gridview . And my images was of big size and got the error decoder return false and this error is solved by taking another class
--
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class PatchInputStream extends FilterInputStream {
public PatchInputStream(InputStream in) {
super(in);
}
public long skip(long n) throws IOException {
long m = 0L;
while (m < n) {
long _m = in.skip(n-m);
if (_m == 0L) break;
m += _m;
}
return m;
}
}
this class is used in AsyncImageLoaderv given above .
b=BitmapFactory.decodeStream(new PatchInputStream(inputStream), null,bpo );

Categories

Resources