Glide Image request : Does downloadOnly check the cache before downloading only? - android

I am making a request like below, but i want to know if downloadOnly checks the cache for the image first?
FutureTarget<File> future = Glide.with(applicationContext)
.load(yourUrl)
.downloadOnly(500, 500);
File cacheFile = future.get();
My main issue is that the image in yourUrl is already loaded in the cache, and i need a synchronous way to retrieve the image from the cache in a background thread.
The code above works, but i need to know if there is a cache check before downloadOnly. Thanks.

After turning on verbose logging for Glide, I was able to see exactly what was going on when the image was being called, while DecodeJob mostly fetched from cache in less than 10ms, at times it fetched the data again, not sure maybe from disk or over the wire.
So ultimately i had to check only the cache with a custom StreamModelLoader that throws an exception if trying to go over the wire, and on a cache MISS then use the default flow.
private final StreamModelLoader<String> cacheOnlyStreamLoader = new StreamModelLoader<String>() {
#Override
public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
return new DataFetcher<InputStream>() {
#Override
public InputStream loadData(Priority priority) throws Exception {
throw new IOException();
}
#Override
public void cleanup() {
}
#Override
public String getId() {
return model;
}
#Override
public void cancel() {
}
};
}
};
FutureTarget<File> future = Glide.with(progressBar.getContext())
.using(cacheOnlyStreamLoader)
.load(url).downloadOnly(width, height);
File cacheFile = null;
try {
cacheFile = future.get();
} catch(Exception ex) {
ex.printStackTrace(); //exception thrown if image not in cache
}
if(cacheFile == null || cacheFile.length() < 1) {
//didn't find the image in cache
future = Glide.with(progressBar.getContext())
.load(url).downloadOnly(width, height);
cacheFile = future.get(3, TimeUnit.SECONDS); //wait 3 seconds to retrieve the image
}

Related

Android: Should I use AsyncTask for these two operations?

I have these next two methods, takescreenshots and saveScreenshot:
public static Bitmap takeScreenshot(View oView)
{
Bitmap oBitmap = null;
if(oView!=null) {
if (oView.getWidth()>0 & oView.getHeight()>0) {
oBitmap = Bitmap.createBitmap(oView.getWidth(),
oView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(oBitmap);
try{
oView.draw(canvas);
}catch (Exception ex){
//log or whatever
}
}
}
return oBitmap;
}
public static File saveScreenshot(Bitmap oBitmap)
{
String mPath = ApplicationContext.getActivity().getCacheDir().getAbsolutePath() + "/artandwordsshared_img.jpg";
File imageFile = new File(mPath);
try{
boolean operationOK = true;
if (imageFile.exists()){
operationOK = imageFile.delete();
}
if (operationOK) {
operationOK = imageFile.createNewFile();
}
if (operationOK) {
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 75;
oBitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
}
}catch(java.io.IOException ex){
ex.printStackTrace();
}
return imageFile;
}
I use this "takeScreenshot" method to capture a view (I use it in some parts of my app) and the other one to save screenshot on device, and I'd like to know if I must/should (regarding good practices) call them through an AsyncTask so they run on background, or if it is not necessary at all. I'm currently using AsyncTask for both and all it's working just fine, but not sure if it is really a must, like it is, for example, for network operations.
Thank you in advance.
Edit 1: Adding the AsyncTask "TakeScreenshot" class.
class TakeScreenshot extends AsyncTask<String, Void, ImageProcessingResult>
{
private WeakReference<View> view2Capture;
private ImageListeners listener;
private String asyncTaskCaller;
TakeScreenshot(View view2Capture, ImageListeners listener, String asyncTaskCaller)
{
this.listener = listener;
this.view2Capture = new WeakReference<>(view2Capture);
this.asyncTaskCaller = asyncTaskCaller;
}
protected ImageProcessingResult doInBackground(String... urls)
{
Bitmap bitmap = null;
String result = Enum.Result.KO;
if(view2Capture.get()!=null)
{
bitmap = ImageHelper.takeScreenshot(view2Capture.get());
result = ImageHelper.saveBitmap(bitmap);
}
return new ImageProcessingResult(bitmap, result);
}
protected void onPostExecute(ImageProcessingResult ipr)
{
listener.onScreenCaptureFinished(ipr, asyncTaskCaller);
}
}
BTW, as for now takeScreenshot method called from AsyncTask is working just fine. Just trying to use good practices, and that's why my post.
I'm not sure you will be able to call first function takeScreenshot in the background Thread. Because you are performing operation with UI Draw. In any case, it makes no sense to put this small implementation to the background.
Next function saveScreenshot must be defined in the background Thread for sure. You need to eliminate the jank on UI which you would have because of using in in the foreground. Maybe you will not feel difference on new devices, but in some condition/platforms you will.
UPDATE
Seems like you new to the Android. Of course you can use AsyncTask, but people prefer other tools. AsyncTask is very old and nowadays there are bunch of alternatives. Just try to search for it.
On another hand. AsyncTask based on Java Executors(which includes ThreadPool, "Handler", MessageQueue, etc). For simple actions like yours, you can use just Thread. Clean and simple.
//Just make it once
private final Handler ui = new Handler(
Looper.getMainLooper());
//Whenever you need just call next call
new Thread("Tag").start(() -> {
//Background Work
//Whenever you need to submit something to UI
ui.post(() -> {
// Foreground Work
});
}
})

