Изображения, которые не кэшируются локально (с помощью Universal Image Loader) – медленное время загрузки изображения

Описание проблемы: Я создаю прокручиваемый список статей с эскизами, заполненными моей базой данных SQLite. В общем, он «работает», за исключением медленного:

Изображения загружаются очень медленно … Я думал, что использование « Универсального загрузчика изображений » кэширует изображения на устройстве, и это приведет к тому, что они будут отображаться только для прокрутки в виде просмотра, если вы уже просмотрели их (или, по крайней мере, близко к этому ). Но – когда вы перетаскиваете вверх / вниз, ни один из изображений не появляется, а через 3-5 секунд изображения начинают появляться (как если бы они перезагружали их)

Я меняю видимость эскизов на лету, но это работает безупречно – они, похоже, не меняются – они просто просматривают или не видят, не мигают или ничего. (Но тогда изображения не появляются еще несколько секунд).

Я протестировал, удалив свой php-скрипт после прокрутки … когда я прокручиваю назад к предыдущему пятну, изображения не отображаются – заставляя меня предположить, что это загрузка из моего PHP-скрипта КАЖДЫЙ раз.

Но согласно документам : «UsingFreqLimitedMemoryCache (наименее часто используемое растровое изображение удаляется при превышении предельного размера кеша) – используется по умолчанию"

Детали:

В моем ArticleEntryAdapter.js меня есть:

 @Override public View getView(final int position, final View convertView, final ViewGroup parent) { // We need to get the best view (re-used if possible) and then // retrieve its corresponding ViewHolder, which optimizes lookup efficiency final View view = getWorkingView(convertView); final ViewHolder viewHolder = getViewHolder(view); final Article article = getItem(position); // Set the title viewHolder.titleView.setText(article.title); //Set the subtitle (subhead) or description if(article.subtitle != null) { viewHolder.subTitleView.setText(article.subtitle); } else if(article.description != null) { viewHolder.subTitleView.setText(article.description); } ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.displayImage("", viewHolder.thumbView); //clears previous one if(article.filepath != null && article.filepath.length() != 0) { imageLoader.displayImage( "http://img.sltdb.com/processes/resize.php?image=" + article.filepath + "&size=100&quality=70", viewHolder.thumbView ); viewHolder.thumbView.setVisibility(View.VISIBLE); } else { viewHolder.thumbView.setVisibility(View.GONE); } return view; } 

Поскольку изображения неправильны – это не часто, но иногда во время прокрутки я вижу 2 одинаковых изображения, и когда я смотрю на статьи, они совсем не связаны (т. Е. Нет возможности фактически иметь То же изображение) Итак, я отскакиваю от него и обратно, и это уже не неправильный образ.

ПРИМЕЧАНИЕ. Я новичок в Java / Android – вы, вероятно, уже заметили это.

Больше кода для комментария-запроса:

 private View getWorkingView(final View convertView) { // The workingView is basically just the convertView re-used if possible // or inflated new if not possible View workingView = null; if(null == convertView) { final Context context = getContext(); final LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE); workingView = inflater.inflate(articleItemLayoutResource, null); } else { workingView = convertView; } return workingView; } 

UPDATE: в моем файле манифеста есть:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

Но папка кэша, которую я нашел, полностью пуста:

 mnt -sdcard -Android -data -com.mysite.news -cache -uil-images 

У меня возникли аналогичные проблемы с изображениями в режиме просмотра списка. Возможно, этот ответ исправит неправильную проблему с изображением.

Я просто загрузил образец проекта с помощью UniversalImageLoader, и он демонстрирует то же поведение, которое вы описываете.

Несколько заметок пока не просматривают исходный код.

 public static final int DEFAULT_THREAD_POOL_SIZE = 3; public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 1; public static final int DEFAULT_MEMORY_CACHE_SIZE = 2 * 1024 * 1024; // bytes 

Это говорит о том, что в любой момент будет загружено три потока и максимум 2 МБ изображений. Насколько велики изображения, которые вы загружаете? Также вы кэшируете диск? Если это так, это будет медленным.

Чтобы настроить некоторые основные параметры в ImageLoader, вам необходимо перейти к displayImage:

  DisplayImageOptions options = new DisplayImageOptions.Builder() .showStubImage(R.drawable.stub_image) .cacheInMemory() .cacheOnDisc() .build(); 

Я также хотел бы, чтобы вы попробовали следующие варианты:

 ImageLoaderConfiguration imageLoaderConfiguration = new ImageLoaderConfiguration.Builder(this) .enableLogging() .memoryCacheSize(41943040) .discCacheSize(104857600) .threadPoolSize(10) .build(); imageLoader = ImageLoader.getInstance(); imageLoader.init(imageLoaderConfiguration); 

При моем тестировании изображения находятся на диске, но загрузка по-прежнему медленная.

После обширного тестирования я решил, что основной проблемой является то, что UniversalImageLoader работает медленно. В частности, ImageLoader и LoadAndDisplayImageTask поддерживают работу. Я (очень быстро) переписал LoadAndDisplayImageTask как AsyncTask, и он сразу же стал лучше. Вы можете загрузить раздвоенную версию кода на GitHub.

Универсальный загрузчик изображений с помощью AsyncTasks

Альтернативным решением является «RemoteImageView» из проекта с открытым исходным кодом зажигания.

http://kaeppler.github.com/ignition-docs/ignition-core/apidocs/com/github/ignition/core/widgets/RemoteImageView.html

Эффективно RemoteImageView расширяет ImageView и делает все выборки / кэширование для вас за кулисами.

Хотя это не обязательно решает вашу проблему, которую вы указали, возможно, стоит изучить ее как альтернативное решение.

EDIT: Я бы очень рекомендовал Picasso, если вам все еще нужно решение для удаленного изображения. Я заменил RemoteImageView в своих приложениях на Picasso: http://square.github.io/picasso/

Я подозреваю, что resize.php медленный, особенно если он должен изменить размер больших страниц, и получено несколько запросов. И как-то кэширование в imageLoader не выполняется.

Сначала я сделал бы все остальное после загрузки изображения: субтитры, описание и все. Потому что, если загрузка изображения занимает слишком много времени, возникает более мгновенный эффект, если описание и все остальные отображаются вместе. Обычно ваш порядок выступлений в порядке.

Ответ @CameronLowellPallmer позаботится об переключенных изображениях и кешировании.

Этот класс работал для меня:

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.Log; import android.widget.ImageView; 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)&&url!=null) { //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()); BitmapFactory.Options bfOptions=new BitmapFactory.Options(); bfOptions.inDither=false; //Disable Dithering mode bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future bfOptions.inTempStorage=new byte[32 * 1024]; FileInputStream fs=null; if(bitmap == null){ //bitmap = BitmapFactory.decodeFile(f.getPath(),options); try { fs = new FileInputStream(f); if(fs!=null) bitmap=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions); } catch (IOException e) { //TODO do something intelligent e.printStackTrace(); } finally{ if(fs!=null) { try { fs.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } if(bitmap != null){ imageCache.put(f.getPath(), bitmap); } } //No? download it if(bitmap == null){ BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); imageView.setImageDrawable(downloadedDrawable); task.execute(url); }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 public 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/tac/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.BLACK); 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; } } 

Пример использования:

 downloader = new ImageDownloader(); ImageView image_profile =(ImageView) row.findViewById(R.id.image_profile); downloader.download(url, image_profile);