Initially I get list of data from server and set it to listview.
When scrolling down the listview, I am getting collection of data from server and calling notifydatasetchanged of my custom adapter.
At getView() method of custom adapter, I am downloading an image from server by asyntask. When it is downloaded successfully and storing it locally. Then just trying to refresh list view at onPostExecute of that asyntask. But its not getting refresh.
The log at onPostExecute is printing but listview is not getting refresh.
public void loadBitmap(MainActivity mainActivity, String imageKey,ImageView imageView, boolean isScrolling)
{
final Bitmap bitmap = getBitmapFromCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
if (!isScrolling && !mCurrentTasks.contains(imageKey)
&& mainActivity.internetIsAvailable()) {
BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
mainActivity.getAdapter());
task.execute();
}
}
}
private class BitmapLoaderTask extends AsyncTask<Void, Void, Bitmap> {
private ListAdapter mListAdapter;
private String mImageKey;
public BitmapLoaderTask(String imageKey, ListAdapter adapter) {
mListAdapter = adapter;
mImageKey = imageKey;
}
#Override
protected void onPreExecute() {
mCurrentTasks.add(mImageKey);
}
#Override
protected Bitmap doInBackground(Void... params) {
Bitmap b = null;
try {
URL url = new URL(mImageKey);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.connect();
b = BitmapFactory.decodeStream(connection.getInputStream());
if (b != null) {
int width = b.getWidth();
int height = b.getHeight();
if (width >= mMaxWidth || height >= mMaxHeight) {
while (true) {
if (width <= mMaxWidth || height <= mMaxHeight) {
break;
}
width /= 2;
height /= 2;
}
b = Bitmap.createScaledBitmap(b, width, height, false);
}
connection.disconnect();
addBitmapToCache(mImageKey, b);
return b;
}
return null;
} catch (IOException e) {
if (e != null) {
e.printStackTrace();
}
return null;
}
}
#Override
protected void onPostExecute(Bitmap param) {
mCurrentTasks.remove(mImageKey);
if (param != null) {
mListAdapter.notifyDataSetChanged();
}
}
}
your code looks right.. but i think what may be the issue is mainActivity.getAdapter() i think you should declare adapter globally like.
private AdapterYourCustomAdapter adapter;
and then in onCreate()
adapter = new AdapterYourCustomAdapter(context,arraylist(whatever constructor is));
and then pass that call it like:
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
if (!isScrolling && !mCurrentTasks.contains(imageKey)
&& mainActivity.internetIsAvailable()) {
new BitmapLoaderTask( imageKey, adapter ).execute();
}
}
Try passing the ImageView for each bitmap into the BitmapLoaderTask at construction and hold onto that ImageView as a member variable inside. When you finish loading your bitmap, use the onPostExecute method to assign that bitmap as the ImageView drawable.
private class BitmapLoaderTask extends AsyncTask<Void, Void, Bitmap> {
private String mImageKey;
private ImageView mImageView;
public BitmapLoaderTask(ImageView imageView, String imageKey) {
mImageView = imageView;
mImageKey = imageKey;
}
protected void onPreExecute() {
/* Pre execute stuff */
}
protected Bitmap doInBackground(Void... params) {
/* Your bitmap processing */
return bitmap;
}
protected void onPostExecute(Bitmap param) {
if(param != null) {
mImageView.setImageBitmap(param);
}
}
}
This is a perfect example of what AsyncTask was built to do. The doInBackground() method is run on a background thread and thus does not interfere with UI processing, but android permissions dictate that such background threads are not allowed to touch UI elements. That's what the onProgressUpdate() and onPostExecute() are for, they run quick UI updates on the main UI thread whenever doInBackground has reached an update-worthy milestone. In this case, you're using them to notify your ImageView objects when their corresponding bitmaps are ready.
Related
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.
I'm downloading multiple images through AsyncTask. It is working well when there are 2-3 images; but, when there are more images, many AsyncTask instances are created, and are causing errors. Do anyone have an idea how to overcome this problem?
Can you add your code here?
Maybe you can try to use onPostExecute function of your Asynctask to start a new one like:
protected void onPostExecute() {
doInBackground();
}
or you can trigger an activity function by your onPostExecute() function to start download " next picture " so it will only create one asynctask for one time.
From Android Doc: Common view components such as ListView and GridView
introduce another issue when used in conjunction with the AsyncTask as
demonstrated in the previous section. In order to be efficient with
memory, these components recycle child views as the user scrolls. If
each child view triggers an AsyncTask, there is no guarantee that when
it completes, the associated view has not already been recycled for
use in another child view. Furthermore, there is no guarantee that the
order in which asynchronous tasks are started is the order that they
complete.
Handle Concurrency
http://android-developers.blogspot.in/2010/07/multithreading-for-performance.html
public class ImageDownloader {
private static final String LOG_TAG = "ImageDownloader";
public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT }
private Mode mode = Mode.NO_ASYNC_TASK;
/**
* Download the specified image from the Internet and binds it to the provided ImageView. The
* binding is immediate if the image is found in the cache and will be done asynchronously
* otherwise. A null bitmap will be associated to the ImageView if an error occurs.
*
* #param url The URL of the image to download.
* #param imageView The ImageView to bind the downloaded image to.
*/
public void download(String url, ImageView imageView) {
resetPurgeTimer();
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null) {
forceDownload(url, imageView);
} else {
cancelPotentialDownload(url, imageView);
imageView.setImageBitmap(bitmap);
}
}
/*
* Same as download but the image is always downloaded and the cache is not used.
* Kept private at the moment as its interest is not clear.
private void forceDownload(String url, ImageView view) {
forceDownload(url, view, null);
}
*/
/**
* Same as download but the image is always downloaded and the cache is not used.
* Kept private at the moment as its interest is not clear.
*/
private void forceDownload(String url, ImageView imageView) {
// State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.
if (url == null) {
imageView.setImageDrawable(null);
return;
}
if (cancelPotentialDownload(url, imageView)) {
switch (mode) {
case NO_ASYNC_TASK:
Bitmap bitmap = downloadBitmap(url);
addBitmapToCache(url, bitmap);
imageView.setImageBitmap(bitmap);
break;
case NO_DOWNLOADED_DRAWABLE:
imageView.setMinimumHeight(156);
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
task.execute(url);
break;
case CORRECT:
task = new BitmapDownloaderTask(imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
imageView.setMinimumHeight(156);
task.execute(url);
break;
}
}
}
/**
* Returns true if the current download has been canceled or if there was no download in
* progress on this image view.
* Returns false if the download in progress deals with the same url. The download is not
* stopped in that case.
*/
private static boolean cancelPotentialDownload(String url, ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
bitmapDownloaderTask.cancel(true);
} else {
// The same URL is already being downloaded.
return false;
}
}
return true;
}
/**
* #param imageView Any imageView
* #return Retrieve the currently active download task (if any) associated with this imageView.
* null if there is no such task.
*/
private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
return downloadedDrawable.getBitmapDownloaderTask();
}
}
return null;
}
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 = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() :
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;
}
}
/**
* The actual AsyncTask that will asynchronously download the image.
*/
class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
/**
* Actual download method.
*/
#Override
protected Bitmap doInBackground(String... params) {
url = params[0];
return downloadBitmap(url);
}
/**
* Once the image is downloaded, associates it to the imageView
*/
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
addBitmapToCache(url, bitmap);
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
// Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode)
if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {
imageView.setImageBitmap(bitmap);
}
}
}
}
/**
* A fake Drawable that will be attached to the imageView while the download is in progress.
*
* <p>Contains a reference to the actual download task, so that a download task can be stopped
* if a new binding is required, and makes sure that only the last started download process can
* bind its result, independently of the download finish order.</p>
*/
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.BLACK);
bitmapDownloaderTaskReference =
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
public void setMode(Mode mode) {
this.mode = mode;
clearCache();
}
/*
* Cache-related fields and methods.
*
* We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
* Garbage Collector.
*/
private static final int HARD_CACHE_CAPACITY = 10;
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
// Hard cache, with a fixed maximum capacity and a life duration
private final HashMap<String, Bitmap> sHardBitmapCache =
new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
#Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
if (size() > HARD_CACHE_CAPACITY) {
// Entries push-out of hard reference cache are transferred to soft reference cache
sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
return true;
} else
return false;
}
};
// Soft cache for bitmaps kicked out of hard cache
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
private final Handler purgeHandler = new Handler();
private final Runnable purger = new Runnable() {
public void run() {
clearCache();
}
};
/**
* Adds this bitmap to the cache.
* #param bitmap The newly downloaded bitmap.
*/
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (sHardBitmapCache) {
sHardBitmapCache.put(url, bitmap);
}
}
}
/**
* #param url The URL of the image that will be retrieved from the cache.
* #return The cached bitmap or null if it was not found.
*/
private Bitmap getBitmapFromCache(String url) {
// First try the hard reference cache
synchronized (sHardBitmapCache) {
final Bitmap bitmap = sHardBitmapCache.get(url);
if (bitmap != null) {
// Bitmap found in hard cache
// Move element to first position, so that it is removed last
sHardBitmapCache.remove(url);
sHardBitmapCache.put(url, bitmap);
return bitmap;
}
}
// Then try the soft reference cache
SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
if (bitmapReference != null) {
final Bitmap bitmap = bitmapReference.get();
if (bitmap != null) {
// Bitmap found in soft cache
return bitmap;
} else {
// Soft reference has been Garbage Collected
sSoftBitmapCache.remove(url);
}
}
return null;
}
/**
* Clears the image cache used internally to improve performance. Note that for memory
* efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
*/
public void clearCache() {
sHardBitmapCache.clear();
sSoftBitmapCache.clear();
}
/**
* Allow a new delay before the automatic cache clear is done.
*/
private void resetPurgeTimer() {
purgeHandler.removeCallbacks(purger);
purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
}
}
you can use Vinci android Library its testet and support Concurrency and its build on top
Builder Pattern
Singleton Pattern
Callback Pattern
and its handle downloading multi Images by her self and its easy to use .
if you wanna get a result of every download request :
Vinci
.base(context)
.process()
.load(uri,
new Request() {
#Override
public void onSuccess(Bitmap bitmap) {
viewHolder.Writer.setImageBitmap(bitmap);
//or
//do some thing with bitmap
}
#Override
public void onFailure(Throwable e) {
Log.e(e.getClass().getSimpleName(), e.getMessage());
}
});
if you wanna show image in ImageView you can use it like this
Vinci
.base(context)
.process()
.load(uri)
.view(imageView);
for use it in RecycleView see this wiki page
Simply use the Picasso library, to load the images into the ImageViews, as this is very simple to use and also supports concurrency very easily.
Example:
Picasso.with(this.context).load(url_of_image).into(imageView);
I created a custom TextView to show and images on it. I have to download the images and then show them on the textview. So I thought that I can do my job with a AsyncTask class. On the doInBackground I download and save the image on the Internal storage (for offline mode), and on the onPostExecute I call a method which shows the images on the textview. But it doesn't work. I mean instead of showing the images it shows the img tags.
I also tried to get the status from the AsyncTask class on the onPostExecute method and it's RUNNING. Isn't that weird? I thought that it will be FINISHED. Am I missing something about AsyncTask class?
Below is addImages method, where I find the img tags from the text and store the data on a List, and call the DownLoadImage class which extends the AsyncTask.
private boolean add(final Context context, final Spannable spannable) {
path = context.getFilesDir();
Pattern refImgPattern = Pattern.compile("<img .+?\\/>");
hasChanges = false;
refImgMatcher = refImgPattern.matcher(spannable);
while (refImgMatcher.find()) {
set = true;
for (ImageSpan span : spannable.getSpans(refImgMatcher.start(), refImgMatcher.end(), ImageSpan.class)) {
if (spannable.getSpanStart(span) >= refImgMatcher.start()
&& spannable.getSpanEnd(span) <= refImgMatcher.end()
) {
spannable.removeSpan(span);
} else {
set = false;
break;
}
}
String imageUrl = spannable.subSequence(refImgMatcher.start(0), refImgMatcher.end(0)).toString().trim();
width = 0;
Pattern widthPattern = Pattern.compile("width=\"[0-9]+?\"");
Matcher widthMatcher = widthPattern.matcher(imageUrl);
if (widthMatcher.find()) {
String w = widthMatcher.group(0);
w = w.replaceAll("width=", "");
w = w.replaceAll("\"", "");
width = Integer.valueOf(w);
}
height = 0;
Pattern heightPattern = Pattern.compile("height=\"[0-9]+?\"");
Matcher heightMatcher = heightPattern.matcher(imageUrl);
if (heightMatcher.find()) {
String h = heightMatcher.group(0);
h = h.replaceAll("height=", "");
h = h.replaceAll("\"", "");
height = Integer.valueOf(h);
}
Pattern urlPattern = Pattern.compile("(http|ftp|https):\\/\\/([\\w_-]+(?:(?:\\.[\\w_ -]+)+))([\\w.,#?^=%&:\\/~+#-]*[\\w#?^=%&\\/~+#-])?");
Matcher urlMatcher = urlPattern.matcher(imageUrl);
if (urlMatcher.find())
imageUrl = urlMatcher.group(0);
imageName = siteData.getId() + "_" + imageUrl.substring(imageUrl.lastIndexOf("/") + 1, imageUrl.length());
images.add(new Image(imageUrl, imageName, width, height, refImgMatcher.start(0), refImgMatcher.end(0)));
}
if (images.size() > 0) {
for (final Image img : images) {
image = new File(path, img.name);
if (!image.exists()) {
new DownLoadImage(context, spannable, img).execute();
} else
addImages(spannable, context, img);
}
}
return hasChanges;
}
This is the addImages method where I replace the tags with images
private void addImages(Spannable spannable, Context context, Image im) {
image = new File(path, im.name);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeFile(image.getAbsolutePath(), bmOptions);
if (im.width > 0 && im.height > 0)
bitmap = Bitmap.createScaledBitmap(bitmap, im.width * 3, im.height * 3, true);
if (set) {
hasChanges = true;
spannable.setSpan(new ImageSpan(context, bitmap),
im.startIndex,
im.endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
and the DownLoadImage class
private class DownLoadImage extends AsyncTask<Void, Void, Void> {
private Connection connection = Connection.getInstance();
private Context context;
private Spannable spannable;
private Image image;
public DownLoadImage(Context context, Spannable spannable, Image image) {
this.spannable = spannable;
this.context = context;
this.image = image;
}
#Override
protected Void doInBackground(Void... params) {
try {
connection.openConnection(image.path, ConnectionType.GET, false, false, null);
Integer status = connection.getResponseCode();
if (status >= 200 && status < 300) {
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Actions.saveImage(context, bitmap, image.name);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.closeConnection();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Log.i("status", this.getStatus().toString());
addImages(spannable, context, image);
}
}
***** EDIT *****
the getTextWithImages where I call the add method
private Spannable getTextWithImages(Context context, CharSequence text) {
images = new ArrayList<>();
Spannable spannable = spannableFactory.newSpannable(text);
add(context, spannable);
return spannable;
}
and the setText method where I call the getTextWithImages
#Override
public void setText(CharSequence text, BufferType type) {
Spannable s = getTextWithImages(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
You could create an interface that invokes a callback to the UI thread, instead of using the context. For example, in your AsyncTask:
private class DownLoadImage extends AsyncTask<Void, Void, Void> {
private Connection connection = Connection.getInstance();
private Context context;
private Spannable spannable;
private Image image;
private OnImageDownloadedListener mOnImageDownloadedListener;
...
#Override
protected Void doInBackground(Void... params) {
...
...
}
// Interface the task will use to communicate with your activity method.
public interface OnImageDownloadedListener {
void onImageDownloaded(Spannable spannable, Image image); // No need for context.
}
#Override
protected void onPostExecute(Void aVoid) {
if (mOnImageDownloadedListener != null) {
// If we set a listener, invoke it.
mOnImageDownloadedListener.onImageDownloaded(spannable, image);
}
}
// Setter.
public setOnImageDownloadedListener(OnImageDownloadedListener listener) {
mOnImageDownloadedListener = listener;
}
}
Then when you create your task try:
if (!image.exists()) {
// Create the task.
DownloadImage downloadTask = new DownLoadImage(context, spannable, img);
// Set your listener.
downloadTask.setOnImageDownloadedListener(new OnImageLoadedListener() {
#Override
public void onImageDownloaded(Spannable spannable, Image image) {
// Add the images.
addImages(spannable, **YourContextHere(Activity/etc)**.this, image)
}
});
// Execute.
downloadTask.execute();
} else
addImages(spannable, context, img);
Hope this helps.
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
I am working on an App in which I have a ListView with a lot of images. To ensure a smooth user experience I followed this lesson from Android Developer: http://developer.android.com/training/displaying-bitmaps/process-bitmap.html . I implemented the Concurrency Handler exactly as described there (as far as I can tell), yet it does not work very well. It does a reasonable job when the user simply flings the ListView, but when the user flings the ListView and suddenly stops it, the wrong images appear or sometimes no images appear at all. My code to load the images in the background is:
public class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> mImageViewReference;
private int mData = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
mImageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background
#Override
protected Bitmap doInBackground(Integer... params) {
Bitmap bitmap = null;
mData = params[0];
...
if (mLruCache.getBitmapFromMemoryCache(key) != null) {
bitmap = mLruCache.getBitmapFromMemoryCache(key);
} else {
bitmap = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory().toString() + "/MyApp/" + key + ".png", options);
mLruCache.addBitmapToMemoryCache(key, bitmap);
}
return bitmap;
}
// Once complete, see if ImageView is still around and set bitmap
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) bitmap = null;
if (mImageViewReference != null && bitmap != null) {
final ImageView imageView = mImageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public static class AsyncDrawable extends BitmapDrawable {
private static WeakReference<BitmapWorkerTask> mBitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
mBitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return mBitmapWorkerTaskReference.get();
}
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
int bitmapData = bitmapWorkerTask.mData;
if (bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
return false;
}
}
return true;
}
public static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
}
In the getView() method of my adapter I have:
// Load images in the background
int data = position;
if (BitmapWorkerTask.cancelPotentialWork(data, imageView)) {
BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView);
BitmapWorkerTask.AsyncDrawable asyncDrawable = new BitmapWorkerTask.AsyncDrawable(mContext.getResources(), null, bitmapWorkerTask);
imageView.setImageDrawable(asyncDrawable);
bitmapWorkerTask.execute(params);
}
It's always hard to get it right... Why don't you just use UniversalImageLoader ?