I have many pictures that I need to download at the same time/getting an accessible URL from Firebase Storage

I'm working right now with Firebase and I've found a problem while trying to get multiple pictures from Firebase Storage at the same time. The log shows something like -- threads 128 pool full...
I think this happens because I'm putting Firebase downloading pictures code in a loop, so that I can download all pics. See code below.
StorageReference islandRef = storageRef.child("images/island.jpg");
final long ONE_MEGABYTE = 1024 * 1024;
islandRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
// Data for "images/island.jpg" is returns, use this as needed
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
So after looking in stack for a solution, I discovered that I can download pictures via URL without Firebase's method and that will not open threads which is good, but now I don't know how to get an accessible URL via this method:
url = new URL (storageRef.getDownloadUrl().toString());
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Thank you. =)

Requesting filesize and MIME type for a list of URLs in Android

In an Android application, I have a list of image URLs like this:
List<String> urls = new ArrayList<String>(100);
urls.add("http://www.example.org/1.jpg");
urls.add("http://www.example.org/2.png");
urls.add("http://www.example.org/3.jpg");
urls.add("http://www.example.org/4.jpg");
urls.add("http://www.example.org/5.png");
urls.add("http://www.example.org/6.png");
urls.add("http://www.example.org/7.png");
urls.add("http://www.example.org/8.jpg");
urls.add("http://www.example.org/9.jpg");
urls.add("http://www.example.org/10.gif");
...
urls.add("http://www.example.org/100.jpg");
Now I have to get the filesize and MIME type for all of these URLs, and this should be done as fast as possible, of course.
What I did is the following:
for (String url : urls) {
int fileSize;
try {
URLConnection urlConnection;
urlConnection = new URL(url).openConnection();
urlConnection.connect();
final String mimeType = urlConnection.getContentType();
final int fileSize = urlConnection.getContentLength();
// do something with those two pieces of information
}
catch (MalformedURLException e) {
continue;
}
catch (IOException e) {
// some special handling
}
}
But this is terribly slow. This is because it is using a single thread and requesting the URLs one by one, while a web browser would always access multiple files at a time, isn't it?
So how can I make it faster?
For HttpClient, I've read that you should re-use instances and there are some ways to use them in a multi-threaded environment.
But how would I do this with URLConnection or any other class that gives you filesize and MIME type?
Edit:
The images are not all on the same host, but spread across only a few servers, say 100 images spread across 5 host names.
Can you use a few threads or run several AsynTasks at once that do the job? Is there anything you have to pay attention to, such as recycling URLConnection objects or so?
I'm not quite sure how to use multiple threads to share task list (100 image files) and merge the results (MIME types and file sizes) afterwards. Can you help?
Split your work up into smaller peaces and let a worker Thread handle it:
The worker Thread:
public class Downloader extends Thread {
private final String mUrl;
private String mMimeType;
private int mFileSize;
public Downloader(String url) {
mUrl = url;
}
#Override
public void run() {
try {
URLConnection urlConnection;
urlConnection = new URL(mUrl).openConnection();
urlConnection.connect();
mMimeType = urlConnection.getContentType();
mFileSize = urlConnection.getContentLength();
} catch (IOException e) {
e.printStackTrace();
}
}
public String getMimeType() {
return mMimeType;
}
public int getFileSize() {
return mFileSize;
}
}
Instantiate, run and wait for the worker:
ArrayList<String> urls = new ArrayList<String>(10);
// ...
ArrayList<Thread> threads = new ArrayList<Thread>(10);
for (String url : urls) {
Thread t = new Downloader(url);
threads.add(t);
t.start();
}
for (Thread t : threads) {
try {
// do not wait for other threads in main UI thread!
t.join();
//((Downloader) t).getMimeType();
//((Downloader) t).getFileSize();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Make sure to note wait for the worker Thread in your UI Thread.
The answer above should only be used for a small set of URLs. A ThreadPool may not be necessary because the Threads will wait for IO most of the time.
Here is your requested answer with a ThreadPool.
It's using the same Downloader class as the above example with only one change:
Spawning Threads is done by the ThreadPool and the single tasks don't need to be a real Thread anymore. So let the Downloader implement a Runnable instead of extending a Thread:
public class Downloader implements Runnable {
Hopefully it's what you are looking for.
public class ThreadedDownloader {
private static final int KEEP_ALIVE_TIME = 1;
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
private LinkedBlockingQueue<Runnable> mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
private ThreadPoolExecutor mDecodeThreadPool = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, mDecodeWorkQueue) {
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Downloader d = (Downloader) r;
// do something with finished Downloader d
// like saving it's result to some sort of list
// d.getMimeType();
// d.getFileSize();
if (mDecodeWorkQueue.isEmpty()) {
onAllDownloadsFinised();
}
}
};
/** Download a list of urls and check it's mime time and file size. */
public void download(List<String> urls) {
for (String url : urls) {
mDecodeThreadPool.execute(new Downloader(url));
}
}
/** Calles when all downloads have finished. */
private void onAllDownloadsFinised() {
// do whatever you want here
// update UI or something
}
}
I don't have example Code, but the HTTP verb HEAD is what you're looking for. It retrieves the headers (including mime and content-length) without transferring the content body.
This answer goes into more detail about what HEAD does.
I think you have most, if not all of the pieces to your solution in the answers already submitted.
Here's what I'd do:
1) make the head requests using Executors.newCachedThreadPool();
2) store the responses in a Map
3) create a payload Map
4) load the real images using AsyncTask instances
5) when each bitmap load completes, store the results in the payload map
Just my thoughts.

