nostra13 / android-universal-image-loader Goto Github PK
View Code? Open in Web Editor NEWPowerful and flexible library for loading, caching and displaying images on Android.
License: Apache License 2.0
Powerful and flexible library for loading, caching and displaying images on Android.
License: Apache License 2.0
Hi.
Thank you for your efforts with this wonderful image loader. :)
But sometimes, image loading fails for an unknown reason.
I found that the 'for' loop in FuzzyKeyMemoryCache.put(K, V) method occurs ConcurrentModificationException in that case.
I guess that this error kills the running thread instantly without calling callback method of ImageLoadingListener.
Please fix this bug.
Thank you.
Thanks for the UniveralImageLoader - exactly what I needed!
Currently, I'm using TotalSizeLimitedDiscCache. I could, however, use one more feature: In my use case, I'm caching pics which may occasionally change. As such, I'd like to be able to tell UniversalImageLoader to only receive images from file cache if they have been stored not longer ago then a certain time period (say, a week). If the image in the cache is older than that, just receive it again from the url.
I am aware of the possibility to provide my own DscCache implementation, but I thought my use case might very well be a common one...
May be this code will be useful:
public static void setClient() {
HttpParams params = new BasicHttpParams();
// Turn off stale checking. Our connections break all the time anyway,
// and it's not worth it to pay the penalty of checking every time.
HttpConnectionParams.setStaleCheckingEnabled(params, false);
// Default connection and socket timeout of 10 seconds. Tweak to taste.
HttpConnectionParams.setConnectionTimeout(params, 10 * 1000);
HttpConnectionParams.setSoTimeout(params, 10 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// Don't handle redirects -- return them to the caller. Our code
// often wants to re-POST after a redirect, which we must do ourselves.
HttpClientParams.setRedirecting(params, false);
// Set the specified user agent and register standard protocols.
HttpProtocolParams.setUserAgent(params, "bigbuzzy business");
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
AndroidApplication.client = new DefaultHttpClient(manager, params);
}
//И загрузку через него (нет сети - берем из кеша иначе грузим):
public static String retrieve(String url, boolean cache) {
StringBuilder sb = new StringBuilder();
sb.append(AndroidApplication.DOMEN);
sb.append(url);
url = sb.toString();
Log.d("retrieve", url);
String md5 = Md5.md5(url);
File casheDir = AndroidApplication.cacheDir;//context.getCacheDir();
File f = null;
if (casheDir != null && cache) {
f = new File(casheDir, md5);
final long time = new Date().getTime() / 1000;
if (f.exists()) {
if ((f.lastModified() / 1000 + 600) > time) {
return readFile(f);
}
}
}
HttpGet getRequest = new HttpGet(url);
try {
HttpResponse getResponse = AndroidApplication.getClient().execute(getRequest);
final int statusCode = getResponse.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.e("statusCode", statusCode + "");
}
HttpEntity getResponseEntity = getResponse.getEntity();
if (getResponseEntity != null && statusCode == HttpStatus.SC_OK) {
String s = EntityUtils.toString(getResponseEntity);
if (statusCode != HttpStatus.SC_OK && f != null) {
BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(f));
os.write(s.getBytes());
os.close();
}
return s;
}
} catch (IOException e) {
getRequest.abort();
if (f != null) {
if (f.exists()) {
return readFile(f);
}
}
Log.e("NetUtils error", e.toString());
}
return null;
}
public static String readFile(File file) {
String data = null;
try {
FileInputStream fis = null;
InputStreamReader isr = null;
fis = new FileInputStream(file);
isr = new InputStreamReader(fis);
char[] inputBuffer;
inputBuffer = new char[fis.available()];
isr.read(inputBuffer);
data = new String(inputBuffer);
isr.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
Я не знаю как у вас используется FileUtils#copyStream(), но, что-то мне подсказывает, что скрывать IoException не правильно. Достаточно задать вопрос: будет ли приложение работать дальше нормально, если потоки не откопировались?
Investigate HTTP persistent connection and possibility to apply this feature to project.
I think that writing into sd-cards root is not the best practice. It woould be better to use
appCacheDir = new File(context.getExternalCacheDir(), cacheDirPath);
instead of
appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDirPath);
Hi,
i am using your library in my app to load images coming from an Internet server in a Gallery (android.widget.Gallery) and I am experiencing an strange behaviour, when I swipe my finger on it the scrolling is not smooth at all, there is some short of constant flickering.
This is the layout of my gallery:
<Gallery
android:id="@+id/gallery"
style="@style/Carousel.Gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</Gallery>
And this is the layout of a gallery item:
@dimen/thumbnail_height and @dimen/thumbnail_width are some dimensions defined by me (120dip for xhdpi devices).
And this is the adapter I am using for the gallery
public class GalleryAdapter extends BaseAdapter {
private int layoutId;
protected Context context;
private List<String> items;
public GalleryAdapter(Context context, int layoutId) {
this.layoutId = layoutId;
this.items = new ArrayList<String>();
this.context = context;
}
public void append(List<String> items) {
this.items.addAll(items);
notifyDataSetChanged();
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = getView(layoutId, parent);
}
updateView(getItem(position), view);
return view;
}
private View getView(int viewId, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflater.inflate(viewId, parent, false);
}
private void updateView(Object item, View view) {
String url = (String) item;
ImageLoader imageLoader = ImageLoader.getInstance();
ImageView imageView = (ImageView) view.findViewById(R.id.image);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUrl(placeholder)
.showStubImage(placeholder).cacheOnDisc()
.decodingType(DecodingType.MEMORY_SAVING).build();
imageLoader.displayImage(url, imageView, options);
}
The placeholder image has the same dimensions as the ones set for the ImageView.
I have initiated the ImageLoader in my class extending from Application in the following way
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
getApplicationContext()).threadPoolSize(3)
.threadPriority(Thread.NORM_PRIORITY - 2)
.memoryCacheSize(1500000) // 1.5 Mb
.discCacheSize(50000000) // 50 Mb
.httpReadTimeout(10000) // 10 s
.denyCacheImageMultipleSizesInMemory().build();
ImageLoader.getInstance().init(config);
I am using your library also with a ListView and a GridView and in those cases everything works perfectly in those cases.
For testing purposes I am using a Samsung Galaxy Nexus.
Have you also experienced this problem?
Your support would be much appreciated.
Thanks
M.
I'm spanish, sorry if my english is bad. I don't know if this is the best place to put this doubt/request.
I have just started using this amazing loader. I was trying to do something similar for myself but this is far better.
Is it possible to choose the image quality (compression) for cached images? It would improve speed. I haven't seen any option for this. Thanks.
Given an albumId load its thumbnail. Now its not possible due to that url needed is:
content://media/external/audio/albumart/20 -> 20 (albumId)
content protocol is not recognised, so it fails.
Something like that will be needed:
ContentResolver res = context.getContentResolver();
Uri uri = Uri.parse("content://media/external/audio/albumart/20");
InputStream is = res.openInputStream(uri);
Content Resolver is not passed to decode functions and i don't know if it can make a memory leak.
Noone of these methods tries to close streams in case of exceptions.
The clear cache function crashes if the cache folder doesn't exists.
This is beacuse in DiskCache.java, line 27
cacheDir.listFiles();
returns null.
Investigate HTTP possibilities for caching. Maybe apply its to project.
In ImageLoader displayImage() method, log string LOG_LOAD_IMAGE_FROM_MEMORY_CACHE is not formatted which leads to placeholder (%s) appearing in logs, instead of cache related information.
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
Log.i(TAG, LOG_LOAD_IMAGE_FROM_MEMORY_CACHE);
imageView.setImageBitmap(bmp);
} else {
listener.onLoadingStarted();
if (imageLoadingExecutor.isShutdown()) {
imageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize);
}
I'm not sure what you thought should be there, but I'm using memoryCacheKey now.
Log.i(TAG, String.format(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey));
I'm looking into new ways to improve speed significantly. Apart from creating a sized cache version of the image, that i know you're working on, i have new ideas.
I haven't made any test of that one:
Think on a listView full of Images. If they were cached alternatively, one on external disk cache and the other on internal disk cache, the read speed would be greatly improved.
Also there is an issue when you have a list with allways the same url repeated. Imagine that there are 10 image thumbnails on screen.The first time it is downloaded from internet/disk up to 5 times! that is due to there is any request control over the same url. The requests over the same url should be blocked until the image is downloaded to memory/disk cache. This can be achieved using semaphores. I will post some sample code later from the last issue!.
First of all, nice work here, very professional and work out of the box. Thanks!
Then the issue I'm having. Maybe I just missed it, but I couldn't find a way in the current API to check whether the ImageView is still a valid target for the loaded image. The use case is recycled listview items: when the user scrolls, the list item view (and the associated ImageView) may have been assigned to a new list item.
Do you see the problem?
After the image has been loaded, I would like to control whether it is still ok to assign to the original ImageView or not. Or use some alternative API that doesn't require using an ImageView at all.
Зачем поле softMap объявлено protected? Даже если нужен доступ у наследников (в данном случае я думаю не нужен) лучше написать модификатор private и добавить protected метод getCache
Hi,
I downloaded your latest jar (1.5.4). When I put it into "libs" folder and refresh the project, compiler says something is wrong in this project. When I delete the jar file then my application works fine.
I tried to use version 1.2.3 of your jar file and it is working well. Therefore, I think last version has problem.
My eclipse is Juno, OS: win32, Android SDK v20
Thanks
Make singltone instance creation synchronization.
Hi,
How can the ImageLoader be used to load images on an application widget?
Because the displayImage method takes an ImageView as parameter and in a widget we have to use RemoteViews.
Thanks.
Regards,
M
Having to create a dummy ImageView every time I'm creating a callback to load a bitmap into a custom widget is kinda annoying. Could we get an option to pass null instead?
When Bitmap was cached in memory cache with small size and we want to display image in big ImageView this small Bitmap would be used.
Search this use-case solution.
I have listView in which I display images , this is a snippet from Adapters' getView method
ImageView imageView = (ImageView) v.findViewById(R.id.ivSmallImage);
File cacheDir = StorageUtils.getOwnCacheDirectory(mContext, "alrawda/Cache");
ImageLoader imageLoader = ImageLoader.getInstance();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(mContext)
.threadPoolSize(3)
.threadPriority(Thread.NORM_PRIORITY - 2)
.memoryCache(new UsingFreqLimitedMemoryCache(2000000))
.memoryCacheSize(1500000) // 1.5 Mb
.denyCacheImageMultipleSizesInMemory()
.discCache(new UnlimitedDiscCache(cacheDir))
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.enableLogging() // Not necessary in common
.build();
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.cacheInMemory()
.cacheOnDisc()
.build();
final ProgressBar spinner = new ProgressBar(mContext);
imageLoader.displayImage(o.getSmallImage(), imageView,options,new ImageLoadingListener() {
@Override
public void onLoadingStarted() {
spinner.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(FailReason failReason) {
spinner.setVisibility(View.INVISIBLE);
}
@Override
public void onLoadingComplete() {
spinner.setVisibility(View.INVISIBLE);
}
@Override
public void onLoadingCancelled() {
}
});
After some images displayed , the Logcat shows me an error :
07-12 18:01:05.885: E/dalvikvm-heap(5680): Out of memory on a 1120016-byte allocation.
07-12 18:01:05.895: I/dalvikvm(5680): "Thread-9724" prio=3 tid=14 RUNNABLE
07-12 18:01:05.950: I/dalvikvm(5680): | group="main" sCount=0 dsCount=0 obj=0x419b7e98 self=0xc2c698
07-12 18:01:05.950: I/dalvikvm(5680): | sysTid=5709 nice=13 sched=0/0 cgrp=bg_non_interactive handle=12568832
07-12 18:01:05.950: I/dalvikvm(5680): | schedstat=( 4136736133 1474479795 2225 ) utm=383 stm=30 core=0
07-12 18:01:05.970: I/dalvikvm(5680): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-12 18:01:05.970: I/dalvikvm(5680): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
07-12 18:01:05.975: I/dalvikvm(5680): at com.nostra13.universalimageloader.core.ImageDecoder.decode(ImageDecoder.java:61)
07-12 18:01:05.990: I/dalvikvm(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.decodeWithOOMHandling(LoadAndDisplayImageTask.java:163)
07-12 18:01:05.995: I/dalvikvm(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.decodeImage(LoadAndDisplayImageTask.java:150)
07-12 18:01:06.000: I/dalvikvm(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.tryLoadBitmap(LoadAndDisplayImageTask.java:104)
07-12 18:01:06.005: I/dalvikvm(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.run(LoadAndDisplayImageTask.java:58)
07-12 18:01:06.010: I/dalvikvm(5680): at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442)
07-12 18:01:06.030: I/dalvikvm(5680): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
07-12 18:01:06.030: I/dalvikvm(5680): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
07-12 18:01:06.035: I/dalvikvm(5680): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
07-12 18:01:06.040: I/dalvikvm(5680): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
07-12 18:01:06.045: I/dalvikvm(5680): at java.lang.Thread.run(Thread.java:856)
07-12 18:01:06.055: D/skia(5680): --- decoder->decode returned false
07-12 18:01:06.095: E/ImageLoader(5680): null
07-12 18:01:06.095: E/ImageLoader(5680): java.lang.OutOfMemoryError
07-12 18:01:06.095: E/ImageLoader(5680): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-12 18:01:06.095: E/ImageLoader(5680): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
07-12 18:01:06.095: E/ImageLoader(5680): at com.nostra13.universalimageloader.core.ImageDecoder.decode(ImageDecoder.java:61)
07-12 18:01:06.095: E/ImageLoader(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.decodeWithOOMHandling(LoadAndDisplayImageTask.java:163)
07-12 18:01:06.095: E/ImageLoader(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.decodeImage(LoadAndDisplayImageTask.java:150)
07-12 18:01:06.095: E/ImageLoader(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.tryLoadBitmap(LoadAndDisplayImageTask.java:104)
07-12 18:01:06.095: E/ImageLoader(5680): at com.nostra13.universalimageloader.core.LoadAndDisplayImageTask.run(LoadAndDisplayImageTask.java:58)
07-12 18:01:06.095: E/ImageLoader(5680): at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442)
07-12 18:01:06.095: E/ImageLoader(5680): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
07-12 18:01:06.095: E/ImageLoader(5680): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
07-12 18:01:06.095: E/ImageLoader(5680): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
07-12 18:01:06.095: E/ImageLoader(5680): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
07-12 18:01:06.095: E/ImageLoader(5680): at java.lang.Thread.run(Thread.java:856)
07-12 18:01:06.180: D/dalvikvm(5680): GC_EXPLICIT freed 54K, 7% free 61470K/65479K, paused 3ms+16ms
How can I get out from this error ,
Please add an option for developer to choose whether to save or discard the same image in memory after reloading it from the file cache but in different size.
if i try get 106dip image,but server is not 106dip , i set config :
public static ImageLoaderConfiguration get106DipConfig(){
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(KShareApplication.getInstance())
.memoryCacheExtraOptions((int)(106 * UIUtil.getInstance().getDensity()),(int)(106 * UIUtil.getInstance().getDensity())) // Can slow ImageLoader, use it carefully (Better don't use it)
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 2)
.denyCacheImageMultipleSizesInMemory()
.offOutOfMemoryHandling()
.memoryCache( new FIFOLimitedMemoryCache(2 * 1024 * 1024)) // You can pass your own memory cache implementation
.discCache(new UnlimitedDiscCache(new File(CommonUtil.getCacheDir()))) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.enableLogging()
.build();
return config;
}
i find imageview setbitmap is not compress!! so if we layout hava many imageview , this layout's Mem hava many.
so i think maybe you fix it.
you can allow this method:
/**
* Utility function for decoding an image resource. The decoded bitmap will
* be optimized for further scaling to the requested destination dimensions
* and scaling logic.
*
* @param res The resources object containing the image data
* @param resId The resource id of the image data
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Decoded bitmap
*/
public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight,ScalingLogic scalingLogic) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);
return unscaledBitmap;
}
/**
*
* @param pathName 文件地址
* @param dstWidth 需要显示的宽度
* @param dstHeight 需要显示高度
* @param scalingLogic 缩放类型,crop等比缩放并裁剪,fit原图等比缩放填充
* @return Decoded bitmap
*/
public static Bitmap decodeFile(String pathName,int dstWidth,int dstHeight,ScalingLogic scalingLogic){
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeFile(pathName, options);
return unscaledBitmap;
}
public static Bitmap decodeFile(byte[] bs,int dstWidth,int dstHeight,ScalingLogic scalingLogic){
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bs, 0 , bs.length,options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeByteArray(bs, 0 , bs.length,options);
bs = null;
return unscaledBitmap;
}
/**
* Utility function for creating a scaled version of an existing bitmap
*
* @param unscaledBitmap Bitmap to scale
* @param dstWidth Wanted width of destination bitmap
* @param dstHeight Wanted height of destination bitmap
* @param scalingLogic Logic to use to avoid image stretching
* @return New scaled bitmap object
*/
public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
dstWidth, dstHeight, scalingLogic);
Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(),
dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(),
Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));
return scaledBitmap;
}
/**
* ScalingLogic defines how scaling should be carried out if source and
* destination image has different aspect ratio.
*
* CROP: Scales the image the minimum amount while making sure that at least
* one of the two dimensions fit inside the requested destination area.
* Parts of the source image will be cropped to realize this.
*
* FIT: Scales the image the minimum amount while making sure both
* dimensions fit inside the requested destination area. The resulting
* destination dimensions might be adjusted to a smaller size than
* requested.
*/
public static enum ScalingLogic {
CROP, FIT
}
/**
* Calculate optimal down-sampling factor given the dimensions of a source
* image, the dimensions of a destination area and a scaling logic.
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal down scaling sample size for decoding
*/
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
} else {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcHeight / dstHeight;
} else {
return srcWidth / dstWidth;
}
}
}
/**
* Calculates source rectangle for scaling bitmap
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal source rectangle
*/
public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.CROP) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
final int srcRectWidth = (int)(srcHeight * dstAspect);
final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
} else {
final int srcRectHeight = (int)(srcWidth / dstAspect);
final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2;
return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
}
} else {
return new Rect(0, 0, srcWidth, srcHeight);
}
}
/**
* Calculates destination rectangle for scaling bitmap
*
* @param srcWidth Width of source image
* @param srcHeight Height of source image
* @param dstWidth Width of destination area
* @param dstHeight Height of destination area
* @param scalingLogic Logic to use to avoid image stretching
* @return Optimal destination rectangle
*/
public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight,
ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect));
} else {
return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight);
}
} else {
return new Rect(0, 0, dstWidth, dstHeight);
}
}
if you can use,i think is very power!
use it :
1:decode
2:scale
Bitmap unscaledBitmap = decodeFile(bs, CommonUtil.dipToPixel(250), CommonUtil.dipToPixel(300), ScalingLogic.FIT);
Bitmap scaledBitmap = createScaledBitmap(unscaledBitmap, 250, 300, ScalingLogic.FIT);
unscaledBitmap.recycle();
if (scaledBitmap != null) {
imageView.setImageBitmap(scaledBitmap);
}
Using universal-image-loader-1.1.1.jar
Using android support package
Main activity is a FragmentActivity containing a ViewPager.
ViewPager contains Fragments. Fragment contains ListView.
When I attempt to use ImageLoader from the Fragment ListViews Adapter I receive the logged exception
ImageView context must be of Activity type
from
public class ImageLoader ...
private void tryRunOnUiThread(Runnable runnable) {
Context context = imageLoadingInfo.imageView.getContext();
if (context instanceof Activity) {
((Activity) context).runOnUiThread(runnable);
} else {
Log.e(TAG, ERROR_IMAGEVIEW_CONTEXT);
imageLoadingInfo.listener.onLoadingFailed(FailReason.WRONG_CONTEXT);
}
}
I would like to be able to associate an arbitrary object with a download request that gets passed to the listener callback methods. Here is why:
In my ListView, I use a ViewSwitcher to switch between an indeterminate progress bar and and ImageView. The idea is to display the progress bar whilst the image is downloading. To do this, I would like to pass the ViewSwitcher reference to the onLoadingStarted() and onLoadingComplete() to control whether the ProgressBar or ImageView is currently displayed.
I could imaging passing an object into the call to the displayImage() method that is then passed as an argument to the listener callbacks.
I would be willing to implement and submit a patch, although not too familiar with git operation...
Hi!
I have installed it, put the imageLoader in my Static class as a static property. Then i ran the init with the default config.
Now i have an adapter called twitterAdapter that loads twitters into a list.
I tried this (inside getView of my adapter class) to load the users image:
Image above is created like this:
final ImageView image = (ImageView) v.findViewById(R.id.avatar);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.build();
// Load and display image
StaticResources.imageLoader.displayImage(tweet.image_url, image, options, new ImageLoadingListener() {
public void onLoadingStarted() {
}
public void onLoadingFailed(FailReason failReason) {
}
public void onLoadingComplete(Bitmap loadedImage) {
notifyDataSetChanged();
}
public void onLoadingCancelled() {
}
});
The twitters show up in the list but the images never loads. Any ideas?
PhotosQueue#clean(): гораздо красивее и более читабельно писать такие шутки через iterator:
Iterator it = photosToLoad.iterator();
while ( it.hasNext() ) {
final ImageView imageView = it.next();
if ( imageView == imageForRemove ) {
it.remove();
}
}
В вашем случае, по-моему, возможно падение с ConcurrentModificationException
Do another image loading trying after OutOfMemory exception.
maybe it's more easier for gc to clear the bitmap, why not use softreference instead?
Image can't be loaded if image URL contains UTF-8 locale-specific symbols (e.g. 'ф', 'à', 'ß')
Consider screen orientation for cases when ImageView havn't specified size (width and height and maxWidth and maxHeight parameters).
I looked at the function saveImageOnDisc() and its usage in loadBitmap(). It looks like the file is always written to the cache, even if TotalSizeLimitedDiscCache is used. And files are never removed. Only the hashmap is updated. This is in line with what I experience looking at the cache directory.
Is this indeed a bug?
Greetings,
Michiel
E/libEGL(22343): call to OpenGL ES API with no current context (logged once per thread)
D/ShaderProgram(22343): couldn't load the vertex shader!
Hi, i allway see this line
Is it bug?
I have everything wired up pretty much like the samples, but when I call imageLoader.displayImage(), I get an immediate crash in ListView.layoutChildren().
I do not have the Android sources attached so I don't know specifically what it doesn't like.
I am debugging on a phone (Android 2.3.4) without external storage, so I tried removing "cacheOnDisc()" but that didn't make any difference.
This is inside a ListFragment inside a FragmentActivity, using the compat library. I am initializing my Adapter using "getActivity()".
Any suggestions on what I am doing wrong?
First of all thanks for sharing this nice stuff :)
I'm trying to implement the image loader into a gridview.
Therefore I need the images to be cropped to a aspect ratio of 1:1.
Is it possible to do this with your current state of code?
Provide possibility to choose image decoding parameter "sample size":
Display cached (on disc) images first. And then upload needed images from Internet.
My application consists of a grid view and a detail view. The thumbnails are loaded using this library into the thumbnail view, then when a grid item is clicked, a detail view is shown with the same image in a larger image view. Both images are retrieved from the same URL.
No matter the configuration options I try, the images load successfully in the grid view but never load in the larger detail view - onLoadingStarted()
is called, but no other callback is fired, not even onLoadingFailed(...)
.
My config:
config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .threadPoolSize(3) .threadPriority(Thread.NORM_PRIORITY - 2) .memoryCacheSize(1500000) // 1.5 Mb .discCacheFileNameGenerator(new Md5FileNameGenerator()) .enableLogging() // Not necessary in common .build();
My options:
options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.smile_placeholder) .showImageForEmptyUri(R.drawable.smile_placeholder) .cacheInMemory() .build();
I looked function "decodeImage" in ImageLoader class.
This function try clear memory in case OutOfMemoryError thrown.
But memory are not always cleared.
I have this messages in my logs:
05-03 08:38:13.202: INFO/dalvikvm-heap(462): Clamp target GC heap from 24.527MB to 24.000MB
05-03 08:38:15.931: ERROR/dalvikvm-heap(462): 816000-byte external allocation too large for this process.
05-03 08:38:16.011: INFO/dalvikvm-heap(462): Clamp target GC heap from 24.550MB to 24.000MB
05-03 08:38:16.011: ERROR/GraphicsJNI(462): VM won't let us allocate 816000 bytes
And new images are not shown. Are you use bitmap.recycle method?
I suggest to use recycle for all images in the memory cache. Such as in this solution http://blog.pseudoblue.com/2010/08/15/android-bitmaps-and-memory-leaks/
HashMap<Integer, Bitmap> mBitmaps;
public void recycleBitmaps() {
Iterator itr = mBitmaps.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry e = (Map.Entry)itr.next();
((Bitmap) e.getValue()).recycle();
}
mBitmaps.clear();
}
In a photo gallery application I am working on, I noticed that on images of a certain resolution (2272x1704) the application went 'out of memory'. The problem was that this particular size of images and the size of the display used (1280x800) caused the method computeImageScale() in the ImageDecoder class to return 1 instead of an appropriate scale factor. Changing the condition in the while loop of this method from AND to OR solved the problem.
Потенциальный NPE в методе get(): не сихронизованы методы clear() и get(). Более подробно: cointaisKey() возвращает true, вызывается метод clear(), get() от Map возвращает null.
Решение без synchronized: внутри метода Cache#get() использовать сразу Map#get() и проверять результат на null. В таком случае нужно поле Map нужно делать private и запрещать любые доступы к нему извне
For example fade in when load is complete.
in StorageUtils.java
public static File getOwnCacheDirectory(Context context, String cacheDir) {
File appCacheDir = null;
if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
}
if (!appCacheDir.mkdirs()) {
appCacheDir = context.getCacheDir();
}
return appCacheDir;
}
when i reinstall the apk, the disc cache already exists, and the appCacheDir.mkdirs() return false. so the disc cache directory change to context.getCacheDir();
i think there should be add the appCacheDir.exists() to judge
Hi again dear Nostra,
During configuration of ImageLoader, I found this problem.
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.imageDownloader(new DefaultImageDownloader(5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)
When I paste above line into my code, compiler cannot find the library of "DefaultImageDownloader". What is solution?
Also, there is same situation with
DisplayImageOptions options = new DisplayImageOptions.Builder()
.transform(matrix)
What is matrix?
Thanks Nostra,
Hi Nostra13,
Thanks for the very nice code you made.
I try to use it for loading images from the filesystem. I get an error. And loading fails. If I inspect the code, functions for loading from the (original) picture from the filesystem seem to be missing. Is that correct? Or is something else happening?
See below.
Michiel
I get the error:
03-29 09:41:01.230: E/ImageLoader(10716): java.lang.ClassCastException: org.apache.harmony.luni.internal.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection
It is sparked in the saveImageOnDisc function. First line:
HttpURLConnection conn = (HttpURLConnection) new URL(imageLoadingInfo.url).openConnection();
The function saveImageOnDisc is called from the loadBitmap function.
This functions checks if there is a cached image on the cache part of the filesystem. There isn't (which is correct.) Then it tries to load the image from the web. There I get the error (which is logical.)
I have a ListView with each row containing an ImageView. Not all of the rows have an associated URL for an image so in my adapter, if there is no URL, I assign a default image to the ImageView.
However, when flinging the list quickly such that ImageViews are recycled, I can end up with one of the rows that should have a default image being assigned a downloaded image because I cannot cancel a pending download. I think it goes like this:
I would like in step 2 to be able to indicate to the ImageLoader that the download associated with an ImageView should not be assigned to the ImageView when it completes please.
Nice library by the way!
It would be a very nice freature to can preload an image to memory cache. Very useful when scrolling in listviews.
All the code for loading the image woud be the same, but without putting it in the imageview.
When my Samsung Galexy S2 (2.3.6) is plugged into my computer via USB, the device is mounted as a storage device, then my app using the LimitedDiscCache (1.2.3) will NPE.
E/AndroidRuntime(7276): Caused by: java.lang.NullPointerException
E/AndroidRuntime(7276): at com.nostra13.universalimageloader.cache.disc.LimitedDiscCache.calculateCacheSizeAndFillUsageMap(LimitedDiscCache.java:41)
E/AndroidRuntime(7276): at com.nostra13.universalimageloader.cache.disc.LimitedDiscCache.<init>(LimitedDiscCache.java:35)
E/AndroidRuntime(7276): at com.nostra13.universalimageloader.cache.disc.impl.FileCountLimitedDiscCache.<init>(FileCountLimitedDiscCache.java:25)
E/AndroidRuntime(7276): at myapp.android.MyModule.configure(MyModule.java:38)
Workaround: Just unmount the device as USB storage.
I have seen that the image is decoded 2 times and read from disc unnecersarily. Also image is downloaded 2 times and overwritting the compressed one?
After compressing the compressed bitmap can be retrieved this way:
Bitmap original ...;
OutputStream discCacheOs = new BufferedOutputStream(new FileOutputStream(targetFile));
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(configuration.imageCompressFormatForDiscCache, configuration.imageQualityForDiscCache, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
then...
out.close();
original.recycle();
decoded.compress(configuration.imageCompressFormatForDiscCache,100, discCacheOs); (Or way you want)
After doing that, it is already on disc cache and you have the compressed bitmap on the var decoded.
Why is the input stream copyed again to de outputstream?
Also after saveImaseOnDisc the bitmap is decoded again unnecesarily.
private void saveImageOnDisc(File targetFile) throws MalformedURLException, IOException {
int width = configuration.maxImageWidthForDiscCache;
int height = configuration.maxImageHeightForDiscCache;
if (width > 0 || height > 0) {
// Download, decode, compress and save image
ImageSize targetImageSize = new ImageSize(width, height);
ImageDecoder decoder = new ImageDecoder(new URL(imageLoadingInfo.url), configuration.downloader, targetImageSize, ImageScaleType.EXACT);
Bitmap bmp = decoder.decode();
OutputStream os = new BufferedOutputStream(new FileOutputStream(targetFile));
boolean compressedSuccessfully = bmp.compress(configuration.imageCompressFormatForDiscCache, configuration.imageQualityForDiscCache, os);
if (compressedSuccessfully) {
bmp.recycle();
return;
}
}
(COMPRESSED BITMAP CAN BE RESTORED FROM OUTPUTSTREAM HERE)
// Download and save original image (FROM HERE IS ANYTHING NEEDED?)
InputStream is = configuration.downloader.getStream(new URL(imageLoadingInfo.url));
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(targetFile));
try {
FileUtils.copyStream(is, os);
} finally {
os.close();
}
} finally {
is.close();
}
}
Also when returning to Bitmap loadBitmap there is that code:
} else {
imageUrlForDecoding = new URL(imageLoadingInfo.url);
}
bitmap = decodeImage(imageUrlForDecoding); //(DECODED AGAIN)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.