问题描述: 我正在创建一个可滚动的文章列表,其中包含由我的SQLite数据库填充的缩略图。一般来说,除了缓慢之外,它“工作”:
图片加载非常缓慢...我想使用“Universal Image Loader”缓存了设备上的图像,如果您已经查看过这些图片,那么这会让它们看起来只是滚动到视图中(或者至少接近那个)。但是 - 当您向上/向下拖动时,没有任何图像存在,然后3-5秒后,图像开始弹出(好像它正在重新下载它们)
我正在改变动态缩略图框的可见性,但这样做完美无瑕 - 它们似乎没有改变 - 它们只是滚动到视图中,没有闪烁或任何东西。 (但随后图像不再出现几秒钟。)
我已经通过在滚动后删除我的PHP脚本进行测试...当我滚动回到prev spot时,图像没有显示 - 让我认为它是从我的PHP脚本每次加载。
但是根据the docs:“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;
}
更新 我的清单文件有:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
但我找到的缓存文件夹是完全空的:
mnt
-sdcard
-Android
-data
-com.mysite.news
-cache
-uil-images
答案 0 :(得分:19)
我在列表视图中遇到了与图像类似的问题。可能this answer将纠正您错误的图像问题。
我刚刚使用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
这表示任何时候都会有三个线程下载,最多2MB的图像。你下载的图片有多大?你还缓存到磁盘?如果是这样,那将会很慢。
要配置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上下载分叉版本的代码。
答案 1 :(得分:3)
另一种解决方案是来自点火开源项目的“RemoteImageView”。
实际上,RemoteImageView扩展了ImageView,并在幕后为您完成所有的抓取/缓存。
虽然它不一定能解决您列出的问题,但可能值得研究作为替代解决方案。
编辑:如果您仍然需要远程图像解决方案,我会强烈推荐Picasso。我用Picasso替换了我的应用程序中的RemoteImageView:http://square.github.io/picasso/答案 2 :(得分:1)
我怀疑 resize.php 很慢,特别是如果它必须调整大页面的大小,并且收到了几个请求。并且在某种程度上没有在imageLoader中进行缓存。
首先,我会在图像加载后完成其余的:字幕,描述和所有。 因为如果图像加载时间过长,则会产生更加即时的效果,如果描述和所有其他内容一起显示的话。通常你的陈述顺序很好。
@CameronLowellPallmer的答案负责切换图像和缓存。
答案 3 :(得分:0)
这堂课对我有用:
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);