How does Android memory allocation work with ImageView?

I'm loading approximately 20 pictures of size 50-70K from server, then display them in a ListView. Initially I stored the data as Bitmap which causes memory running out quickly. Then I decided to compress all these bitmap files and store in Content provider Media. So in my adapter, the user data only contains the Uri to the image file.
However it didn't fix the problem at all, it run a bit longer, but still crashed after loading about 10 pictures or so. Here is the error log from the compiler.
1048576-byte external allocation too large for this process
VM won't let us allocate 1048576 bytes
I even clean up each bitmap data after setting it to my ImageView, plus delete the all the image files which are stored in my sdcard
#Override
public void onDestroy() {
// clean up
for (User user : userList) {
getContentResolver().delete(user.getImageUri(), null, null);
}
super.onDestroy();
}
private Uri constructUriFromBitmap(Bitmap bitmap) {
ContentValues values = new ContentValues(1);
values.put(Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
try {
OutputStream outStream = getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close();
}
catch (Exception e) {
Log.e(TAG, "exception while writing image", e);
}
bitmap.recycle();
return uri;
}
Now I ran out of idea, I really don't know what could go wrong in this case. I wonder if anyone has experienced this issue could shed me some lights?
Since the code is quite long, I only extract the main functions:
Here is my User class data:
public class FriendFeed {
// required parameters
private final int activityId; // in case we want to handle the detail of
// this activity
private final int friendId;
private final String friendName;
private final Challenge.Type challengeType;
private final String activityTime;
private final String placeName;
// optional parameter
private String challengeName;
private String challengeDescription;
private Uri activitySnapPictureUri = null;
private Uri friendPictureUri = null;
private String activityComment;
And here is my main function:
#Override
protected Boolean doInBackground(Void...voids) {
JSONArray array = JsonHelper.getJsonArrayFromUrlWithData(GET_FRIEND_FEED_URL, datas);
if (array != null) {
try {
for (int i = 0; i < array.length(); ++i) {
Uri snapPictureUri = null;
Uri userPictureUri = null;
if (Challenge.returnType(array.getJSONObject(i).getString("challenges_tbl_type")) == Challenge.Type.SNAP_PICTURE) {
snapPictureUri = constructUriFromBitmap(ImageHelper.downloadImage(array.getJSONObject(i).getString("activity_tbl_snap_picture_url")));
}
if(ImageHelper.downloadImage(array.getJSONObject(i).getString("users_tbl_user_image_url")) != null) {
userPictureUri = constructUriFromBitmap(ImageHelper.downloadImage(array.getJSONObject(i).getString("users_tbl_user_image_url")));
}
publishProgress(
new FriendFeed.Builder(
// required parameters
array.getJSONObject(i).getInt("activity_tbl_id"),
array.getJSONObject(i).getInt("friends_tbl_friend_id"),
array.getJSONObject(i).getString("users_tbl_username"),
Challenge.returnType(array.getJSONObject(i).getString("challenges_tbl_type")),
array.getJSONObject(i).getString("activity_tbl_created"),
array.getJSONObject(i).getString("spots_tbl_name"))
// optional parameters
.challengeName(array.getJSONObject(i).getString("challenges_tbl_name"))
.challengeDescription(array.getJSONObject(i).getString("challenges_tbl_description"))
.activitySnapPictureUri(snapPictureUri)
.friendPictureUri(userPictureUri)
.activityComment(array.getJSONObject(i).getString("activity_tbl_comment"))
.build());
}
}
catch (JSONException e) {
Log.e(TAG + "GetFriendFeedTask.doInBackGround(Void ...voids) : ", "JSON error parsing data" + e.toString());
}
return true;
}
else {
return false;
}
}
Android enforces a per-process memory allocation limit of 24MB so you can't allocate more than that. However, 20 pics of 70K each should amount to 1.4MB only... so my guesses:
Maybe you're allocating Bitmaps in other parts of your app, so that there's less than 1.4MB available for your bitmaps on this ListView.
Memory leak somewhere
If you determine that you really need all the bitmaps you're using, are you sure you need the bitmaps to be this large or have this much resolution? Reducing them can help.
If all else fails and you do need lots of bitmaps in memory, you can always use OpenGL textures.

How to lazy load images in ListView in Android

I am using a ListView to display some images and captions associated with those images. I am getting the images from the Internet. Is there a way to lazy load images so while the text displays, the UI is not blocked and images are displayed as they are downloaded?
The total number of images is not fixed.
Here's what I created to hold the images that my app is currently displaying. Please note that the "Log" object in use here is my custom wrapper around the final Log class inside Android.
package com.wilson.android.library;
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;
public class DrawableManager {
private final Map<String, Drawable> drawableMap;
public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}
public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}
Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}
return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}
final Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
#Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}
I made a simple demo of a lazy list (located at GitHub) with images.
Basic Usage
ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView);
Don't forget to add the
following permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please
create only one instance of ImageLoader and reuse it all around your
application. This way image caching will be much more efficient.
It may be helpful to somebody. It downloads images in the background thread. Images are being cached on an SD card and in memory. The cache implementation is very simple and is just enough for the demo. I decode images with inSampleSize to reduce memory consumption. I also try to handle recycled views correctly.
I recommend open source instrument Universal Image Loader. It is originally based on Fedor Vlasov's project LazyList and has been vastly improved since then.
Multithread image loading
Possibility of wide tuning ImageLoader's configuration (thread executors, downloader, decoder, memory and disc cache, display image options, and others)
Possibility of image caching in memory and/or on the device's file system (or SD card)
Possibility to "listen" loading process
Possibility to customize every display image call with separated options
Widget support
Android 2.0+ support
Multithreading For Performance, a tutorial by Gilles Debunne.
This is from the Android Developers Blog. The suggested code uses:
AsyncTasks.
A hard, limited size, FIFO cache.
A soft, easily garbage collect-ed cache.
A placeholder Drawable while you download.
Update: Note that this answer is pretty ineffective now. The Garbage Collector acts aggressively on SoftReference and WeakReference, so this code is NOT suitable for new apps. (Instead, try libraries like Universal Image Loader suggested in other answers.)
Thanks to James for the code, and Bao-Long for the suggestion of using SoftReference. I implemented the SoftReference changes on James' code. Unfortunately, SoftReferences caused my images to be garbage collected too quickly. In my case, it was fine without the SoftReference stuff, because my list size is limited and my images are small.
There's a discussion from a year ago regarding the SoftReferences on google groups: link to thread. As a solution to the too-early garbage collection, they suggest the possibility of manually setting the VM heap size using dalvik.system.VMRuntime.setMinimumHeapSize(), which is not very attractive to me.
public DrawableManager() {
drawableMap = new HashMap<String, SoftReference<Drawable>>();
}
public Drawable fetchDrawable(String urlString) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null)
return drawable;
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");
drawableRef = new SoftReference<Drawable>(drawable);
drawableMap.put(urlString, drawableRef);
if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
return drawableRef.get();
} catch (MalformedURLException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}
public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
if (drawableRef != null) {
Drawable drawable = drawableRef.get();
if (drawable != null) {
imageView.setImageDrawable(drawableRef.get());
return;
}
// Reference has expired so remove the key from drawableMap
drawableMap.remove(urlString);
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};
Thread thread = new Thread() {
#Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}
Picasso
Use Jake Wharton's Picasso Library.
(A Perfect ImageLoading Library from the developer of ActionBarSherlock)
A powerful image downloading and caching library for Android.
Images add much-needed context and visual flair to Android applications. Picasso allows for hassle-free image loading in your application—often in one line of code!
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Many common pitfalls of image loading on Android are handled automatically by Picasso:
Handling ImageView recycling and download cancellation in an adapter.
Complex image transformations with minimal memory use.
Automatic memory and disk caching.
Picasso Jake Wharton's Library
Glide
Glide is a fast and efficient open-source media management framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy-to-use interface.
Glide supports fetching, decoding, and displaying video stills, images, and animated GIFs. Glide includes a flexible API that allows developers to plug into almost any network stack. By default, Glide uses a custom HttpUrlConnection based stack but also includes utility libraries plug-in to Google's Volley project or Square's OkHttp library instead.
Glide.with(this).load("your-url-here").into(imageView);
Glide's primary focus is on making scrolling any kind of a list of images as smooth and fast as possible, but Glide is also effective for almost any case where you need to fetch, resize, and display a remote image.
Glide Image Loading Library
Fresco by Facebook
Fresco is a powerful system for displaying images in Android applications.
Fresco takes care of image loading and display, so you don't have to. It will load images from the network, local storage, or local resources, and display a placeholder until the image has arrived. It has two levels of cache; one in memory and another in internal storage.
Fresco Github
In Android 4.x and lower, Fresco puts images in a special region of Android memory. This lets your application run faster - and suffer the dreaded OutOfMemoryError much less often.
Fresco Documentation
High-performance loader - after examining the methods suggested here,
I used Ben's solution with some changes -
I realized that working with drawable is faster than with bitmaps so I uses drawable instead
Using SoftReference is great, but it makes the cached image to be deleted too often, so I added a Linked list that holds images references, preventing the image to be deleted, until it reached a predefined size
To open the InputStream I used java.net.URLConnection which allows me to use web cache (you need to set a response cache first, but that's another story)
My code:
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Collections;
import java.util.WeakHashMap;
import java.lang.ref.SoftReference;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
public class DrawableBackgroundDownloader {
private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public static int MAX_CACHE_SIZE = 80;
public int THREAD_POOL_SIZE = 3;
/**
* Constructor
*/
public DrawableBackgroundDownloader() {
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
/**
* Clears all instance data and stops running threads
*/
public void Reset() {
ExecutorService oldThreadPool = mThreadPool;
mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
oldThreadPool.shutdownNow();
mChacheController.clear();
mCache.clear();
mImageViews.clear();
}
public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {
mImageViews.put(imageView, url);
Drawable drawable = getDrawableFromCache(url);
// check in UI thread, so no concurrency issues
if (drawable != null) {
//Log.d(null, "Item loaded from mCache: " + url);
imageView.setImageDrawable(drawable);
} else {
imageView.setImageDrawable(placeholder);
queueJob(url, imageView, placeholder);
}
}
private Drawable getDrawableFromCache(String url) {
if (mCache.containsKey(url)) {
return mCache.get(url).get();
}
return null;
}
private synchronized void putDrawableInCache(String url,Drawable drawable) {
int chacheControllerSize = mChacheController.size();
if (chacheControllerSize > MAX_CACHE_SIZE)
mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();
mChacheController.addLast(drawable);
mCache.put(url, new SoftReference<Drawable>(drawable));
}
private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {
/* Create handler in UI thread. */
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
String tag = mImageViews.get(imageView);
if (tag != null && tag.equals(url)) {
if (imageView.isShown())
if (msg.obj != null) {
imageView.setImageDrawable((Drawable) msg.obj);
} else {
imageView.setImageDrawable(placeholder);
//Log.d(null, "fail " + url);
}
}
}
};
mThreadPool.submit(new Runnable() {
#Override
public void run() {
final Drawable bmp = downloadDrawable(url);
// if the view is not visible anymore, the image will be ready for next time in cache
if (imageView.isShown())
{
Message message = Message.obtain();
message.obj = bmp;
//Log.d(null, "Item downloaded: " + url);
handler.sendMessage(message);
}
}
});
}
private Drawable downloadDrawable(String url) {
try {
InputStream is = getInputStream(url);
Drawable drawable = Drawable.createFromStream(is, url);
putDrawableInCache(url,drawable);
return drawable;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
URL url = new URL(urlString);
URLConnection connection;
connection = url.openConnection();
connection.setUseCaches(true);
connection.connect();
InputStream response = connection.getInputStream();
return response;
}
}
I have followed this Android Training and I think it does an excellent job at downloading images without blocking the main UI. It also handles caching and dealing with scrolling through many images: Loading Large Bitmaps Efficiently
1. Picasso allows for hassle-free image loading in your application—often in one line of code!
Use Gradle:
implementation 'com.squareup.picasso:picasso:(insert latest version)'
Just one line of code!
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. Glide An image loading and caching library for Android focused on smooth scrolling
Use Gradle:
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
// For a simple view:
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);
3. fresco is a powerful system for displaying images in Android
applications.Fresco takes care of image loading and display, so you don't have
to.
Getting Started with Fresco
I've written a tutorial that explains how to do lazy-loading of images in a listview. I go into some detail about the issues of recycling and concurrency. I also use a fixed thread pool to prevent spawning a lot of threads.
Lazy loading of images in Listview Tutorial
The way I do it is by launching a thread to download the images in the background and hand it a callback for each list item. When an image is finished downloading it calls the callback which updates the view for the list item.
This method doesn't work very well when you're recycling views however.
I just want to add one more good example, XML Adapters. As it's is used by Google and I am also using the same logic to avoid an OutOfMemory error.
Basically this ImageDownloader is your answer (as it covers most of your requirements). Some you can also implement in that.
This is a common problem on Android that has been solved in many ways by many people. In my opinion the best solution I've seen is the relatively new library called Picasso. Here are the highlights:
Open source, but headed up by Jake Wharton of ActionBarSherlock fame.
Asynchronously load images from network or app resources with one line of code
Automatic ListView detection
Automatic disk and memory caching
Can do custom transformations
Lots of configurable options
Super simple API
Frequently updated
I have been using NetworkImageView from the new Android Volley Library com.android.volley.toolbox.NetworkImageView, and it seems to be working pretty well. Apparently, this is the same view that is used in Google Play and other new Google applications. Definitely worth checking out.
Google I/O 2013 volley image cache tutorial
Developers Google events
Well, image loading time from the Internet has many solutions. You may also use the library Android-Query. It will give you all the required activity. Make sure what you want to do and read the library wiki page. And solve the image loading restriction.
This is my code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView imageview = (ImageView) v.findViewById(R.id.icon);
AQuery aq = new AQuery(convertView);
String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";
aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
#Override
public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
iv.setImageBitmap(bm);
}
));
return v;
}
It should be solve your lazy loading problem.
I think this issue is very popular among Android developers, and there are plenty of such libraries that claims to resolve this issue, but only a few of them seems to be on the mark. AQuery is one such library, but it is better than most of them in all aspects and is worth trying for.
You must try this Universal Loader is best.
I am using this after done many RnD on lazy loading .
Universal Image Loader
Features
Multithread image loading (async or sync)
Wide customization of ImageLoader's configuration (thread executors, downloader, decoder, memory and disk cache, display image options, etc.)
Many customization options for every display image call (stub images, caching switch, decoding options, Bitmap processing and displaying, etc.)
Image caching in memory and/or on disk (device's file system or SD card)
Listening loading process (including downloading progress)
Android 2.0+ support
Have a look at Shutterbug, Applidium's lightweight SDWebImage (a nice library on iOS) port to Android.
It supports asynchronous caching, stores failed URLs, handles concurrency well, and helpful subclasses are included.
Pull requests (and bug reports) are welcome, too!
DroidParts has ImageFetcher that requires zero configuration to get started.
Uses a disk & in-memory Least Recently Used (LRU) cache.
Efficiently decodes images.
Supports modifying bitmaps in background thread.
Has simple cross-fade.
Has image loading progress callback.
Clone DroidPartsGram for an example:
Novoda also has a great lazy image loading library and many apps like Songkick, Podio, SecretDJ and ImageSearch use their library.
Their library is hosted here on Github and they have a pretty active issues tracker as well. Their project seems to be pretty active too, with over 300+ commits at the time of writing this reply.
Just a quick tip for someone who is in indecision regarding what library to use for lazy-loading images:
There are four basic ways.
DIY => Not the best solution but for a few images and if you want to go without the hassle of using others libraries
Volley's Lazy Loading library => From guys at android. It is nice and everything but is poorly documented and hence is a problem to use.
Picasso: A simple solution that just works, you can even specify the exact image size you want to bring in. It is very simple to use but might not be very "performant" for apps that has to deal with humongous amounts of images.
UIL: The best way to lazy load images. You can cache images(you need permission of course), initialize the loader once, then have your work done. The most mature asynchronous image loading library I have ever seen so far.
If you want to display Shimmer layout like Facebook there is a official facebook library for that. FaceBook Shimmer Android
It takes care of everything, You just need to put your desired design code in nested manner in shimmer frame.
Here is a sample code.
<com.facebook.shimmer.ShimmerFrameLayout
android:id=“#+id/shimmer_view_container”
android:layout_width=“wrap_content”
android:layout_height="wrap_content"
shimmer:duration="1000">
<here will be your content to display />
</com.facebook.shimmer.ShimmerFrameLayout>
And here is the java code for it.
ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();
Add this dependency in your gradle file.
implementation 'com.facebook.shimmer:shimmer:0.1.0#aar'
Here is how it looks like.
Check my fork of LazyList. Basically, I improve the LazyList by delaying the call of the ImageView and create two methods:
When you need to put something like "Loading image..."
When you need to show the downloaded image.
I also improved the ImageLoader by implementing a singleton in this object.
All above code have their own worth but with my personal experience just give a try with Picasso.
Picasso is a library specifically for this purpose, in-fact it will manage cache and all other network operations automatically.You will have to add library in your project and just write a single line of code to load image from remote URL.
Please visit here : http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149
Use the glide library. It worked for me and will work for your code too.It works for both images as well as gifs too.
ImageView imageView = (ImageView) findViewById(R.id.test_image);
GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
Glide
.with(this)
.load(url)
.listener(new RequestListener<String, GlideDrawable>() {
#Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
#Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(imagePreview);
}
I can recommend a different way that works like a charm: Android Query.
You can download that JAR file from here
AQuery androidAQuery = new AQuery(this);
As an example:
androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);
It's very fast and accurate, and using this you can find many more features like animation when loading, getting a bitmap (if needed), etc.
Give Aquery a try. It has amazingly simple methods to load and cache images asynchronously.
URLImageViewHelper is an amazing library that helps you to do that.
public class ImageDownloader {
Map<String, Bitmap> imageCache;
public ImageDownloader() {
imageCache = new HashMap<String, Bitmap>();
}
// download function
public void download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
// Caching code right here
String filename = String.valueOf(url.hashCode());
File f = new File(getCacheDirectory(imageView.getContext()),
filename);
// Is the bitmap in our memory cache?
Bitmap bitmap = null;
bitmap = (Bitmap) imageCache.get(f.getPath());
if (bitmap == null) {
bitmap = BitmapFactory.decodeFile(f.getPath());
if (bitmap != null) {
imageCache.put(f.getPath(), bitmap);
}
}
// No? download it
if (bitmap == null) {
try {
BitmapDownloaderTask task = new BitmapDownloaderTask(
imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
} catch (Exception e) {
Log.e("Error==>", e.toString());
}
} else {
// Yes? set the image
imageView.setImageBitmap(bitmap);
}
}
}
// cancel a download (internal only)
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;
}
// gets an existing download if one exists for the imageview
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;
}
// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
String sdState = android.os.Environment.getExternalStorageState();
File cacheDir;
if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
File sdDir = android.os.Environment.getExternalStorageDirectory();
// TODO : Change your diretcory here
cacheDir = new File(sdDir, "data/ToDo/images");
} else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
return cacheDir;
}
private void writeFile(Bitmap bmp, File f) {
FileOutputStream out = null;
try {
out = new FileOutputStream(f);
bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null)
out.close();
} catch (Exception ex) {
}
}
}
// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#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.
url = (String) params[0];
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();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with
// it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
// cache the image
String filename = String.valueOf(url.hashCode());
File f = new File(
getCacheDirectory(imageView.getContext()), filename);
imageCache.put(f.getPath(), bitmap);
writeFile(bitmap, f);
}
}
}
}
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.WHITE);
bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
// the actual download code
static Bitmap downloadBitmap(String url) {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
HttpVersion.HTTP_1_1);
HttpClient client = new DefaultHttpClient(params);
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();
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from "
+ url + e.toString());
} finally {
if (client != null) {
// client.close();
}
}
return null;
}
}
I had this issue and implemented lruCache. I believe you need API 12 and above or use the compatiblity v4 library. lurCache is fast memory, but it also has a budget, so if you're worried about that you can use a diskcache... It's all described in Caching Bitmaps.
I'll now provide my implementation which is a singleton I call from anywhere like this:
//Where the first is a string and the other is a imageview to load.
DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);
Here's the ideal code to cache and then call the above in getView of an adapter when retrieving the web image:
public class DownloadImageTask {
private LruCache<String, Bitmap> mMemoryCache;
/* Create a singleton class to call this from multiple classes */
private static DownloadImageTask instance = null;
public static DownloadImageTask getInstance() {
if (instance == null) {
instance = new DownloadImageTask();
}
return instance;
}
//Lock the constructor from public instances
private DownloadImageTask() {
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
public void loadBitmap(String avatarURL, ImageView imageView) {
final String imageKey = String.valueOf(avatarURL);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
}
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
/* A background process that opens a http stream and decodes a web image. */
class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTaskViaWeb(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon = BitmapFactory.decodeStream(in);
}
catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);
return mIcon;
}
/* After decoding we update the view on the main UI. */
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}

Categories

Resources