Commit 724a0c23ec7479d4d5bf59c47c52e68510d20b91

Authored by Penley
1 parent 3af87bc3

init uil

Showing 30 changed files with 4668 additions and 0 deletions

Too many changes to show.

To preserve performance only 30 of 30+ files are displayed.

  1 +/build
  2 +uil.iml
\ No newline at end of file
... ...
  1 +/*
  2 + * Copyright (c) 2016. wugian
  3 + * Licensed under the Apache License, Version 2.0 (the "License");
  4 + * you may not use this file except in compliance with the License.
  5 + * You may obtain a copy of the License at
  6 + *
  7 + * http://www.apache.org/licenses/LICENSE-2.0
  8 + *
  9 + * Unless required by applicable law or agreed to in writing, software
  10 + * distributed under the License is distributed on an "AS IS" BASIS,
  11 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + * See the License for the specific language governing permissions and
  13 + * limitations under the License.
  14 + *
  15 + */
  16 +
  17 +apply plugin: 'com.android.library'
  18 +
  19 +android {
  20 + compileSdkVersion 25
  21 + buildToolsVersion "25.0.0"
  22 +
  23 + defaultConfig {
  24 + minSdkVersion 18
  25 + targetSdkVersion 25
  26 + versionCode 1
  27 + versionName "1.0"
  28 +
  29 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  30 +
  31 + }
  32 + buildTypes {
  33 + release {
  34 + minifyEnabled false
  35 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  36 + }
  37 + }
  38 +}
  39 +
  40 +dependencies {
  41 + compile fileTree(dir: 'libs', include: ['*.jar'])
  42 +}
... ...
  1 +# Add project specific ProGuard rules here.
  2 +# By default, the flags in this file are appended to flags specified
  3 +# in D:\develope-file\android-sdk/tools/proguard/proguard-android.txt
  4 +# You can edit the include path and order by changing the proguardFiles
  5 +# directive in build.gradle.
  6 +#
  7 +# For more details, see
  8 +# http://developer.android.com/guide/developing/tools/proguard.html
  9 +
  10 +# Add any project specific keep options here:
  11 +
  12 +# If your project uses WebView with JS, uncomment the following
  13 +# and specify the fully qualified class name to the JavaScript interface
  14 +# class:
  15 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  16 +# public *;
  17 +#}
... ...
  1 +<!--
  2 + ~ Copyright (c) 2016. wugian
  3 + ~ Licensed under the Apache License, Version 2.0 (the "License");
  4 + ~ you may not use this file except in compliance with the License.
  5 + ~ You may obtain a copy of the License at
  6 + ~
  7 + ~ http://www.apache.org/licenses/LICENSE-2.0
  8 + ~
  9 + ~ Unless required by applicable law or agreed to in writing, software
  10 + ~ distributed under the License is distributed on an "AS IS" BASIS,
  11 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + ~ See the License for the specific language governing permissions and
  13 + ~ limitations under the License.
  14 + ~
  15 + -->
  16 +
  17 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  18 + package="com.nostra13.universalimageloader">
  19 +
  20 + <application android:allowBackup="true"
  21 + android:supportsRtl="true"
  22 + >
  23 +
  24 + </application>
  25 +
  26 +</manifest>
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.utils.IoUtils;
  20 +
  21 +import java.io.File;
  22 +import java.io.IOException;
  23 +import java.io.InputStream;
  24 +
  25 +/**
  26 + * Interface for disk cache
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @since 1.9.2
  30 + */
  31 +public interface DiskCache {
  32 + /**
  33 + * Returns root directory of disk cache
  34 + *
  35 + * @return Root directory of disk cache
  36 + */
  37 + File getDirectory();
  38 +
  39 + /**
  40 + * Returns file of cached image
  41 + *
  42 + * @param imageUri Original image URI
  43 + * @return File of cached image or <b>null</b> if image wasn't cached
  44 + */
  45 + File get(String imageUri);
  46 +
  47 + /**
  48 + * Saves image stream in disk cache.
  49 + * Incoming image stream shouldn't be closed in this method.
  50 + *
  51 + * @param imageUri Original image URI
  52 + * @param imageStream Input stream of image (shouldn't be closed in this method)
  53 + * @param listener Listener for saving progress, can be ignored if you don't use
  54 + * {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
  55 + * progress listener} in ImageLoader calls
  56 + * @return <b>true</b> - if image was saved successfully; <b>false</b> - if image wasn't saved in disk cache.
  57 + * @throws IOException
  58 + */
  59 + boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;
  60 +
  61 + /**
  62 + * Saves image bitmap in disk cache.
  63 + *
  64 + * @param imageUri Original image URI
  65 + * @param bitmap Image bitmap
  66 + * @return <b>true</b> - if bitmap was saved successfully; <b>false</b> - if bitmap wasn't saved in disk cache.
  67 + * @throws IOException
  68 + */
  69 + boolean save(String imageUri, Bitmap bitmap) throws IOException;
  70 +
  71 + /**
  72 + * Removes image file associated with incoming URI
  73 + *
  74 + * @param imageUri Image URI
  75 + * @return <b>true</b> - if image file is deleted successfully; <b>false</b> - if image file doesn't exist for
  76 + * incoming URI or image file can't be deleted.
  77 + */
  78 + boolean remove(String imageUri);
  79 +
  80 + /** Closes disk cache, releases resources. */
  81 + void close();
  82 +
  83 + /** Clears disk cache. */
  84 + void clear();
  85 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  20 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  21 +import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  22 +import com.nostra13.universalimageloader.utils.IoUtils;
  23 +
  24 +import java.io.BufferedOutputStream;
  25 +import java.io.File;
  26 +import java.io.FileOutputStream;
  27 +import java.io.IOException;
  28 +import java.io.InputStream;
  29 +import java.io.OutputStream;
  30 +
  31 +/**
  32 + * Base disk cache.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @see FileNameGenerator
  36 + * @since 1.0.0
  37 + */
  38 +public abstract class BaseDiskCache implements DiskCache {
  39 + /** {@value */
  40 + public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
  41 + /** {@value */
  42 + public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
  43 + /** {@value */
  44 + public static final int DEFAULT_COMPRESS_QUALITY = 100;
  45 +
  46 + private static final String ERROR_ARG_NULL = " argument must be not null";
  47 + private static final String TEMP_IMAGE_POSTFIX = ".tmp";
  48 +
  49 + protected final File cacheDir;
  50 + protected final File reserveCacheDir;
  51 +
  52 + protected final FileNameGenerator fileNameGenerator;
  53 +
  54 + protected int bufferSize = DEFAULT_BUFFER_SIZE;
  55 +
  56 + protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
  57 + protected int compressQuality = DEFAULT_COMPRESS_QUALITY;
  58 +
  59 + /** @param cacheDir Directory for file caching */
  60 + public BaseDiskCache(File cacheDir) {
  61 + this(cacheDir, null);
  62 + }
  63 +
  64 + /**
  65 + * @param cacheDir Directory for file caching
  66 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  67 + */
  68 + public BaseDiskCache(File cacheDir, File reserveCacheDir) {
  69 + this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
  70 + }
  71 +
  72 + /**
  73 + * @param cacheDir Directory for file caching
  74 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  75 + * @param fileNameGenerator {@linkplain FileNameGenerator
  76 + * Name generator} for cached files
  77 + */
  78 + public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
  79 + if (cacheDir == null) {
  80 + throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
  81 + }
  82 + if (fileNameGenerator == null) {
  83 + throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
  84 + }
  85 +
  86 + this.cacheDir = cacheDir;
  87 + this.reserveCacheDir = reserveCacheDir;
  88 + this.fileNameGenerator = fileNameGenerator;
  89 + }
  90 +
  91 + @Override
  92 + public File getDirectory() {
  93 + return cacheDir;
  94 + }
  95 +
  96 + @Override
  97 + public File get(String imageUri) {
  98 + return getFile(imageUri);
  99 + }
  100 +
  101 + @Override
  102 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  103 + File imageFile = getFile(imageUri);
  104 + File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
  105 + boolean loaded = false;
  106 + try {
  107 + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
  108 + try {
  109 + loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
  110 + } finally {
  111 + IoUtils.closeSilently(os);
  112 + }
  113 + } finally {
  114 + if (loaded && !tmpFile.renameTo(imageFile)) {
  115 + loaded = false;
  116 + }
  117 + if (!loaded) {
  118 + tmpFile.delete();
  119 + }
  120 + }
  121 + return loaded;
  122 + }
  123 +
  124 + @Override
  125 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  126 + File imageFile = getFile(imageUri);
  127 + File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
  128 + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
  129 + boolean savedSuccessfully = false;
  130 + try {
  131 + savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
  132 + } finally {
  133 + IoUtils.closeSilently(os);
  134 + if (savedSuccessfully && !tmpFile.renameTo(imageFile)) {
  135 + savedSuccessfully = false;
  136 + }
  137 + if (!savedSuccessfully) {
  138 + tmpFile.delete();
  139 + }
  140 + }
  141 + bitmap.recycle();
  142 + return savedSuccessfully;
  143 + }
  144 +
  145 + @Override
  146 + public boolean remove(String imageUri) {
  147 + return getFile(imageUri).delete();
  148 + }
  149 +
  150 + @Override
  151 + public void close() {
  152 + // Nothing to do
  153 + }
  154 +
  155 + @Override
  156 + public void clear() {
  157 + File[] files = cacheDir.listFiles();
  158 + if (files != null) {
  159 + for (File f : files) {
  160 + f.delete();
  161 + }
  162 + }
  163 + }
  164 +
  165 + /** Returns file object (not null) for incoming image URI. File object can reference to non-existing file. */
  166 + protected File getFile(String imageUri) {
  167 + String fileName = fileNameGenerator.generate(imageUri);
  168 + File dir = cacheDir;
  169 + if (!cacheDir.exists() && !cacheDir.mkdirs()) {
  170 + if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) {
  171 + dir = reserveCacheDir;
  172 + }
  173 + }
  174 + return new File(dir, fileName);
  175 + }
  176 +
  177 + public void setBufferSize(int bufferSize) {
  178 + this.bufferSize = bufferSize;
  179 + }
  180 +
  181 + public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
  182 + this.compressFormat = compressFormat;
  183 + }
  184 +
  185 + public void setCompressQuality(int compressQuality) {
  186 + this.compressQuality = compressQuality;
  187 + }
  188 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  20 +import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  21 +import com.nostra13.universalimageloader.utils.IoUtils;
  22 +
  23 +import java.io.File;
  24 +import java.io.IOException;
  25 +import java.io.InputStream;
  26 +import java.util.Collections;
  27 +import java.util.HashMap;
  28 +import java.util.Map;
  29 +
  30 +/**
  31 + * Cache which deletes files which were loaded more than defined time. Cache size is unlimited.
  32 + *
  33 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  34 + * @since 1.3.1
  35 + */
  36 +public class LimitedAgeDiskCache extends BaseDiskCache {
  37 +
  38 + private final long maxFileAge;
  39 +
  40 + private final Map<File, Long> loadingDates = Collections.synchronizedMap(new HashMap<File, Long>());
  41 +
  42 + /**
  43 + * @param cacheDir Directory for file caching
  44 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  45 + * treatment (and therefore be reloaded).
  46 + */
  47 + public LimitedAgeDiskCache(File cacheDir, long maxAge) {
  48 + this(cacheDir, null, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
  49 + }
  50 +
  51 + /**
  52 + * @param cacheDir Directory for file caching
  53 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  54 + * treatment (and therefore be reloaded).
  55 + */
  56 + public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, long maxAge) {
  57 + this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
  58 + }
  59 +
  60 + /**
  61 + * @param cacheDir Directory for file caching
  62 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  63 + * @param fileNameGenerator Name generator for cached files
  64 + * @param maxAge Max file age (in seconds). If file age will exceed this value then it'll be removed on next
  65 + * treatment (and therefore be reloaded).
  66 + */
  67 + public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long maxAge) {
  68 + super(cacheDir, reserveCacheDir, fileNameGenerator);
  69 + this.maxFileAge = maxAge * 1000; // to milliseconds
  70 + }
  71 +
  72 + @Override
  73 + public File get(String imageUri) {
  74 + File file = super.get(imageUri);
  75 + if (file != null && file.exists()) {
  76 + boolean cached;
  77 + Long loadingDate = loadingDates.get(file);
  78 + if (loadingDate == null) {
  79 + cached = false;
  80 + loadingDate = file.lastModified();
  81 + } else {
  82 + cached = true;
  83 + }
  84 +
  85 + if (System.currentTimeMillis() - loadingDate > maxFileAge) {
  86 + file.delete();
  87 + loadingDates.remove(file);
  88 + } else if (!cached) {
  89 + loadingDates.put(file, loadingDate);
  90 + }
  91 + }
  92 + return file;
  93 + }
  94 +
  95 + @Override
  96 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  97 + boolean saved = super.save(imageUri, imageStream, listener);
  98 + rememberUsage(imageUri);
  99 + return saved;
  100 + }
  101 +
  102 + @Override
  103 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  104 + boolean saved = super.save(imageUri, bitmap);
  105 + rememberUsage(imageUri);
  106 + return saved;
  107 + }
  108 +
  109 + @Override
  110 + public boolean remove(String imageUri) {
  111 + loadingDates.remove(getFile(imageUri));
  112 + return super.remove(imageUri);
  113 + }
  114 +
  115 + @Override
  116 + public void clear() {
  117 + super.clear();
  118 + loadingDates.clear();
  119 + }
  120 +
  121 + private void rememberUsage(String imageUri) {
  122 + File file = getFile(imageUri);
  123 + long currentTime = System.currentTimeMillis();
  124 + file.setLastModified(currentTime);
  125 + loadingDates.put(file, currentTime);
  126 + }
  127 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl;
  17 +
  18 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  19 +
  20 +import java.io.File;
  21 +
  22 +/**
  23 + * Default implementation of {@linkplain com.nostra13.universalimageloader.cache.disc.DiskCache disk cache}.
  24 + * Cache size is unlimited.
  25 + *
  26 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27 + * @since 1.0.0
  28 + */
  29 +public class UnlimitedDiskCache extends BaseDiskCache {
  30 + /** @param cacheDir Directory for file caching */
  31 + public UnlimitedDiskCache(File cacheDir) {
  32 + super(cacheDir);
  33 + }
  34 +
  35 + /**
  36 + * @param cacheDir Directory for file caching
  37 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  38 + */
  39 + public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
  40 + super(cacheDir, reserveCacheDir);
  41 + }
  42 +
  43 + /**
  44 + * @param cacheDir Directory for file caching
  45 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  46 + * @param fileNameGenerator {@linkplain FileNameGenerator
  47 + * Name generator} for cached files
  48 + */
  49 + public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
  50 + super(cacheDir, reserveCacheDir, fileNameGenerator);
  51 + }
  52 +}
... ...
  1 +/*
  2 + * Copyright (C) 2011 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.BufferedWriter;
  19 +import java.io.Closeable;
  20 +import java.io.EOFException;
  21 +import java.io.File;
  22 +import java.io.FileInputStream;
  23 +import java.io.FileNotFoundException;
  24 +import java.io.FileOutputStream;
  25 +import java.io.FilterOutputStream;
  26 +import java.io.IOException;
  27 +import java.io.InputStream;
  28 +import java.io.InputStreamReader;
  29 +import java.io.OutputStream;
  30 +import java.io.OutputStreamWriter;
  31 +import java.io.Writer;
  32 +import java.util.ArrayList;
  33 +import java.util.Iterator;
  34 +import java.util.LinkedHashMap;
  35 +import java.util.Map;
  36 +import java.util.concurrent.Callable;
  37 +import java.util.concurrent.LinkedBlockingQueue;
  38 +import java.util.concurrent.ThreadPoolExecutor;
  39 +import java.util.concurrent.TimeUnit;
  40 +import java.util.regex.Matcher;
  41 +import java.util.regex.Pattern;
  42 +
  43 +/**
  44 + * A cache that uses a bounded amount of space on a filesystem. Each cache
  45 + * entry has a string key and a fixed number of values. Each key must match
  46 + * the regex <strong>[a-z0-9_-]{1,64}</strong>. Values are byte sequences,
  47 + * accessible as streams or files. Each value must be between {@code 0} and
  48 + * {@code Integer.MAX_VALUE} bytes in length.
  49 + *
  50 + * <p>The cache stores its data in a directory on the filesystem. This
  51 + * directory must be exclusive to the cache; the cache may delete or overwrite
  52 + * files from its directory. It is an error for multiple processes to use the
  53 + * same cache directory at the same time.
  54 + *
  55 + * <p>This cache limits the number of bytes that it will store on the
  56 + * filesystem. When the number of stored bytes exceeds the limit, the cache will
  57 + * remove entries in the background until the limit is satisfied. The limit is
  58 + * not strict: the cache may temporarily exceed it while waiting for files to be
  59 + * deleted. The limit does not include filesystem overhead or the cache
  60 + * journal so space-sensitive applications should set a conservative limit.
  61 + *
  62 + * <p>Clients call {@link #edit} to create or update the values of an entry. An
  63 + * entry may have only one editor at one time; if a value is not available to be
  64 + * edited then {@link #edit} will return null.
  65 + * <ul>
  66 + * <li>When an entry is being <strong>created</strong> it is necessary to
  67 + * supply a full set of values; the empty value should be used as a
  68 + * placeholder if necessary.
  69 + * <li>When an entry is being <strong>edited</strong>, it is not necessary
  70 + * to supply data for every value; values default to their previous
  71 + * value.
  72 + * </ul>
  73 + * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
  74 + * or {@link Editor#abort}. Committing is atomic: a read observes the full set
  75 + * of values as they were before or after the commit, but never a mix of values.
  76 + *
  77 + * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
  78 + * observe the value at the time that {@link #get} was called. Updates and
  79 + * removals after the call do not impact ongoing reads.
  80 + *
  81 + * <p>This class is tolerant of some I/O errors. If files are missing from the
  82 + * filesystem, the corresponding entries will be dropped from the cache. If
  83 + * an error occurs while writing a cache value, the edit will fail silently.
  84 + * Callers should handle other problems by catching {@code IOException} and
  85 + * responding appropriately.
  86 + */
  87 +final class DiskLruCache implements Closeable {
  88 + static final String JOURNAL_FILE = "journal";
  89 + static final String JOURNAL_FILE_TEMP = "journal.tmp";
  90 + static final String JOURNAL_FILE_BACKUP = "journal.bkp";
  91 + static final String MAGIC = "libcore.io.DiskLruCache";
  92 + static final String VERSION_1 = "1";
  93 + static final long ANY_SEQUENCE_NUMBER = -1;
  94 + static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}");
  95 + private static final String CLEAN = "CLEAN";
  96 + private static final String DIRTY = "DIRTY";
  97 + private static final String REMOVE = "REMOVE";
  98 + private static final String READ = "READ";
  99 +
  100 + /*
  101 + * This cache uses a journal file named "journal". A typical journal file
  102 + * looks like this:
  103 + * libcore.io.DiskLruCache
  104 + * 1
  105 + * 100
  106 + * 2
  107 + *
  108 + * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
  109 + * DIRTY 335c4c6028171cfddfbaae1a9c313c52
  110 + * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
  111 + * REMOVE 335c4c6028171cfddfbaae1a9c313c52
  112 + * DIRTY 1ab96a171faeeee38496d8b330771a7a
  113 + * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
  114 + * READ 335c4c6028171cfddfbaae1a9c313c52
  115 + * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
  116 + *
  117 + * The first five lines of the journal form its header. They are the
  118 + * constant string "libcore.io.DiskLruCache", the disk cache's version,
  119 + * the application's version, the value count, and a blank line.
  120 + *
  121 + * Each of the subsequent lines in the file is a record of the state of a
  122 + * cache entry. Each line contains space-separated values: a state, a key,
  123 + * and optional state-specific values.
  124 + * o DIRTY lines track that an entry is actively being created or updated.
  125 + * Every successful DIRTY action should be followed by a CLEAN or REMOVE
  126 + * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
  127 + * temporary files may need to be deleted.
  128 + * o CLEAN lines track a cache entry that has been successfully published
  129 + * and may be read. A publish line is followed by the lengths of each of
  130 + * its values.
  131 + * o READ lines track accesses for LRU.
  132 + * o REMOVE lines track entries that have been deleted.
  133 + *
  134 + * The journal file is appended to as cache operations occur. The journal may
  135 + * occasionally be compacted by dropping redundant lines. A temporary file named
  136 + * "journal.tmp" will be used during compaction; that file should be deleted if
  137 + * it exists when the cache is opened.
  138 + */
  139 +
  140 + private final File directory;
  141 + private final File journalFile;
  142 + private final File journalFileTmp;
  143 + private final File journalFileBackup;
  144 + private final int appVersion;
  145 + private long maxSize;
  146 + private int maxFileCount;
  147 + private final int valueCount;
  148 + private long size = 0;
  149 + private int fileCount = 0;
  150 + private Writer journalWriter;
  151 + private final LinkedHashMap<String, Entry> lruEntries =
  152 + new LinkedHashMap<String, Entry>(0, 0.75f, true);
  153 + private int redundantOpCount;
  154 +
  155 + /**
  156 + * To differentiate between old and current snapshots, each entry is given
  157 + * a sequence number each time an edit is committed. A snapshot is stale if
  158 + * its sequence number is not equal to its entry's sequence number.
  159 + */
  160 + private long nextSequenceNumber = 0;
  161 +
  162 + /** This cache uses a single background thread to evict entries. */
  163 + final ThreadPoolExecutor executorService =
  164 + new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
  165 + private final Callable<Void> cleanupCallable = new Callable<Void>() {
  166 + public Void call() throws Exception {
  167 + synchronized (DiskLruCache.this) {
  168 + if (journalWriter == null) {
  169 + return null; // Closed.
  170 + }
  171 + trimToSize();
  172 + trimToFileCount();
  173 + if (journalRebuildRequired()) {
  174 + rebuildJournal();
  175 + redundantOpCount = 0;
  176 + }
  177 + }
  178 + return null;
  179 + }
  180 + };
  181 +
  182 + private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) {
  183 + this.directory = directory;
  184 + this.appVersion = appVersion;
  185 + this.journalFile = new File(directory, JOURNAL_FILE);
  186 + this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
  187 + this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
  188 + this.valueCount = valueCount;
  189 + this.maxSize = maxSize;
  190 + this.maxFileCount = maxFileCount;
  191 + }
  192 +
  193 + /**
  194 + * Opens the cache in {@code directory}, creating a cache if none exists
  195 + * there.
  196 + *
  197 + * @param directory a writable directory
  198 + * @param valueCount the number of values per cache entry. Must be positive.
  199 + * @param maxSize the maximum number of bytes this cache should use to store
  200 + * @param maxFileCount the maximum file count this cache should store
  201 + * @throws IOException if reading or writing the cache directory fails
  202 + */
  203 + public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount)
  204 + throws IOException {
  205 + if (maxSize <= 0) {
  206 + throw new IllegalArgumentException("maxSize <= 0");
  207 + }
  208 + if (maxFileCount <= 0) {
  209 + throw new IllegalArgumentException("maxFileCount <= 0");
  210 + }
  211 + if (valueCount <= 0) {
  212 + throw new IllegalArgumentException("valueCount <= 0");
  213 + }
  214 +
  215 + // If a bkp file exists, use it instead.
  216 + File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
  217 + if (backupFile.exists()) {
  218 + File journalFile = new File(directory, JOURNAL_FILE);
  219 + // If journal file also exists just delete backup file.
  220 + if (journalFile.exists()) {
  221 + backupFile.delete();
  222 + } else {
  223 + renameTo(backupFile, journalFile, false);
  224 + }
  225 + }
  226 +
  227 + // Prefer to pick up where we left off.
  228 + DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
  229 + if (cache.journalFile.exists()) {
  230 + try {
  231 + cache.readJournal();
  232 + cache.processJournal();
  233 + cache.journalWriter = new BufferedWriter(
  234 + new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII));
  235 + return cache;
  236 + } catch (IOException journalIsCorrupt) {
  237 + System.out
  238 + .println("DiskLruCache "
  239 + + directory
  240 + + " is corrupt: "
  241 + + journalIsCorrupt.getMessage()
  242 + + ", removing");
  243 + cache.delete();
  244 + }
  245 + }
  246 +
  247 + // Create a new empty cache.
  248 + directory.mkdirs();
  249 + cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount);
  250 + cache.rebuildJournal();
  251 + return cache;
  252 + }
  253 +
  254 + private void readJournal() throws IOException {
  255 + StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
  256 + try {
  257 + String magic = reader.readLine();
  258 + String version = reader.readLine();
  259 + String appVersionString = reader.readLine();
  260 + String valueCountString = reader.readLine();
  261 + String blank = reader.readLine();
  262 + if (!MAGIC.equals(magic)
  263 + || !VERSION_1.equals(version)
  264 + || !Integer.toString(appVersion).equals(appVersionString)
  265 + || !Integer.toString(valueCount).equals(valueCountString)
  266 + || !"".equals(blank)) {
  267 + throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
  268 + + valueCountString + ", " + blank + "]");
  269 + }
  270 +
  271 + int lineCount = 0;
  272 + while (true) {
  273 + try {
  274 + readJournalLine(reader.readLine());
  275 + lineCount++;
  276 + } catch (EOFException endOfJournal) {
  277 + break;
  278 + }
  279 + }
  280 + redundantOpCount = lineCount - lruEntries.size();
  281 + } finally {
  282 + Util.closeQuietly(reader);
  283 + }
  284 + }
  285 +
  286 + private void readJournalLine(String line) throws IOException {
  287 + int firstSpace = line.indexOf(' ');
  288 + if (firstSpace == -1) {
  289 + throw new IOException("unexpected journal line: " + line);
  290 + }
  291 +
  292 + int keyBegin = firstSpace + 1;
  293 + int secondSpace = line.indexOf(' ', keyBegin);
  294 + final String key;
  295 + if (secondSpace == -1) {
  296 + key = line.substring(keyBegin);
  297 + if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
  298 + lruEntries.remove(key);
  299 + return;
  300 + }
  301 + } else {
  302 + key = line.substring(keyBegin, secondSpace);
  303 + }
  304 +
  305 + Entry entry = lruEntries.get(key);
  306 + if (entry == null) {
  307 + entry = new Entry(key);
  308 + lruEntries.put(key, entry);
  309 + }
  310 +
  311 + if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
  312 + String[] parts = line.substring(secondSpace + 1).split(" ");
  313 + entry.readable = true;
  314 + entry.currentEditor = null;
  315 + entry.setLengths(parts);
  316 + } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
  317 + entry.currentEditor = new Editor(entry);
  318 + } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {
  319 + // This work was already done by calling lruEntries.get().
  320 + } else {
  321 + throw new IOException("unexpected journal line: " + line);
  322 + }
  323 + }
  324 +
  325 + /**
  326 + * Computes the initial size and collects garbage as a part of opening the
  327 + * cache. Dirty entries are assumed to be inconsistent and will be deleted.
  328 + */
  329 + private void processJournal() throws IOException {
  330 + deleteIfExists(journalFileTmp);
  331 + for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
  332 + Entry entry = i.next();
  333 + if (entry.currentEditor == null) {
  334 + for (int t = 0; t < valueCount; t++) {
  335 + size += entry.lengths[t];
  336 + fileCount++;
  337 + }
  338 + } else {
  339 + entry.currentEditor = null;
  340 + for (int t = 0; t < valueCount; t++) {
  341 + deleteIfExists(entry.getCleanFile(t));
  342 + deleteIfExists(entry.getDirtyFile(t));
  343 + }
  344 + i.remove();
  345 + }
  346 + }
  347 + }
  348 +
  349 + /**
  350 + * Creates a new journal that omits redundant information. This replaces the
  351 + * current journal if it exists.
  352 + */
  353 + private synchronized void rebuildJournal() throws IOException {
  354 + if (journalWriter != null) {
  355 + journalWriter.close();
  356 + }
  357 +
  358 + Writer writer = new BufferedWriter(
  359 + new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII));
  360 + try {
  361 + writer.write(MAGIC);
  362 + writer.write("\n");
  363 + writer.write(VERSION_1);
  364 + writer.write("\n");
  365 + writer.write(Integer.toString(appVersion));
  366 + writer.write("\n");
  367 + writer.write(Integer.toString(valueCount));
  368 + writer.write("\n");
  369 + writer.write("\n");
  370 +
  371 + for (Entry entry : lruEntries.values()) {
  372 + if (entry.currentEditor != null) {
  373 + writer.write(DIRTY + ' ' + entry.key + '\n');
  374 + } else {
  375 + writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
  376 + }
  377 + }
  378 + } finally {
  379 + writer.close();
  380 + }
  381 +
  382 + if (journalFile.exists()) {
  383 + renameTo(journalFile, journalFileBackup, true);
  384 + }
  385 + renameTo(journalFileTmp, journalFile, false);
  386 + journalFileBackup.delete();
  387 +
  388 + journalWriter = new BufferedWriter(
  389 + new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
  390 + }
  391 +
  392 + private static void deleteIfExists(File file) throws IOException {
  393 + if (file.exists() && !file.delete()) {
  394 + throw new IOException();
  395 + }
  396 + }
  397 +
  398 + private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {
  399 + if (deleteDestination) {
  400 + deleteIfExists(to);
  401 + }
  402 + if (!from.renameTo(to)) {
  403 + throw new IOException();
  404 + }
  405 + }
  406 +
  407 + /**
  408 + * Returns a snapshot of the entry named {@code key}, or null if it doesn't
  409 + * exist is not currently readable. If a value is returned, it is moved to
  410 + * the head of the LRU queue.
  411 + */
  412 + public synchronized Snapshot get(String key) throws IOException {
  413 + checkNotClosed();
  414 + validateKey(key);
  415 + Entry entry = lruEntries.get(key);
  416 + if (entry == null) {
  417 + return null;
  418 + }
  419 +
  420 + if (!entry.readable) {
  421 + return null;
  422 + }
  423 +
  424 + // Open all streams eagerly to guarantee that we see a single published
  425 + // snapshot. If we opened streams lazily then the streams could come
  426 + // from different edits.
  427 + File[] files = new File[valueCount];
  428 + InputStream[] ins = new InputStream[valueCount];
  429 + try {
  430 + File file;
  431 + for (int i = 0; i < valueCount; i++) {
  432 + file = entry.getCleanFile(i);
  433 + files[i] = file;
  434 + ins[i] = new FileInputStream(file);
  435 + }
  436 + } catch (FileNotFoundException e) {
  437 + // A file must have been deleted manually!
  438 + for (int i = 0; i < valueCount; i++) {
  439 + if (ins[i] != null) {
  440 + Util.closeQuietly(ins[i]);
  441 + } else {
  442 + break;
  443 + }
  444 + }
  445 + return null;
  446 + }
  447 +
  448 + redundantOpCount++;
  449 + journalWriter.append(READ + ' ' + key + '\n');
  450 + if (journalRebuildRequired()) {
  451 + executorService.submit(cleanupCallable);
  452 + }
  453 +
  454 + return new Snapshot(key, entry.sequenceNumber, files, ins, entry.lengths);
  455 + }
  456 +
  457 + /**
  458 + * Returns an editor for the entry named {@code key}, or null if another
  459 + * edit is in progress.
  460 + */
  461 + public Editor edit(String key) throws IOException {
  462 + return edit(key, ANY_SEQUENCE_NUMBER);
  463 + }
  464 +
  465 + private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
  466 + checkNotClosed();
  467 + validateKey(key);
  468 + Entry entry = lruEntries.get(key);
  469 + if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
  470 + || entry.sequenceNumber != expectedSequenceNumber)) {
  471 + return null; // Snapshot is stale.
  472 + }
  473 + if (entry == null) {
  474 + entry = new Entry(key);
  475 + lruEntries.put(key, entry);
  476 + } else if (entry.currentEditor != null) {
  477 + return null; // Another edit is in progress.
  478 + }
  479 +
  480 + Editor editor = new Editor(entry);
  481 + entry.currentEditor = editor;
  482 +
  483 + // Flush the journal before creating files to prevent file leaks.
  484 + journalWriter.write(DIRTY + ' ' + key + '\n');
  485 + journalWriter.flush();
  486 + return editor;
  487 + }
  488 +
  489 + /** Returns the directory where this cache stores its data. */
  490 + public File getDirectory() {
  491 + return directory;
  492 + }
  493 +
  494 + /**
  495 + * Returns the maximum number of bytes that this cache should use to store
  496 + * its data.
  497 + */
  498 + public synchronized long getMaxSize() {
  499 + return maxSize;
  500 + }
  501 +
  502 + /** Returns the maximum number of files that this cache should store */
  503 + public synchronized int getMaxFileCount() {
  504 + return maxFileCount;
  505 + }
  506 +
  507 + /**
  508 + * Changes the maximum number of bytes the cache can store and queues a job
  509 + * to trim the existing store, if necessary.
  510 + */
  511 + public synchronized void setMaxSize(long maxSize) {
  512 + this.maxSize = maxSize;
  513 + executorService.submit(cleanupCallable);
  514 + }
  515 +
  516 + /**
  517 + * Returns the number of bytes currently being used to store the values in
  518 + * this cache. This may be greater than the max size if a background
  519 + * deletion is pending.
  520 + */
  521 + public synchronized long size() {
  522 + return size;
  523 + }
  524 +
  525 + /**
  526 + * Returns the number of files currently being used to store the values in
  527 + * this cache. This may be greater than the max file count if a background
  528 + * deletion is pending.
  529 + */
  530 + public synchronized long fileCount() {
  531 + return fileCount;
  532 + }
  533 +
  534 + private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
  535 + Entry entry = editor.entry;
  536 + if (entry.currentEditor != editor) {
  537 + throw new IllegalStateException();
  538 + }
  539 +
  540 + // If this edit is creating the entry for the first time, every index must have a value.
  541 + if (success && !entry.readable) {
  542 + for (int i = 0; i < valueCount; i++) {
  543 + if (!editor.written[i]) {
  544 + editor.abort();
  545 + throw new IllegalStateException("Newly created entry didn't create value for index " + i);
  546 + }
  547 + if (!entry.getDirtyFile(i).exists()) {
  548 + editor.abort();
  549 + return;
  550 + }
  551 + }
  552 + }
  553 +
  554 + for (int i = 0; i < valueCount; i++) {
  555 + File dirty = entry.getDirtyFile(i);
  556 + if (success) {
  557 + if (dirty.exists()) {
  558 + File clean = entry.getCleanFile(i);
  559 + dirty.renameTo(clean);
  560 + long oldLength = entry.lengths[i];
  561 + long newLength = clean.length();
  562 + entry.lengths[i] = newLength;
  563 + size = size - oldLength + newLength;
  564 + fileCount++;
  565 + }
  566 + } else {
  567 + deleteIfExists(dirty);
  568 + }
  569 + }
  570 +
  571 + redundantOpCount++;
  572 + entry.currentEditor = null;
  573 + if (entry.readable | success) {
  574 + entry.readable = true;
  575 + journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
  576 + if (success) {
  577 + entry.sequenceNumber = nextSequenceNumber++;
  578 + }
  579 + } else {
  580 + lruEntries.remove(entry.key);
  581 + journalWriter.write(REMOVE + ' ' + entry.key + '\n');
  582 + }
  583 + journalWriter.flush();
  584 +
  585 + if (size > maxSize || fileCount > maxFileCount || journalRebuildRequired()) {
  586 + executorService.submit(cleanupCallable);
  587 + }
  588 + }
  589 +
  590 + /**
  591 + * We only rebuild the journal when it will halve the size of the journal
  592 + * and eliminate at least 2000 ops.
  593 + */
  594 + private boolean journalRebuildRequired() {
  595 + final int redundantOpCompactThreshold = 2000;
  596 + return redundantOpCount >= redundantOpCompactThreshold //
  597 + && redundantOpCount >= lruEntries.size();
  598 + }
  599 +
  600 + /**
  601 + * Drops the entry for {@code key} if it exists and can be removed. Entries
  602 + * actively being edited cannot be removed.
  603 + *
  604 + * @return true if an entry was removed.
  605 + */
  606 + public synchronized boolean remove(String key) throws IOException {
  607 + checkNotClosed();
  608 + validateKey(key);
  609 + Entry entry = lruEntries.get(key);
  610 + if (entry == null || entry.currentEditor != null) {
  611 + return false;
  612 + }
  613 +
  614 + for (int i = 0; i < valueCount; i++) {
  615 + File file = entry.getCleanFile(i);
  616 + if (file.exists() && !file.delete()) {
  617 + throw new IOException("failed to delete " + file);
  618 + }
  619 + size -= entry.lengths[i];
  620 + fileCount--;
  621 + entry.lengths[i] = 0;
  622 + }
  623 +
  624 + redundantOpCount++;
  625 + journalWriter.append(REMOVE + ' ' + key + '\n');
  626 + lruEntries.remove(key);
  627 +
  628 + if (journalRebuildRequired()) {
  629 + executorService.submit(cleanupCallable);
  630 + }
  631 +
  632 + return true;
  633 + }
  634 +
  635 + /** Returns true if this cache has been closed. */
  636 + public synchronized boolean isClosed() {
  637 + return journalWriter == null;
  638 + }
  639 +
  640 + private void checkNotClosed() {
  641 + if (journalWriter == null) {
  642 + throw new IllegalStateException("cache is closed");
  643 + }
  644 + }
  645 +
  646 + /** Force buffered operations to the filesystem. */
  647 + public synchronized void flush() throws IOException {
  648 + checkNotClosed();
  649 + trimToSize();
  650 + trimToFileCount();
  651 + journalWriter.flush();
  652 + }
  653 +
  654 + /** Closes this cache. Stored values will remain on the filesystem. */
  655 + public synchronized void close() throws IOException {
  656 + if (journalWriter == null) {
  657 + return; // Already closed.
  658 + }
  659 + for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
  660 + if (entry.currentEditor != null) {
  661 + entry.currentEditor.abort();
  662 + }
  663 + }
  664 + trimToSize();
  665 + trimToFileCount();
  666 + journalWriter.close();
  667 + journalWriter = null;
  668 + }
  669 +
  670 + private void trimToSize() throws IOException {
  671 + while (size > maxSize) {
  672 + Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
  673 + remove(toEvict.getKey());
  674 + }
  675 + }
  676 +
  677 + private void trimToFileCount() throws IOException {
  678 + while (fileCount > maxFileCount) {
  679 + Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next();
  680 + remove(toEvict.getKey());
  681 + }
  682 + }
  683 +
  684 + /**
  685 + * Closes the cache and deletes all of its stored values. This will delete
  686 + * all files in the cache directory including files that weren't created by
  687 + * the cache.
  688 + */
  689 + public void delete() throws IOException {
  690 + close();
  691 + Util.deleteContents(directory);
  692 + }
  693 +
  694 + private void validateKey(String key) {
  695 + Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
  696 + if (!matcher.matches()) {
  697 + throw new IllegalArgumentException("keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\"");
  698 + }
  699 + }
  700 +
  701 + private static String inputStreamToString(InputStream in) throws IOException {
  702 + return Util.readFully(new InputStreamReader(in, Util.UTF_8));
  703 + }
  704 +
  705 + /** A snapshot of the values for an entry. */
  706 + public final class Snapshot implements Closeable {
  707 + private final String key;
  708 + private final long sequenceNumber;
  709 + private File[] files;
  710 + private final InputStream[] ins;
  711 + private final long[] lengths;
  712 +
  713 + private Snapshot(String key, long sequenceNumber, File[] files, InputStream[] ins, long[] lengths) {
  714 + this.key = key;
  715 + this.sequenceNumber = sequenceNumber;
  716 + this.files = files;
  717 + this.ins = ins;
  718 + this.lengths = lengths;
  719 + }
  720 +
  721 + /**
  722 + * Returns an editor for this snapshot's entry, or null if either the
  723 + * entry has changed since this snapshot was created or if another edit
  724 + * is in progress.
  725 + */
  726 + public Editor edit() throws IOException {
  727 + return DiskLruCache.this.edit(key, sequenceNumber);
  728 + }
  729 +
  730 + /** Returns file with the value for {@code index}. */
  731 + public File getFile(int index) {
  732 + return files[index];
  733 + }
  734 +
  735 + /** Returns the unbuffered stream with the value for {@code index}. */
  736 + public InputStream getInputStream(int index) {
  737 + return ins[index];
  738 + }
  739 +
  740 + /** Returns the string value for {@code index}. */
  741 + public String getString(int index) throws IOException {
  742 + return inputStreamToString(getInputStream(index));
  743 + }
  744 +
  745 + /** Returns the byte length of the value for {@code index}. */
  746 + public long getLength(int index) {
  747 + return lengths[index];
  748 + }
  749 +
  750 + public void close() {
  751 + for (InputStream in : ins) {
  752 + Util.closeQuietly(in);
  753 + }
  754 + }
  755 + }
  756 +
  757 + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
  758 + @Override
  759 + public void write(int b) throws IOException {
  760 + // Eat all writes silently. Nom nom.
  761 + }
  762 + };
  763 +
  764 + /** Edits the values for an entry. */
  765 + public final class Editor {
  766 + private final Entry entry;
  767 + private final boolean[] written;
  768 + private boolean hasErrors;
  769 + private boolean committed;
  770 +
  771 + private Editor(Entry entry) {
  772 + this.entry = entry;
  773 + this.written = (entry.readable) ? null : new boolean[valueCount];
  774 + }
  775 +
  776 + /**
  777 + * Returns an unbuffered input stream to read the last committed value,
  778 + * or null if no value has been committed.
  779 + */
  780 + public InputStream newInputStream(int index) throws IOException {
  781 + synchronized (DiskLruCache.this) {
  782 + if (entry.currentEditor != this) {
  783 + throw new IllegalStateException();
  784 + }
  785 + if (!entry.readable) {
  786 + return null;
  787 + }
  788 + try {
  789 + return new FileInputStream(entry.getCleanFile(index));
  790 + } catch (FileNotFoundException e) {
  791 + return null;
  792 + }
  793 + }
  794 + }
  795 +
  796 + /**
  797 + * Returns the last committed value as a string, or null if no value
  798 + * has been committed.
  799 + */
  800 + public String getString(int index) throws IOException {
  801 + InputStream in = newInputStream(index);
  802 + return in != null ? inputStreamToString(in) : null;
  803 + }
  804 +
  805 + /**
  806 + * Returns a new unbuffered output stream to write the value at
  807 + * {@code index}. If the underlying output stream encounters errors
  808 + * when writing to the filesystem, this edit will be aborted when
  809 + * {@link #commit} is called. The returned output stream does not throw
  810 + * IOExceptions.
  811 + */
  812 + public OutputStream newOutputStream(int index) throws IOException {
  813 + synchronized (DiskLruCache.this) {
  814 + if (entry.currentEditor != this) {
  815 + throw new IllegalStateException();
  816 + }
  817 + if (!entry.readable) {
  818 + written[index] = true;
  819 + }
  820 + File dirtyFile = entry.getDirtyFile(index);
  821 + FileOutputStream outputStream;
  822 + try {
  823 + outputStream = new FileOutputStream(dirtyFile);
  824 + } catch (FileNotFoundException e) {
  825 + // Attempt to recreate the cache directory.
  826 + directory.mkdirs();
  827 + try {
  828 + outputStream = new FileOutputStream(dirtyFile);
  829 + } catch (FileNotFoundException e2) {
  830 + // We are unable to recover. Silently eat the writes.
  831 + return NULL_OUTPUT_STREAM;
  832 + }
  833 + }
  834 + return new FaultHidingOutputStream(outputStream);
  835 + }
  836 + }
  837 +
  838 + /** Sets the value at {@code index} to {@code value}. */
  839 + public void set(int index, String value) throws IOException {
  840 + Writer writer = null;
  841 + try {
  842 + writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8);
  843 + writer.write(value);
  844 + } finally {
  845 + Util.closeQuietly(writer);
  846 + }
  847 + }
  848 +
  849 + /**
  850 + * Commits this edit so it is visible to readers. This releases the
  851 + * edit lock so another edit may be started on the same key.
  852 + */
  853 + public void commit() throws IOException {
  854 + if (hasErrors) {
  855 + completeEdit(this, false);
  856 + remove(entry.key); // The previous entry is stale.
  857 + } else {
  858 + completeEdit(this, true);
  859 + }
  860 + committed = true;
  861 + }
  862 +
  863 + /**
  864 + * Aborts this edit. This releases the edit lock so another edit may be
  865 + * started on the same key.
  866 + */
  867 + public void abort() throws IOException {
  868 + completeEdit(this, false);
  869 + }
  870 +
  871 + public void abortUnlessCommitted() {
  872 + if (!committed) {
  873 + try {
  874 + abort();
  875 + } catch (IOException ignored) {
  876 + }
  877 + }
  878 + }
  879 +
  880 + private class FaultHidingOutputStream extends FilterOutputStream {
  881 + private FaultHidingOutputStream(OutputStream out) {
  882 + super(out);
  883 + }
  884 +
  885 + @Override public void write(int oneByte) {
  886 + try {
  887 + out.write(oneByte);
  888 + } catch (IOException e) {
  889 + hasErrors = true;
  890 + }
  891 + }
  892 +
  893 + @Override public void write(byte[] buffer, int offset, int length) {
  894 + try {
  895 + out.write(buffer, offset, length);
  896 + } catch (IOException e) {
  897 + hasErrors = true;
  898 + }
  899 + }
  900 +
  901 + @Override public void close() {
  902 + try {
  903 + out.close();
  904 + } catch (IOException e) {
  905 + hasErrors = true;
  906 + }
  907 + }
  908 +
  909 + @Override public void flush() {
  910 + try {
  911 + out.flush();
  912 + } catch (IOException e) {
  913 + hasErrors = true;
  914 + }
  915 + }
  916 + }
  917 + }
  918 +
  919 + private final class Entry {
  920 + private final String key;
  921 +
  922 + /** Lengths of this entry's files. */
  923 + private final long[] lengths;
  924 +
  925 + /** True if this entry has ever been published. */
  926 + private boolean readable;
  927 +
  928 + /** The ongoing edit or null if this entry is not being edited. */
  929 + private Editor currentEditor;
  930 +
  931 + /** The sequence number of the most recently committed edit to this entry. */
  932 + private long sequenceNumber;
  933 +
  934 + private Entry(String key) {
  935 + this.key = key;
  936 + this.lengths = new long[valueCount];
  937 + }
  938 +
  939 + public String getLengths() throws IOException {
  940 + StringBuilder result = new StringBuilder();
  941 + for (long size : lengths) {
  942 + result.append(' ').append(size);
  943 + }
  944 + return result.toString();
  945 + }
  946 +
  947 + /** Set lengths using decimal numbers like "10123". */
  948 + private void setLengths(String[] strings) throws IOException {
  949 + if (strings.length != valueCount) {
  950 + throw invalidLengths(strings);
  951 + }
  952 +
  953 + try {
  954 + for (int i = 0; i < strings.length; i++) {
  955 + lengths[i] = Long.parseLong(strings[i]);
  956 + }
  957 + } catch (NumberFormatException e) {
  958 + throw invalidLengths(strings);
  959 + }
  960 + }
  961 +
  962 + private IOException invalidLengths(String[] strings) throws IOException {
  963 + throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings));
  964 + }
  965 +
  966 + public File getCleanFile(int i) {
  967 + return new File(directory, key + "." + i);
  968 + }
  969 +
  970 + public File getDirtyFile(int i) {
  971 + return new File(directory, key + "." + i + ".tmp");
  972 + }
  973 + }
  974 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  20 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  21 +import com.nostra13.universalimageloader.utils.IoUtils;
  22 +import com.nostra13.universalimageloader.utils.L;
  23 +
  24 +import java.io.BufferedOutputStream;
  25 +import java.io.File;
  26 +import java.io.IOException;
  27 +import java.io.InputStream;
  28 +import java.io.OutputStream;
  29 +
  30 +/**
  31 + * Disk cache based on "Least-Recently Used" principle. Adapter pattern, adapts
  32 + * {@link DiskLruCache DiskLruCache} to
  33 + * {@link DiskCache DiskCache}
  34 + *
  35 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  36 + * @see FileNameGenerator
  37 + * @since 1.9.2
  38 + */
  39 +public class LruDiskCache implements DiskCache {
  40 + /** {@value */
  41 + public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
  42 + /** {@value */
  43 + public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
  44 + /** {@value */
  45 + public static final int DEFAULT_COMPRESS_QUALITY = 100;
  46 +
  47 + private static final String ERROR_ARG_NULL = " argument must be not null";
  48 + private static final String ERROR_ARG_NEGATIVE = " argument must be positive number";
  49 +
  50 + protected DiskLruCache cache;
  51 + private File reserveCacheDir;
  52 +
  53 + protected final FileNameGenerator fileNameGenerator;
  54 +
  55 + protected int bufferSize = DEFAULT_BUFFER_SIZE;
  56 +
  57 + protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
  58 + protected int compressQuality = DEFAULT_COMPRESS_QUALITY;
  59 +
  60 + /**
  61 + * @param cacheDir Directory for file caching
  62 + * @param fileNameGenerator {@linkplain FileNameGenerator
  63 + * Name generator} for cached files. Generated names must match the regex
  64 + * <strong>[a-z0-9_-]{1,64}</strong>
  65 + * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited.
  66 + * @throws IOException if cache can't be initialized (e.g. "No space left on device")
  67 + */
  68 + public LruDiskCache(File cacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize) throws IOException {
  69 + this(cacheDir, null, fileNameGenerator, cacheMaxSize, 0);
  70 + }
  71 +
  72 + /**
  73 + * @param cacheDir Directory for file caching
  74 + * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available.
  75 + * @param fileNameGenerator {@linkplain FileNameGenerator
  76 + * Name generator} for cached files. Generated names must match the regex
  77 + * <strong>[a-z0-9_-]{1,64}</strong>
  78 + * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited.
  79 + * @param cacheMaxFileCount Max file count in cache. <b>0</b> means file count is unlimited.
  80 + * @throws IOException if cache can't be initialized (e.g. "No space left on device")
  81 + */
  82 + public LruDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize,
  83 + int cacheMaxFileCount) throws IOException {
  84 + if (cacheDir == null) {
  85 + throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
  86 + }
  87 + if (cacheMaxSize < 0) {
  88 + throw new IllegalArgumentException("cacheMaxSize" + ERROR_ARG_NEGATIVE);
  89 + }
  90 + if (cacheMaxFileCount < 0) {
  91 + throw new IllegalArgumentException("cacheMaxFileCount" + ERROR_ARG_NEGATIVE);
  92 + }
  93 + if (fileNameGenerator == null) {
  94 + throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
  95 + }
  96 +
  97 + if (cacheMaxSize == 0) {
  98 + cacheMaxSize = Long.MAX_VALUE;
  99 + }
  100 + if (cacheMaxFileCount == 0) {
  101 + cacheMaxFileCount = Integer.MAX_VALUE;
  102 + }
  103 +
  104 + this.reserveCacheDir = reserveCacheDir;
  105 + this.fileNameGenerator = fileNameGenerator;
  106 + initCache(cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount);
  107 + }
  108 +
  109 + private void initCache(File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount)
  110 + throws IOException {
  111 + try {
  112 + cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);
  113 + } catch (IOException e) {
  114 + L.e(e);
  115 + if (reserveCacheDir != null) {
  116 + initCache(reserveCacheDir, null, cacheMaxSize, cacheMaxFileCount);
  117 + }
  118 + if (cache == null) {
  119 + throw e; //new RuntimeException("Can't initialize disk cache", e);
  120 + }
  121 + }
  122 + }
  123 +
  124 + @Override
  125 + public File getDirectory() {
  126 + return cache.getDirectory();
  127 + }
  128 +
  129 + @Override
  130 + public File get(String imageUri) {
  131 + DiskLruCache.Snapshot snapshot = null;
  132 + try {
  133 + snapshot = cache.get(getKey(imageUri));
  134 + return snapshot == null ? null : snapshot.getFile(0);
  135 + } catch (IOException e) {
  136 + L.e(e);
  137 + return null;
  138 + } finally {
  139 + if (snapshot != null) {
  140 + snapshot.close();
  141 + }
  142 + }
  143 + }
  144 +
  145 + @Override
  146 + public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
  147 + DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
  148 + if (editor == null) {
  149 + return false;
  150 + }
  151 +
  152 + OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
  153 + boolean copied = false;
  154 + try {
  155 + copied = IoUtils.copyStream(imageStream, os, listener, bufferSize);
  156 + } finally {
  157 + IoUtils.closeSilently(os);
  158 + if (copied) {
  159 + editor.commit();
  160 + } else {
  161 + editor.abort();
  162 + }
  163 + }
  164 + return copied;
  165 + }
  166 +
  167 + @Override
  168 + public boolean save(String imageUri, Bitmap bitmap) throws IOException {
  169 + DiskLruCache.Editor editor = cache.edit(getKey(imageUri));
  170 + if (editor == null) {
  171 + return false;
  172 + }
  173 +
  174 + OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize);
  175 + boolean savedSuccessfully = false;
  176 + try {
  177 + savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
  178 + } finally {
  179 + IoUtils.closeSilently(os);
  180 + }
  181 + if (savedSuccessfully) {
  182 + editor.commit();
  183 + } else {
  184 + editor.abort();
  185 + }
  186 + return savedSuccessfully;
  187 + }
  188 +
  189 + @Override
  190 + public boolean remove(String imageUri) {
  191 + try {
  192 + return cache.remove(getKey(imageUri));
  193 + } catch (IOException e) {
  194 + L.e(e);
  195 + return false;
  196 + }
  197 + }
  198 +
  199 + @Override
  200 + public void close() {
  201 + try {
  202 + cache.close();
  203 + } catch (IOException e) {
  204 + L.e(e);
  205 + }
  206 + cache = null;
  207 + }
  208 +
  209 + @Override
  210 + public void clear() {
  211 + try {
  212 + cache.delete();
  213 + } catch (IOException e) {
  214 + L.e(e);
  215 + }
  216 + try {
  217 + initCache(cache.getDirectory(), reserveCacheDir, cache.getMaxSize(), cache.getMaxFileCount());
  218 + } catch (IOException e) {
  219 + L.e(e);
  220 + }
  221 + }
  222 +
  223 + private String getKey(String imageUri) {
  224 + return fileNameGenerator.generate(imageUri);
  225 + }
  226 +
  227 + public void setBufferSize(int bufferSize) {
  228 + this.bufferSize = bufferSize;
  229 + }
  230 +
  231 + public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
  232 + this.compressFormat = compressFormat;
  233 + }
  234 +
  235 + public void setCompressQuality(int compressQuality) {
  236 + this.compressQuality = compressQuality;
  237 + }
  238 +}
... ...
  1 +/*
  2 + * Copyright (C) 2012 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.ByteArrayOutputStream;
  19 +import java.io.Closeable;
  20 +import java.io.EOFException;
  21 +import java.io.IOException;
  22 +import java.io.InputStream;
  23 +import java.io.UnsupportedEncodingException;
  24 +import java.nio.charset.Charset;
  25 +
  26 +/**
  27 + * Buffers input from an {@link InputStream} for reading lines.
  28 + *
  29 + * <p>This class is used for buffered reading of lines. For purposes of this class, a line ends
  30 + * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
  31 + * line at end of input is invalid and will be ignored, the caller may use {@code
  32 + * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
  33 + *
  34 + * <p>This class is intended for reading input that strictly consists of lines, such as line-based
  35 + * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
  36 + * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
  37 + * end-of-input reporting and a more restrictive definition of a line.
  38 + *
  39 + * <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
  40 + * and 10, respectively, and the representation of no other character contains these values.
  41 + * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
  42 + * The default charset is US_ASCII.
  43 + */
  44 +class StrictLineReader implements Closeable {
  45 + private static final byte CR = (byte) '\r';
  46 + private static final byte LF = (byte) '\n';
  47 +
  48 + private final InputStream in;
  49 + private final Charset charset;
  50 +
  51 + /*
  52 + * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
  53 + * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
  54 + * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
  55 + * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
  56 + */
  57 + private byte[] buf;
  58 + private int pos;
  59 + private int end;
  60 +
  61 + /**
  62 + * Constructs a new {@code LineReader} with the specified charset and the default capacity.
  63 + *
  64 + * @param in the {@code InputStream} to read data from.
  65 + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
  66 + * supported.
  67 + * @throws NullPointerException if {@code in} or {@code charset} is null.
  68 + * @throws IllegalArgumentException if the specified charset is not supported.
  69 + */
  70 + public StrictLineReader(InputStream in, Charset charset) {
  71 + this(in, 8192, charset);
  72 + }
  73 +
  74 + /**
  75 + * Constructs a new {@code LineReader} with the specified capacity and charset.
  76 + *
  77 + * @param in the {@code InputStream} to read data from.
  78 + * @param capacity the capacity of the buffer.
  79 + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
  80 + * supported.
  81 + * @throws NullPointerException if {@code in} or {@code charset} is null.
  82 + * @throws IllegalArgumentException if {@code capacity} is negative or zero
  83 + * or the specified charset is not supported.
  84 + */
  85 + public StrictLineReader(InputStream in, int capacity, Charset charset) {
  86 + if (in == null || charset == null) {
  87 + throw new NullPointerException();
  88 + }
  89 + if (capacity < 0) {
  90 + throw new IllegalArgumentException("capacity <= 0");
  91 + }
  92 + if (!(charset.equals(Util.US_ASCII))) {
  93 + throw new IllegalArgumentException("Unsupported encoding");
  94 + }
  95 +
  96 + this.in = in;
  97 + this.charset = charset;
  98 + buf = new byte[capacity];
  99 + }
  100 +
  101 + /**
  102 + * Closes the reader by closing the underlying {@code InputStream} and
  103 + * marking this reader as closed.
  104 + *
  105 + * @throws IOException for errors when closing the underlying {@code InputStream}.
  106 + */
  107 + public void close() throws IOException {
  108 + synchronized (in) {
  109 + if (buf != null) {
  110 + buf = null;
  111 + in.close();
  112 + }
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
  118 + * this end of line marker is not included in the result.
  119 + *
  120 + * @return the next line from the input.
  121 + * @throws IOException for underlying {@code InputStream} errors.
  122 + * @throws EOFException for the end of source stream.
  123 + */
  124 + public String readLine() throws IOException {
  125 + synchronized (in) {
  126 + if (buf == null) {
  127 + throw new IOException("LineReader is closed");
  128 + }
  129 +
  130 + // Read more data if we are at the end of the buffered data.
  131 + // Though it's an error to read after an exception, we will let {@code fillBuf()}
  132 + // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
  133 + if (pos >= end) {
  134 + fillBuf();
  135 + }
  136 + // Try to find LF in the buffered data and return the line if successful.
  137 + for (int i = pos; i != end; ++i) {
  138 + if (buf[i] == LF) {
  139 + int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
  140 + String res = new String(buf, pos, lineEnd - pos, charset.name());
  141 + pos = i + 1;
  142 + return res;
  143 + }
  144 + }
  145 +
  146 + // Let's anticipate up to 80 characters on top of those already read.
  147 + ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
  148 + @Override
  149 + public String toString() {
  150 + int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
  151 + try {
  152 + return new String(buf, 0, length, charset.name());
  153 + } catch (UnsupportedEncodingException e) {
  154 + throw new AssertionError(e); // Since we control the charset this will never happen.
  155 + }
  156 + }
  157 + };
  158 +
  159 + while (true) {
  160 + out.write(buf, pos, end - pos);
  161 + // Mark unterminated line in case fillBuf throws EOFException or IOException.
  162 + end = -1;
  163 + fillBuf();
  164 + // Try to find LF in the buffered data and return the line if successful.
  165 + for (int i = pos; i != end; ++i) {
  166 + if (buf[i] == LF) {
  167 + if (i != pos) {
  168 + out.write(buf, pos, i - pos);
  169 + }
  170 + pos = i + 1;
  171 + return out.toString();
  172 + }
  173 + }
  174 + }
  175 + }
  176 + }
  177 +
  178 + /**
  179 + * Reads new input data into the buffer. Call only with pos == end or end == -1,
  180 + * depending on the desired outcome if the function throws.
  181 + */
  182 + private void fillBuf() throws IOException {
  183 + int result = in.read(buf, 0, buf.length);
  184 + if (result == -1) {
  185 + throw new EOFException();
  186 + }
  187 + pos = 0;
  188 + end = result;
  189 + }
  190 +}
  191 +
... ...
  1 +/*
  2 + * Copyright (C) 2010 The Android Open Source Project
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package com.nostra13.universalimageloader.cache.disc.impl.ext;
  17 +
  18 +import java.io.Closeable;
  19 +import java.io.File;
  20 +import java.io.IOException;
  21 +import java.io.Reader;
  22 +import java.io.StringWriter;
  23 +import java.nio.charset.Charset;
  24 +
  25 +/** Junk drawer of utility methods. */
  26 +final class Util {
  27 + static final Charset US_ASCII = Charset.forName("US-ASCII");
  28 + static final Charset UTF_8 = Charset.forName("UTF-8");
  29 +
  30 + private Util() {
  31 + }
  32 +
  33 + static String readFully(Reader reader) throws IOException {
  34 + try {
  35 + StringWriter writer = new StringWriter();
  36 + char[] buffer = new char[1024];
  37 + int count;
  38 + while ((count = reader.read(buffer)) != -1) {
  39 + writer.write(buffer, 0, count);
  40 + }
  41 + return writer.toString();
  42 + } finally {
  43 + reader.close();
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * Deletes the contents of {@code dir}. Throws an IOException if any file
  49 + * could not be deleted, or if {@code dir} is not a readable directory.
  50 + */
  51 + static void deleteContents(File dir) throws IOException {
  52 + File[] files = dir.listFiles();
  53 + if (files == null) {
  54 + throw new IOException("not a readable directory: " + dir);
  55 + }
  56 + for (File file : files) {
  57 + if (file.isDirectory()) {
  58 + deleteContents(file);
  59 + }
  60 + if (!file.delete()) {
  61 + throw new IOException("failed to delete file: " + file);
  62 + }
  63 + }
  64 + }
  65 +
  66 + static void closeQuietly(/*Auto*/Closeable closeable) {
  67 + if (closeable != null) {
  68 + try {
  69 + closeable.close();
  70 + } catch (RuntimeException rethrown) {
  71 + throw rethrown;
  72 + } catch (Exception ignored) {
  73 + }
  74 + }
  75 + }
  76 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +/**
  19 + * Generates names for files at disk cache
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.3.1
  23 + */
  24 +public interface FileNameGenerator {
  25 +
  26 + /** Generates unique file name for image defined by URI */
  27 + String generate(String imageUri);
  28 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +/**
  19 + * Names image file as image URI {@linkplain String#hashCode() hashcode}
  20 + *
  21 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  22 + * @since 1.3.1
  23 + */
  24 +public class HashCodeFileNameGenerator implements FileNameGenerator {
  25 + @Override
  26 + public String generate(String imageUri) {
  27 + return String.valueOf(imageUri.hashCode());
  28 + }
  29 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2013 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.disc.naming;
  17 +
  18 +import com.nostra13.universalimageloader.utils.L;
  19 +
  20 +import java.math.BigInteger;
  21 +import java.security.MessageDigest;
  22 +import java.security.NoSuchAlgorithmException;
  23 +
  24 +/**
  25 + * Names image file as MD5 hash of image URI
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @since 1.4.0
  29 + */
  30 +public class Md5FileNameGenerator implements FileNameGenerator {
  31 +
  32 + private static final String HASH_ALGORITHM = "MD5";
  33 + private static final int RADIX = 10 + 26; // 10 digits + 26 letters
  34 +
  35 + @Override
  36 + public String generate(String imageUri) {
  37 + byte[] md5 = getMD5(imageUri.getBytes());
  38 + BigInteger bi = new BigInteger(md5).abs();
  39 + return bi.toString(RADIX);
  40 + }
  41 +
  42 + private byte[] getMD5(byte[] data) {
  43 + byte[] hash = null;
  44 + try {
  45 + MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
  46 + digest.update(data);
  47 + hash = digest.digest();
  48 + } catch (NoSuchAlgorithmException e) {
  49 + L.e(e);
  50 + }
  51 + return hash;
  52 + }
  53 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import java.lang.ref.Reference;
  21 +import java.util.*;
  22 +
  23 +/**
  24 + * Base memory cache. Implements common functionality for memory cache. Provides object references (
  25 + * {@linkplain Reference not strong}) storing.
  26 + *
  27 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  28 + * @since 1.0.0
  29 + */
  30 +public abstract class BaseMemoryCache implements MemoryCache {
  31 +
  32 + /** Stores not strong references to objects */
  33 + private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());
  34 +
  35 + @Override
  36 + public Bitmap get(String key) {
  37 + Bitmap result = null;
  38 + Reference<Bitmap> reference = softMap.get(key);
  39 + if (reference != null) {
  40 + result = reference.get();
  41 + }
  42 + return result;
  43 + }
  44 +
  45 + @Override
  46 + public boolean put(String key, Bitmap value) {
  47 + softMap.put(key, createReference(value));
  48 + return true;
  49 + }
  50 +
  51 + @Override
  52 + public Bitmap remove(String key) {
  53 + Reference<Bitmap> bmpRef = softMap.remove(key);
  54 + return bmpRef == null ? null : bmpRef.get();
  55 + }
  56 +
  57 + @Override
  58 + public Collection<String> keys() {
  59 + synchronized (softMap) {
  60 + return new HashSet<String>(softMap.keySet());
  61 + }
  62 + }
  63 +
  64 + @Override
  65 + public void clear() {
  66 + softMap.clear();
  67 + }
  68 +
  69 + /** Creates {@linkplain Reference not strong} reference of value */
  70 + protected abstract Reference<Bitmap> createReference(Bitmap value);
  71 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.utils.L;
  21 +
  22 +import java.util.Collections;
  23 +import java.util.LinkedList;
  24 +import java.util.List;
  25 +import java.util.concurrent.atomic.AtomicInteger;
  26 +
  27 +/**
  28 + * Limited cache. Provides object storing. Size of all stored bitmaps will not to exceed size limit (
  29 + * {@link #getSizeLimit()}).<br />
  30 + * <br />
  31 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  32 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @see BaseMemoryCache
  36 + * @since 1.0.0
  37 + */
  38 +public abstract class LimitedMemoryCache extends BaseMemoryCache {
  39 +
  40 + private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
  41 + private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;
  42 +
  43 + private final int sizeLimit;
  44 +
  45 + private final AtomicInteger cacheSize;
  46 +
  47 + /**
  48 + * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
  49 + * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
  50 + * time)
  51 + */
  52 + private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
  53 +
  54 + /** @param sizeLimit Maximum size for cache (in bytes) */
  55 + public LimitedMemoryCache(int sizeLimit) {
  56 + this.sizeLimit = sizeLimit;
  57 + cacheSize = new AtomicInteger();
  58 + if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
  59 + L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
  60 + }
  61 + }
  62 +
  63 + @Override
  64 + public boolean put(String key, Bitmap value) {
  65 + boolean putSuccessfully = false;
  66 + // Try to add value to hard cache
  67 + int valueSize = getSize(value);
  68 + int sizeLimit = getSizeLimit();
  69 + int curCacheSize = cacheSize.get();
  70 + if (valueSize < sizeLimit) {
  71 + while (curCacheSize + valueSize > sizeLimit) {
  72 + Bitmap removedValue = removeNext();
  73 + if (hardCache.remove(removedValue)) {
  74 + curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
  75 + }
  76 + }
  77 + hardCache.add(value);
  78 + cacheSize.addAndGet(valueSize);
  79 +
  80 + putSuccessfully = true;
  81 + }
  82 + // Add value to soft cache
  83 + super.put(key, value);
  84 + return putSuccessfully;
  85 + }
  86 +
  87 + @Override
  88 + public Bitmap remove(String key) {
  89 + Bitmap value = super.get(key);
  90 + if (value != null) {
  91 + if (hardCache.remove(value)) {
  92 + cacheSize.addAndGet(-getSize(value));
  93 + }
  94 + }
  95 + return super.remove(key);
  96 + }
  97 +
  98 + @Override
  99 + public void clear() {
  100 + hardCache.clear();
  101 + cacheSize.set(0);
  102 + super.clear();
  103 + }
  104 +
  105 + protected int getSizeLimit() {
  106 + return sizeLimit;
  107 + }
  108 +
  109 + protected abstract int getSize(Bitmap value);
  110 +
  111 + protected abstract Bitmap removeNext();
  112 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import java.util.Collection;
  21 +
  22 +/**
  23 + * Interface for memory cache
  24 + *
  25 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  26 + * @since 1.9.2
  27 + */
  28 +public interface MemoryCache {
  29 + /**
  30 + * Puts value into cache by key
  31 + *
  32 + * @return <b>true</b> - if value was put into cache successfully, <b>false</b> - if value was <b>not</b> put into
  33 + * cache
  34 + */
  35 + boolean put(String key, Bitmap value);
  36 +
  37 + /** Returns value by key. If there is no value for key then null will be returned. */
  38 + Bitmap get(String key);
  39 +
  40 + /** Removes item by key */
  41 + Bitmap remove(String key);
  42 +
  43 + /** Returns all keys of cache */
  44 + Collection<String> keys();
  45 +
  46 + /** Remove all items from cache */
  47 + void clear();
  48 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.LinkedList;
  25 +import java.util.List;
  26 +
  27 +/**
  28 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  29 + * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.<br />
  30 + * <br />
  31 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  32 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  33 + *
  34 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  35 + * @since 1.0.0
  36 + */
  37 +public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
  38 +
  39 + private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());
  40 +
  41 + public FIFOLimitedMemoryCache(int sizeLimit) {
  42 + super(sizeLimit);
  43 + }
  44 +
  45 + @Override
  46 + public boolean put(String key, Bitmap value) {
  47 + if (super.put(key, value)) {
  48 + queue.add(value);
  49 + return true;
  50 + } else {
  51 + return false;
  52 + }
  53 + }
  54 +
  55 + @Override
  56 + public Bitmap remove(String key) {
  57 + Bitmap value = super.get(key);
  58 + if (value != null) {
  59 + queue.remove(value);
  60 + }
  61 + return super.remove(key);
  62 + }
  63 +
  64 + @Override
  65 + public void clear() {
  66 + queue.clear();
  67 + super.clear();
  68 + }
  69 +
  70 + @Override
  71 + protected int getSize(Bitmap value) {
  72 + return value.getRowBytes() * value.getHeight();
  73 + }
  74 +
  75 + @Override
  76 + protected Bitmap removeNext() {
  77 + return queue.remove(0);
  78 + }
  79 +
  80 + @Override
  81 + protected Reference<Bitmap> createReference(Bitmap value) {
  82 + return new WeakReference<Bitmap>(value);
  83 + }
  84 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  21 +
  22 +import java.util.Collection;
  23 +import java.util.Comparator;
  24 +
  25 +/**
  26 + * Decorator for {@link MemoryCache}. Provides special feature for cache: some different keys are considered as
  27 + * equals (using {@link Comparator comparator}). And when you try to put some value into cache by key so entries with
  28 + * "equals" keys will be removed from cache before.<br />
  29 + * <b>NOTE:</b> Used for internal needs. Normally you don't need to use this class.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @since 1.0.0
  33 + */
  34 +public class FuzzyKeyMemoryCache implements MemoryCache {
  35 +
  36 + private final MemoryCache cache;
  37 + private final Comparator<String> keyComparator;
  38 +
  39 + public FuzzyKeyMemoryCache(MemoryCache cache, Comparator<String> keyComparator) {
  40 + this.cache = cache;
  41 + this.keyComparator = keyComparator;
  42 + }
  43 +
  44 + @Override
  45 + public boolean put(String key, Bitmap value) {
  46 + // Search equal key and remove this entry
  47 + synchronized (cache) {
  48 + String keyToRemove = null;
  49 + for (String cacheKey : cache.keys()) {
  50 + if (keyComparator.compare(key, cacheKey) == 0) {
  51 + keyToRemove = cacheKey;
  52 + break;
  53 + }
  54 + }
  55 + if (keyToRemove != null) {
  56 + cache.remove(keyToRemove);
  57 + }
  58 + }
  59 + return cache.put(key, value);
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + return cache.get(key);
  65 + }
  66 +
  67 + @Override
  68 + public Bitmap remove(String key) {
  69 + return cache.remove(key);
  70 + }
  71 +
  72 + @Override
  73 + public void clear() {
  74 + cache.clear();
  75 + }
  76 +
  77 + @Override
  78 + public Collection<String> keys() {
  79 + return cache.keys();
  80 + }
  81 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.Iterator;
  25 +import java.util.LinkedHashMap;
  26 +import java.util.Map;
  27 +import java.util.Map.Entry;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the least recently used bitmap is deleted from cache.<br />
  32 + * <br />
  33 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  34 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  35 + *
  36 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  37 + * @since 1.3.0
  38 + */
  39 +public class LRULimitedMemoryCache extends LimitedMemoryCache {
  40 +
  41 + private static final int INITIAL_CAPACITY = 10;
  42 + private static final float LOAD_FACTOR = 1.1f;
  43 +
  44 + /** Cache providing Least-Recently-Used logic */
  45 + private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
  46 +
  47 + /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
  48 + public LRULimitedMemoryCache(int maxSize) {
  49 + super(maxSize);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + lruCache.put(key, value);
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + lruCache.get(key); // call "get" for LRU logic
  65 + return super.get(key);
  66 + }
  67 +
  68 + @Override
  69 + public Bitmap remove(String key) {
  70 + lruCache.remove(key);
  71 + return super.remove(key);
  72 + }
  73 +
  74 + @Override
  75 + public void clear() {
  76 + lruCache.clear();
  77 + super.clear();
  78 + }
  79 +
  80 + @Override
  81 + protected int getSize(Bitmap value) {
  82 + return value.getRowBytes() * value.getHeight();
  83 + }
  84 +
  85 + @Override
  86 + protected Bitmap removeNext() {
  87 + Bitmap mostLongUsedValue = null;
  88 + synchronized (lruCache) {
  89 + Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
  90 + if (it.hasNext()) {
  91 + Entry<String, Bitmap> entry = it.next();
  92 + mostLongUsedValue = entry.getValue();
  93 + it.remove();
  94 + }
  95 + }
  96 + return mostLongUsedValue;
  97 + }
  98 +
  99 + @Override
  100 + protected Reference<Bitmap> createReference(Bitmap value) {
  101 + return new WeakReference<Bitmap>(value);
  102 + }
  103 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +import java.util.Map.Entry;
  27 +import java.util.Set;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the bitmap which has the largest size is deleted from
  32 + * cache.<br />
  33 + * <br />
  34 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  35 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  36 + *
  37 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  38 + * @since 1.0.0
  39 + */
  40 +public class LargestLimitedMemoryCache extends LimitedMemoryCache {
  41 + /**
  42 + * Contains strong references to stored objects (keys) and sizes of the objects. If hard cache
  43 + * size will exceed limit then object with the largest size is deleted (but it continue exist at
  44 + * {@link #softMap} and can be collected by GC at any time)
  45 + */
  46 + private final Map<Bitmap, Integer> valueSizes = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
  47 +
  48 + public LargestLimitedMemoryCache(int sizeLimit) {
  49 + super(sizeLimit);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + valueSizes.put(value, getSize(value));
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap remove(String key) {
  64 + Bitmap value = super.get(key);
  65 + if (value != null) {
  66 + valueSizes.remove(value);
  67 + }
  68 + return super.remove(key);
  69 + }
  70 +
  71 + @Override
  72 + public void clear() {
  73 + valueSizes.clear();
  74 + super.clear();
  75 + }
  76 +
  77 + @Override
  78 + protected int getSize(Bitmap value) {
  79 + return value.getRowBytes() * value.getHeight();
  80 + }
  81 +
  82 + @Override
  83 + protected Bitmap removeNext() {
  84 + Integer maxSize = null;
  85 + Bitmap largestValue = null;
  86 + Set<Entry<Bitmap, Integer>> entries = valueSizes.entrySet();
  87 + synchronized (valueSizes) {
  88 + for (Entry<Bitmap, Integer> entry : entries) {
  89 + if (largestValue == null) {
  90 + largestValue = entry.getKey();
  91 + maxSize = entry.getValue();
  92 + } else {
  93 + Integer size = entry.getValue();
  94 + if (size > maxSize) {
  95 + maxSize = size;
  96 + largestValue = entry.getKey();
  97 + }
  98 + }
  99 + }
  100 + }
  101 + valueSizes.remove(largestValue);
  102 + return largestValue;
  103 + }
  104 +
  105 + @Override
  106 + protected Reference<Bitmap> createReference(Bitmap value) {
  107 + return new WeakReference<Bitmap>(value);
  108 + }
  109 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +
  20 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  21 +
  22 +import java.util.Collection;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +
  27 +/**
  28 + * Decorator for {@link MemoryCache}. Provides special feature for cache: if some cached object age exceeds defined
  29 + * value then this object will be removed from cache.
  30 + *
  31 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  32 + * @see MemoryCache
  33 + * @since 1.3.1
  34 + */
  35 +public class LimitedAgeMemoryCache implements MemoryCache {
  36 +
  37 + private final MemoryCache cache;
  38 +
  39 + private final long maxAge;
  40 + private final Map<String, Long> loadingDates = Collections.synchronizedMap(new HashMap<String, Long>());
  41 +
  42 + /**
  43 + * @param cache Wrapped memory cache
  44 + * @param maxAge Max object age <b>(in seconds)</b>. If object age will exceed this value then it'll be removed from
  45 + * cache on next treatment (and therefore be reloaded).
  46 + */
  47 + public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) {
  48 + this.cache = cache;
  49 + this.maxAge = maxAge * 1000; // to milliseconds
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + boolean putSuccesfully = cache.put(key, value);
  55 + if (putSuccesfully) {
  56 + loadingDates.put(key, System.currentTimeMillis());
  57 + }
  58 + return putSuccesfully;
  59 + }
  60 +
  61 + @Override
  62 + public Bitmap get(String key) {
  63 + Long loadingDate = loadingDates.get(key);
  64 + if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) {
  65 + cache.remove(key);
  66 + loadingDates.remove(key);
  67 + }
  68 +
  69 + return cache.get(key);
  70 + }
  71 +
  72 + @Override
  73 + public Bitmap remove(String key) {
  74 + loadingDates.remove(key);
  75 + return cache.remove(key);
  76 + }
  77 +
  78 + @Override
  79 + public Collection<String> keys() {
  80 + return cache.keys();
  81 + }
  82 +
  83 + @Override
  84 + public void clear() {
  85 + cache.clear();
  86 + loadingDates.clear();
  87 + }
  88 +}
... ...
  1 +package com.nostra13.universalimageloader.cache.memory.impl;
  2 +
  3 +import android.graphics.Bitmap;
  4 +
  5 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  6 +
  7 +import java.util.Collection;
  8 +import java.util.HashSet;
  9 +import java.util.LinkedHashMap;
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
  14 + * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
  15 + * become eligible for garbage collection.<br />
  16 + * <br />
  17 + * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
  18 + *
  19 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  20 + * @since 1.8.1
  21 + */
  22 +public class LruMemoryCache implements MemoryCache {
  23 +
  24 + private final LinkedHashMap<String, Bitmap> map;
  25 +
  26 + private final int maxSize;
  27 + /** Size of this cache in bytes */
  28 + private int size;
  29 +
  30 + /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
  31 + public LruMemoryCache(int maxSize) {
  32 + if (maxSize <= 0) {
  33 + throw new IllegalArgumentException("maxSize <= 0");
  34 + }
  35 + this.maxSize = maxSize;
  36 + this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
  37 + }
  38 +
  39 + /**
  40 + * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
  41 + * of the queue. This returns null if a Bitmap is not cached.
  42 + */
  43 + @Override
  44 + public final Bitmap get(String key) {
  45 + if (key == null) {
  46 + throw new NullPointerException("key == null");
  47 + }
  48 +
  49 + synchronized (this) {
  50 + return map.get(key);
  51 + }
  52 + }
  53 +
  54 + /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
  55 + @Override
  56 + public final boolean put(String key, Bitmap value) {
  57 + if (key == null || value == null) {
  58 + throw new NullPointerException("key == null || value == null");
  59 + }
  60 +
  61 + synchronized (this) {
  62 + size += sizeOf(key, value);
  63 + Bitmap previous = map.put(key, value);
  64 + if (previous != null) {
  65 + size -= sizeOf(key, previous);
  66 + }
  67 + }
  68 +
  69 + trimToSize(maxSize);
  70 + return true;
  71 + }
  72 +
  73 + /**
  74 + * Remove the eldest entries until the total of remaining entries is at or below the requested size.
  75 + *
  76 + * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
  77 + */
  78 + private void trimToSize(int maxSize) {
  79 + while (true) {
  80 + String key;
  81 + Bitmap value;
  82 + synchronized (this) {
  83 + if (size < 0 || (map.isEmpty() && size != 0)) {
  84 + throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
  85 + }
  86 +
  87 + if (size <= maxSize || map.isEmpty()) {
  88 + break;
  89 + }
  90 +
  91 + Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
  92 + if (toEvict == null) {
  93 + break;
  94 + }
  95 + key = toEvict.getKey();
  96 + value = toEvict.getValue();
  97 + map.remove(key);
  98 + size -= sizeOf(key, value);
  99 + }
  100 + }
  101 + }
  102 +
  103 + /** Removes the entry for {@code key} if it exists. */
  104 + @Override
  105 + public final Bitmap remove(String key) {
  106 + if (key == null) {
  107 + throw new NullPointerException("key == null");
  108 + }
  109 +
  110 + synchronized (this) {
  111 + Bitmap previous = map.remove(key);
  112 + if (previous != null) {
  113 + size -= sizeOf(key, previous);
  114 + }
  115 + return previous;
  116 + }
  117 + }
  118 +
  119 + @Override
  120 + public Collection<String> keys() {
  121 + synchronized (this) {
  122 + return new HashSet<String>(map.keySet());
  123 + }
  124 + }
  125 +
  126 + @Override
  127 + public void clear() {
  128 + trimToSize(-1); // -1 will evict 0-sized elements
  129 + }
  130 +
  131 + /**
  132 + * Returns the size {@code Bitmap} in bytes.
  133 + * <p/>
  134 + * An entry's size must not change while it is in the cache.
  135 + */
  136 + private int sizeOf(String key, Bitmap value) {
  137 + return value.getRowBytes() * value.getHeight();
  138 + }
  139 +
  140 + @Override
  141 + public synchronized final String toString() {
  142 + return String.format("LruCache[maxSize=%d]", maxSize);
  143 + }
  144 +}
\ No newline at end of file
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.LimitedMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +import java.util.Collections;
  24 +import java.util.HashMap;
  25 +import java.util.Map;
  26 +import java.util.Map.Entry;
  27 +import java.util.Set;
  28 +
  29 +/**
  30 + * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
  31 + * exceed size limit. When cache reaches limit size then the bitmap which used the least frequently is deleted from
  32 + * cache.<br />
  33 + * <br />
  34 + * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
  35 + * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
  36 + *
  37 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  38 + * @since 1.0.0
  39 + */
  40 +public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
  41 + /**
  42 + * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
  43 + * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
  44 + * {@link #softMap} and can be collected by GC at any time)
  45 + */
  46 + private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
  47 +
  48 + public UsingFreqLimitedMemoryCache(int sizeLimit) {
  49 + super(sizeLimit);
  50 + }
  51 +
  52 + @Override
  53 + public boolean put(String key, Bitmap value) {
  54 + if (super.put(key, value)) {
  55 + usingCounts.put(value, 0);
  56 + return true;
  57 + } else {
  58 + return false;
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public Bitmap get(String key) {
  64 + Bitmap value = super.get(key);
  65 + // Increment usage count for value if value is contained in hardCahe
  66 + if (value != null) {
  67 + Integer usageCount = usingCounts.get(value);
  68 + if (usageCount != null) {
  69 + usingCounts.put(value, usageCount + 1);
  70 + }
  71 + }
  72 + return value;
  73 + }
  74 +
  75 + @Override
  76 + public Bitmap remove(String key) {
  77 + Bitmap value = super.get(key);
  78 + if (value != null) {
  79 + usingCounts.remove(value);
  80 + }
  81 + return super.remove(key);
  82 + }
  83 +
  84 + @Override
  85 + public void clear() {
  86 + usingCounts.clear();
  87 + super.clear();
  88 + }
  89 +
  90 + @Override
  91 + protected int getSize(Bitmap value) {
  92 + return value.getRowBytes() * value.getHeight();
  93 + }
  94 +
  95 + @Override
  96 + protected Bitmap removeNext() {
  97 + Integer minUsageCount = null;
  98 + Bitmap leastUsedValue = null;
  99 + Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
  100 + synchronized (usingCounts) {
  101 + for (Entry<Bitmap, Integer> entry : entries) {
  102 + if (leastUsedValue == null) {
  103 + leastUsedValue = entry.getKey();
  104 + minUsageCount = entry.getValue();
  105 + } else {
  106 + Integer lastValueUsage = entry.getValue();
  107 + if (lastValueUsage < minUsageCount) {
  108 + minUsageCount = lastValueUsage;
  109 + leastUsedValue = entry.getKey();
  110 + }
  111 + }
  112 + }
  113 + }
  114 + usingCounts.remove(leastUsedValue);
  115 + return leastUsedValue;
  116 + }
  117 +
  118 + @Override
  119 + protected Reference<Bitmap> createReference(Bitmap value) {
  120 + return new WeakReference<Bitmap>(value);
  121 + }
  122 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.cache.memory.impl;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.cache.memory.BaseMemoryCache;
  20 +
  21 +import java.lang.ref.Reference;
  22 +import java.lang.ref.WeakReference;
  23 +
  24 +/**
  25 + * Memory cache with {@linkplain WeakReference weak references} to {@linkplain Bitmap bitmaps}<br />
  26 + * <br />
  27 + * <b>NOTE:</b> This cache uses only weak references for stored Bitmaps.
  28 + *
  29 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  30 + * @since 1.5.3
  31 + */
  32 +public class WeakMemoryCache extends BaseMemoryCache {
  33 + @Override
  34 + protected Reference<Bitmap> createReference(Bitmap value) {
  35 + return new WeakReference<Bitmap>(value);
  36 + }
  37 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.annotation.TargetApi;
  19 +import android.app.ActivityManager;
  20 +import android.content.Context;
  21 +import android.content.pm.ApplicationInfo;
  22 +import android.os.Build;
  23 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  24 +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
  25 +import com.nostra13.universalimageloader.cache.disc.impl.ext.LruDiskCache;
  26 +import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  27 +import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
  28 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  29 +import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;
  30 +import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
  31 +import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque;
  32 +import com.nostra13.universalimageloader.core.decode.BaseImageDecoder;
  33 +import com.nostra13.universalimageloader.core.decode.ImageDecoder;
  34 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  35 +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
  36 +import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
  37 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  38 +import com.nostra13.universalimageloader.utils.L;
  39 +import com.nostra13.universalimageloader.utils.StorageUtils;
  40 +
  41 +import java.io.File;
  42 +import java.io.IOException;
  43 +import java.util.concurrent.BlockingQueue;
  44 +import java.util.concurrent.Executor;
  45 +import java.util.concurrent.Executors;
  46 +import java.util.concurrent.LinkedBlockingQueue;
  47 +import java.util.concurrent.ThreadFactory;
  48 +import java.util.concurrent.ThreadPoolExecutor;
  49 +import java.util.concurrent.TimeUnit;
  50 +import java.util.concurrent.atomic.AtomicInteger;
  51 +
  52 +/**
  53 + * Factory for providing of default options for {@linkplain ImageLoaderConfiguration configuration}
  54 + *
  55 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  56 + * @since 1.5.6
  57 + */
  58 +public class DefaultConfigurationFactory {
  59 +
  60 + /** Creates default implementation of task executor */
  61 + public static Executor createExecutor(int threadPoolSize, int threadPriority,
  62 + QueueProcessingType tasksProcessingType) {
  63 + boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
  64 + BlockingQueue<Runnable> taskQueue =
  65 + lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
  66 + return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
  67 + createThreadFactory(threadPriority, "uil-pool-"));
  68 + }
  69 +
  70 + /** Creates default implementation of task distributor */
  71 + public static Executor createTaskDistributor() {
  72 + return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
  73 + }
  74 +
  75 + /** Creates {@linkplain HashCodeFileNameGenerator default implementation} of FileNameGenerator */
  76 + public static FileNameGenerator createFileNameGenerator() {
  77 + return new HashCodeFileNameGenerator();
  78 + }
  79 +
  80 + /**
  81 + * Creates default implementation of {@link DiskCache} depends on incoming parameters
  82 + */
  83 + public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
  84 + long diskCacheSize, int diskCacheFileCount) {
  85 + File reserveCacheDir = createReserveDiskCacheDir(context);
  86 + if (diskCacheSize > 0 || diskCacheFileCount > 0) {
  87 + File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
  88 + try {
  89 + return new LruDiskCache(individualCacheDir, reserveCacheDir, diskCacheFileNameGenerator, diskCacheSize,
  90 + diskCacheFileCount);
  91 + } catch (IOException e) {
  92 + L.e(e);
  93 + // continue and create unlimited cache
  94 + }
  95 + }
  96 + File cacheDir = StorageUtils.getCacheDirectory(context);
  97 + return new UnlimitedDiskCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
  98 + }
  99 +
  100 + /** Creates reserve disk cache folder which will be used if primary disk cache folder becomes unavailable */
  101 + private static File createReserveDiskCacheDir(Context context) {
  102 + File cacheDir = StorageUtils.getCacheDirectory(context, false);
  103 + File individualDir = new File(cacheDir, "uil-images");
  104 + if (individualDir.exists() || individualDir.mkdir()) {
  105 + cacheDir = individualDir;
  106 + }
  107 + return cacheDir;
  108 + }
  109 +
  110 + /**
  111 + * Creates default implementation of {@link MemoryCache} - {@link LruMemoryCache}<br />
  112 + * Default cache size = 1/8 of available app memory.
  113 + */
  114 + public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) {
  115 + if (memoryCacheSize == 0) {
  116 + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  117 + int memoryClass = am.getMemoryClass();
  118 + if (hasHoneycomb() && isLargeHeap(context)) {
  119 + memoryClass = getLargeMemoryClass(am);
  120 + }
  121 + memoryCacheSize = 1024 * 1024 * memoryClass / 8;
  122 + }
  123 + return new LruMemoryCache(memoryCacheSize);
  124 + }
  125 +
  126 + private static boolean hasHoneycomb() {
  127 + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
  128 + }
  129 +
  130 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  131 + private static boolean isLargeHeap(Context context) {
  132 + return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) != 0;
  133 + }
  134 +
  135 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  136 + private static int getLargeMemoryClass(ActivityManager am) {
  137 + return am.getLargeMemoryClass();
  138 + }
  139 +
  140 + /** Creates default implementation of {@link ImageDownloader} - {@link BaseImageDownloader} */
  141 + public static ImageDownloader createImageDownloader(Context context) {
  142 + return new BaseImageDownloader(context);
  143 + }
  144 +
  145 + /** Creates default implementation of {@link ImageDecoder} - {@link BaseImageDecoder} */
  146 + public static ImageDecoder createImageDecoder(boolean loggingEnabled) {
  147 + return new BaseImageDecoder(loggingEnabled);
  148 + }
  149 +
  150 + /** Creates default implementation of {@link BitmapDisplayer} - {@link SimpleBitmapDisplayer} */
  151 + public static BitmapDisplayer createBitmapDisplayer() {
  152 + return new SimpleBitmapDisplayer();
  153 + }
  154 +
  155 + /** Creates default implementation of {@linkplain ThreadFactory thread factory} for task executor */
  156 + private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
  157 + return new DefaultThreadFactory(threadPriority, threadNamePrefix);
  158 + }
  159 +
  160 + private static class DefaultThreadFactory implements ThreadFactory {
  161 +
  162 + private static final AtomicInteger poolNumber = new AtomicInteger(1);
  163 +
  164 + private final ThreadGroup group;
  165 + private final AtomicInteger threadNumber = new AtomicInteger(1);
  166 + private final String namePrefix;
  167 + private final int threadPriority;
  168 +
  169 + DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
  170 + this.threadPriority = threadPriority;
  171 + group = Thread.currentThread().getThreadGroup();
  172 + namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
  173 + }
  174 +
  175 + @Override
  176 + public Thread newThread(Runnable r) {
  177 + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
  178 + if (t.isDaemon()) t.setDaemon(false);
  179 + t.setPriority(threadPriority);
  180 + return t;
  181 + }
  182 + }
  183 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  20 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  21 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  22 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  23 +import com.nostra13.universalimageloader.utils.L;
  24 +
  25 +/**
  26 + * Displays bitmap in {@link ImageAware}. Must be called on UI thread.
  27 + *
  28 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  29 + * @see ImageLoadingListener
  30 + * @see BitmapDisplayer
  31 + * @since 1.3.1
  32 + */
  33 +final class DisplayBitmapTask implements Runnable {
  34 +
  35 + private static final String LOG_DISPLAY_IMAGE_IN_IMAGEAWARE = "Display image in ImageAware (loaded from %1$s) [%2$s]";
  36 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_REUSED = "ImageAware is reused for another image. Task is cancelled. [%s]";
  37 + private static final String LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED = "ImageAware was collected by GC. Task is cancelled. [%s]";
  38 +
  39 + private final Bitmap bitmap;
  40 + private final String imageUri;
  41 + private final ImageAware imageAware;
  42 + private final String memoryCacheKey;
  43 + private final BitmapDisplayer displayer;
  44 + private final ImageLoadingListener listener;
  45 + private final ImageLoaderEngine engine;
  46 + private final LoadedFrom loadedFrom;
  47 +
  48 + public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine,
  49 + LoadedFrom loadedFrom) {
  50 + this.bitmap = bitmap;
  51 + imageUri = imageLoadingInfo.uri;
  52 + imageAware = imageLoadingInfo.imageAware;
  53 + memoryCacheKey = imageLoadingInfo.memoryCacheKey;
  54 + displayer = imageLoadingInfo.options.getDisplayer();
  55 + listener = imageLoadingInfo.listener;
  56 + this.engine = engine;
  57 + this.loadedFrom = loadedFrom;
  58 + }
  59 +
  60 + @Override
  61 + public void run() {
  62 + if (imageAware.isCollected()) {
  63 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
  64 + listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
  65 + } else if (isViewWasReused()) {
  66 + L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
  67 + listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
  68 + } else {
  69 + L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
  70 + displayer.display(bitmap, imageAware, loadedFrom);
  71 + engine.cancelDisplayTaskFor(imageAware);
  72 + listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
  73 + }
  74 + }
  75 +
  76 + /** Checks whether memory cache key (image URI) for current ImageAware is actual */
  77 + private boolean isViewWasReused() {
  78 + String currentCacheKey = engine.getLoadingUriForView(imageAware);
  79 + return !memoryCacheKey.equals(currentCacheKey);
  80 + }
  81 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.content.res.Resources;
  19 +import android.graphics.Bitmap;
  20 +import android.graphics.BitmapFactory.Options;
  21 +import android.graphics.drawable.Drawable;
  22 +import android.os.Handler;
  23 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  24 +import com.nostra13.universalimageloader.core.assist.ImageScaleType;
  25 +import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
  26 +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
  27 +import com.nostra13.universalimageloader.core.download.ImageDownloader;
  28 +import com.nostra13.universalimageloader.core.process.BitmapProcessor;
  29 +
  30 +/**
  31 + * Contains options for image display. Defines:
  32 + * <ul>
  33 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  34 + * image aware view} during image loading</li>
  35 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  36 + * image aware view} if empty URI is passed</li>
  37 + * <li>whether stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  38 + * image aware view} if image loading fails</li>
  39 + * <li>whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} should be reset
  40 + * before image loading start</li>
  41 + * <li>whether loaded image will be cached in memory</li>
  42 + * <li>whether loaded image will be cached on disk</li>
  43 + * <li>image scale type</li>
  44 + * <li>decoding options (including bitmap decoding configuration)</li>
  45 + * <li>delay before loading of image</li>
  46 + * <li>whether consider EXIF parameters of image</li>
  47 + * <li>auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object) ImageDownloader}</li>
  48 + * <li>pre-processor for image Bitmap (before caching in memory)</li>
  49 + * <li>post-processor for image Bitmap (after caching in memory, before displaying)</li>
  50 + * <li>how decoded {@link Bitmap} will be displayed</li>
  51 + * </ul>
  52 + * <p/>
  53 + * You can create instance:
  54 + * <ul>
  55 + * <li>with {@link Builder}:<br />
  56 + * <b>i.e.</b> :
  57 + * <code>new {@link DisplayImageOptions}.{@link Builder#Builder() Builder()}.{@link Builder#cacheInMemory() cacheInMemory()}.
  58 + * {@link Builder#showImageOnLoading(int) showImageOnLoading()}.{@link Builder#build() build()}</code><br />
  59 + * </li>
  60 + * <li>or by static method: {@link #createSimple()}</li> <br />
  61 + *
  62 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  63 + * @since 1.0.0
  64 + */
  65 +public final class DisplayImageOptions {
  66 +
  67 + private final int imageResOnLoading;
  68 + private final int imageResForEmptyUri;
  69 + private final int imageResOnFail;
  70 + private final Drawable imageOnLoading;
  71 + private final Drawable imageForEmptyUri;
  72 + private final Drawable imageOnFail;
  73 + private final boolean resetViewBeforeLoading;
  74 + private final boolean cacheInMemory;
  75 + private final boolean cacheOnDisk;
  76 + private final ImageScaleType imageScaleType;
  77 + private final Options decodingOptions;
  78 + private final int delayBeforeLoading;
  79 + private final boolean considerExifParams;
  80 + private final Object extraForDownloader;
  81 + private final BitmapProcessor preProcessor;
  82 + private final BitmapProcessor postProcessor;
  83 + private final BitmapDisplayer displayer;
  84 + private final Handler handler;
  85 + private final boolean isSyncLoading;
  86 +
  87 + private DisplayImageOptions(Builder builder) {
  88 + imageResOnLoading = builder.imageResOnLoading;
  89 + imageResForEmptyUri = builder.imageResForEmptyUri;
  90 + imageResOnFail = builder.imageResOnFail;
  91 + imageOnLoading = builder.imageOnLoading;
  92 + imageForEmptyUri = builder.imageForEmptyUri;
  93 + imageOnFail = builder.imageOnFail;
  94 + resetViewBeforeLoading = builder.resetViewBeforeLoading;
  95 + cacheInMemory = builder.cacheInMemory;
  96 + cacheOnDisk = builder.cacheOnDisk;
  97 + imageScaleType = builder.imageScaleType;
  98 + decodingOptions = builder.decodingOptions;
  99 + delayBeforeLoading = builder.delayBeforeLoading;
  100 + considerExifParams = builder.considerExifParams;
  101 + extraForDownloader = builder.extraForDownloader;
  102 + preProcessor = builder.preProcessor;
  103 + postProcessor = builder.postProcessor;
  104 + displayer = builder.displayer;
  105 + handler = builder.handler;
  106 + isSyncLoading = builder.isSyncLoading;
  107 + }
  108 +
  109 + public boolean shouldShowImageOnLoading() {
  110 + return imageOnLoading != null || imageResOnLoading != 0;
  111 + }
  112 +
  113 + public boolean shouldShowImageForEmptyUri() {
  114 + return imageForEmptyUri != null || imageResForEmptyUri != 0;
  115 + }
  116 +
  117 + public boolean shouldShowImageOnFail() {
  118 + return imageOnFail != null || imageResOnFail != 0;
  119 + }
  120 +
  121 + public boolean shouldPreProcess() {
  122 + return preProcessor != null;
  123 + }
  124 +
  125 + public boolean shouldPostProcess() {
  126 + return postProcessor != null;
  127 + }
  128 +
  129 + public boolean shouldDelayBeforeLoading() {
  130 + return delayBeforeLoading > 0;
  131 + }
  132 +
  133 + public Drawable getImageOnLoading(Resources res) {
  134 + return imageResOnLoading != 0 ? res.getDrawable(imageResOnLoading) : imageOnLoading;
  135 + }
  136 +
  137 + public Drawable getImageForEmptyUri(Resources res) {
  138 + return imageResForEmptyUri != 0 ? res.getDrawable(imageResForEmptyUri) : imageForEmptyUri;
  139 + }
  140 +
  141 + public Drawable getImageOnFail(Resources res) {
  142 + return imageResOnFail != 0 ? res.getDrawable(imageResOnFail) : imageOnFail;
  143 + }
  144 +
  145 + public boolean isResetViewBeforeLoading() {
  146 + return resetViewBeforeLoading;
  147 + }
  148 +
  149 + public boolean isCacheInMemory() {
  150 + return cacheInMemory;
  151 + }
  152 +
  153 + public boolean isCacheOnDisk() {
  154 + return cacheOnDisk;
  155 + }
  156 +
  157 + public ImageScaleType getImageScaleType() {
  158 + return imageScaleType;
  159 + }
  160 +
  161 + public Options getDecodingOptions() {
  162 + return decodingOptions;
  163 + }
  164 +
  165 + public int getDelayBeforeLoading() {
  166 + return delayBeforeLoading;
  167 + }
  168 +
  169 + public boolean isConsiderExifParams() {
  170 + return considerExifParams;
  171 + }
  172 +
  173 + public Object getExtraForDownloader() {
  174 + return extraForDownloader;
  175 + }
  176 +
  177 + public BitmapProcessor getPreProcessor() {
  178 + return preProcessor;
  179 + }
  180 +
  181 + public BitmapProcessor getPostProcessor() {
  182 + return postProcessor;
  183 + }
  184 +
  185 + public BitmapDisplayer getDisplayer() {
  186 + return displayer;
  187 + }
  188 +
  189 + public Handler getHandler() {
  190 + return handler;
  191 + }
  192 +
  193 + boolean isSyncLoading() {
  194 + return isSyncLoading;
  195 + }
  196 +
  197 + /**
  198 + * Builder for {@link DisplayImageOptions}
  199 + *
  200 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  201 + */
  202 + public static class Builder {
  203 + private int imageResOnLoading = 0;
  204 + private int imageResForEmptyUri = 0;
  205 + private int imageResOnFail = 0;
  206 + private Drawable imageOnLoading = null;
  207 + private Drawable imageForEmptyUri = null;
  208 + private Drawable imageOnFail = null;
  209 + private boolean resetViewBeforeLoading = false;
  210 + private boolean cacheInMemory = false;
  211 + private boolean cacheOnDisk = false;
  212 + private ImageScaleType imageScaleType = ImageScaleType.IN_SAMPLE_POWER_OF_2;
  213 + private Options decodingOptions = new Options();
  214 + private int delayBeforeLoading = 0;
  215 + private boolean considerExifParams = false;
  216 + private Object extraForDownloader = null;
  217 + private BitmapProcessor preProcessor = null;
  218 + private BitmapProcessor postProcessor = null;
  219 + private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();
  220 + private Handler handler = null;
  221 + private boolean isSyncLoading = false;
  222 +
  223 + public Builder() {
  224 + decodingOptions.inPurgeable = true;
  225 + decodingOptions.inInputShareable = true;
  226 + }
  227 +
  228 + /**
  229 + * Stub image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  230 + * image aware view} during image loading
  231 + *
  232 + * @param imageRes Stub image resource
  233 + * @deprecated Use {@link #showImageOnLoading(int)} instead
  234 + */
  235 + @Deprecated
  236 + public Builder showStubImage(int imageRes) {
  237 + imageResOnLoading = imageRes;
  238 + return this;
  239 + }
  240 +
  241 + /**
  242 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  243 + * image aware view} during image loading
  244 + *
  245 + * @param imageRes Image resource
  246 + */
  247 + public Builder showImageOnLoading(int imageRes) {
  248 + imageResOnLoading = imageRes;
  249 + return this;
  250 + }
  251 +
  252 + /**
  253 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  254 + * image aware view} during image loading.
  255 + * This option will be ignored if {@link Builder#showImageOnLoading(int)} is set.
  256 + */
  257 + public Builder showImageOnLoading(Drawable drawable) {
  258 + imageOnLoading = drawable;
  259 + return this;
  260 + }
  261 +
  262 + /**
  263 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  264 + * image aware view} if empty URI (null or empty
  265 + * string) will be passed to <b>ImageLoader.displayImage(...)</b> method.
  266 + *
  267 + * @param imageRes Image resource
  268 + */
  269 + public Builder showImageForEmptyUri(int imageRes) {
  270 + imageResForEmptyUri = imageRes;
  271 + return this;
  272 + }
  273 +
  274 + /**
  275 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  276 + * image aware view} if empty URI (null or empty
  277 + * string) will be passed to <b>ImageLoader.displayImage(...)</b> method.
  278 + * This option will be ignored if {@link Builder#showImageForEmptyUri(int)} is set.
  279 + */
  280 + public Builder showImageForEmptyUri(Drawable drawable) {
  281 + imageForEmptyUri = drawable;
  282 + return this;
  283 + }
  284 +
  285 + /**
  286 + * Incoming image will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  287 + * image aware view} if some error occurs during
  288 + * requested image loading/decoding.
  289 + *
  290 + * @param imageRes Image resource
  291 + */
  292 + public Builder showImageOnFail(int imageRes) {
  293 + imageResOnFail = imageRes;
  294 + return this;
  295 + }
  296 +
  297 + /**
  298 + * Incoming drawable will be displayed in {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  299 + * image aware view} if some error occurs during
  300 + * requested image loading/decoding.
  301 + * This option will be ignored if {@link Builder#showImageOnFail(int)} is set.
  302 + */
  303 + public Builder showImageOnFail(Drawable drawable) {
  304 + imageOnFail = drawable;
  305 + return this;
  306 + }
  307 +
  308 + /**
  309 + * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  310 + * image aware view} will be reset (set <b>null</b>) before image loading start
  311 + *
  312 + * @deprecated Use {@link #resetViewBeforeLoading(boolean) resetViewBeforeLoading(true)} instead
  313 + */
  314 + public Builder resetViewBeforeLoading() {
  315 + resetViewBeforeLoading = true;
  316 + return this;
  317 + }
  318 +
  319 + /**
  320 + * Sets whether {@link com.nostra13.universalimageloader.core.imageaware.ImageAware
  321 + * image aware view} will be reset (set <b>null</b>) before image loading start
  322 + */
  323 + public Builder resetViewBeforeLoading(boolean resetViewBeforeLoading) {
  324 + this.resetViewBeforeLoading = resetViewBeforeLoading;
  325 + return this;
  326 + }
  327 +
  328 + /**
  329 + * Loaded image will be cached in memory
  330 + *
  331 + * @deprecated Use {@link #cacheInMemory(boolean) cacheInMemory(true)} instead
  332 + */
  333 + @Deprecated
  334 + public Builder cacheInMemory() {
  335 + cacheInMemory = true;
  336 + return this;
  337 + }
  338 +
  339 + /** Sets whether loaded image will be cached in memory */
  340 + public Builder cacheInMemory(boolean cacheInMemory) {
  341 + this.cacheInMemory = cacheInMemory;
  342 + return this;
  343 + }
  344 +
  345 + /**
  346 + * Loaded image will be cached on disk
  347 + *
  348 + * @deprecated Use {@link #cacheOnDisk(boolean) cacheOnDisk(true)} instead
  349 + */
  350 + @Deprecated
  351 + public Builder cacheOnDisc() {
  352 + return cacheOnDisk(true);
  353 + }
  354 +
  355 + /**
  356 + * Sets whether loaded image will be cached on disk
  357 + *
  358 + * @deprecated Use {@link #cacheOnDisk(boolean)} instead
  359 + */
  360 + @Deprecated
  361 + public Builder cacheOnDisc(boolean cacheOnDisk) {
  362 + return cacheOnDisk(cacheOnDisk);
  363 + }
  364 +
  365 + /** Sets whether loaded image will be cached on disk */
  366 + public Builder cacheOnDisk(boolean cacheOnDisk) {
  367 + this.cacheOnDisk = cacheOnDisk;
  368 + return this;
  369 + }
  370 +
  371 + /**
  372 + * Sets {@linkplain ImageScaleType scale type} for decoding image. This parameter is used while define scale
  373 + * size for decoding image to Bitmap. Default value - {@link ImageScaleType#IN_SAMPLE_POWER_OF_2}
  374 + */
  375 + public Builder imageScaleType(ImageScaleType imageScaleType) {
  376 + this.imageScaleType = imageScaleType;
  377 + return this;
  378 + }
  379 +
  380 + /** Sets {@link Bitmap.Config bitmap config} for image decoding. Default value - {@link Bitmap.Config#ARGB_8888} */
  381 + public Builder bitmapConfig(Bitmap.Config bitmapConfig) {
  382 + if (bitmapConfig == null) throw new IllegalArgumentException("bitmapConfig can't be null");
  383 + decodingOptions.inPreferredConfig = bitmapConfig;
  384 + return this;
  385 + }
  386 +
  387 + /**
  388 + * Sets options for image decoding.<br />
  389 + * <b>NOTE:</b> {@link Options#inSampleSize} of incoming options will <b>NOT</b> be considered. Library
  390 + * calculate the most appropriate sample size itself according yo {@link #imageScaleType(ImageScaleType)}
  391 + * options.<br />
  392 + * <b>NOTE:</b> This option overlaps {@link #bitmapConfig(Bitmap.Config) bitmapConfig()}
  393 + * option.
  394 + */
  395 + public Builder decodingOptions(Options decodingOptions) {
  396 + if (decodingOptions == null) throw new IllegalArgumentException("decodingOptions can't be null");
  397 + this.decodingOptions = decodingOptions;
  398 + return this;
  399 + }
  400 +
  401 + /** Sets delay time before starting loading task. Default - no delay. */
  402 + public Builder delayBeforeLoading(int delayInMillis) {
  403 + this.delayBeforeLoading = delayInMillis;
  404 + return this;
  405 + }
  406 +
  407 + /** Sets auxiliary object which will be passed to {@link ImageDownloader#getStream(String, Object)} */
  408 + public Builder extraForDownloader(Object extra) {
  409 + this.extraForDownloader = extra;
  410 + return this;
  411 + }
  412 +
  413 + /** Sets whether ImageLoader will consider EXIF parameters of JPEG image (rotate, flip) */
  414 + public Builder considerExifParams(boolean considerExifParams) {
  415 + this.considerExifParams = considerExifParams;
  416 + return this;
  417 + }
  418 +
  419 + /**
  420 + * Sets bitmap processor which will be process bitmaps before they will be cached in memory. So memory cache
  421 + * will contain bitmap processed by incoming preProcessor.<br />
  422 + * Image will be pre-processed even if caching in memory is disabled.
  423 + */
  424 + public Builder preProcessor(BitmapProcessor preProcessor) {
  425 + this.preProcessor = preProcessor;
  426 + return this;
  427 + }
  428 +
  429 + /**
  430 + * Sets bitmap processor which will be process bitmaps before they will be displayed in
  431 + * {@link com.nostra13.universalimageloader.core.imageaware.ImageAware image aware view} but
  432 + * after they'll have been saved in memory cache.
  433 + */
  434 + public Builder postProcessor(BitmapProcessor postProcessor) {
  435 + this.postProcessor = postProcessor;
  436 + return this;
  437 + }
  438 +
  439 + /**
  440 + * Sets custom {@link BitmapDisplayer displayer} for image loading task. Default value -
  441 + * {@link DefaultConfigurationFactory#createBitmapDisplayer()}
  442 + */
  443 + public Builder displayer(BitmapDisplayer displayer) {
  444 + if (displayer == null) throw new IllegalArgumentException("displayer can't be null");
  445 + this.displayer = displayer;
  446 + return this;
  447 + }
  448 +
  449 + Builder syncLoading(boolean isSyncLoading) {
  450 + this.isSyncLoading = isSyncLoading;
  451 + return this;
  452 + }
  453 +
  454 + /**
  455 + * Sets custom {@linkplain Handler handler} for displaying images and firing {@linkplain ImageLoadingListener
  456 + * listener} events.
  457 + */
  458 + public Builder handler(Handler handler) {
  459 + this.handler = handler;
  460 + return this;
  461 + }
  462 +
  463 + /** Sets all options equal to incoming options */
  464 + public Builder cloneFrom(DisplayImageOptions options) {
  465 + imageResOnLoading = options.imageResOnLoading;
  466 + imageResForEmptyUri = options.imageResForEmptyUri;
  467 + imageResOnFail = options.imageResOnFail;
  468 + imageOnLoading = options.imageOnLoading;
  469 + imageForEmptyUri = options.imageForEmptyUri;
  470 + imageOnFail = options.imageOnFail;
  471 + resetViewBeforeLoading = options.resetViewBeforeLoading;
  472 + cacheInMemory = options.cacheInMemory;
  473 + cacheOnDisk = options.cacheOnDisk;
  474 + imageScaleType = options.imageScaleType;
  475 + decodingOptions = options.decodingOptions;
  476 + delayBeforeLoading = options.delayBeforeLoading;
  477 + considerExifParams = options.considerExifParams;
  478 + extraForDownloader = options.extraForDownloader;
  479 + preProcessor = options.preProcessor;
  480 + postProcessor = options.postProcessor;
  481 + displayer = options.displayer;
  482 + handler = options.handler;
  483 + isSyncLoading = options.isSyncLoading;
  484 + return this;
  485 + }
  486 +
  487 + /** Builds configured {@link DisplayImageOptions} object */
  488 + public DisplayImageOptions build() {
  489 + return new DisplayImageOptions(this);
  490 + }
  491 + }
  492 +
  493 + /**
  494 + * Creates options appropriate for single displaying:
  495 + * <ul>
  496 + * <li>View will <b>not</b> be reset before loading</li>
  497 + * <li>Loaded image will <b>not</b> be cached in memory</li>
  498 + * <li>Loaded image will <b>not</b> be cached on disk</li>
  499 + * <li>{@link ImageScaleType#IN_SAMPLE_POWER_OF_2} decoding type will be used</li>
  500 + * <li>{@link Bitmap.Config#ARGB_8888} bitmap config will be used for image decoding</li>
  501 + * <li>{@link SimpleBitmapDisplayer} will be used for image displaying</li>
  502 + * </ul>
  503 + * <p/>
  504 + * These option are appropriate for simple single-use image (from drawables or from Internet) displaying.
  505 + */
  506 + public static DisplayImageOptions createSimple() {
  507 + return new Builder().build();
  508 + }
  509 +}
... ...
  1 +/*******************************************************************************
  2 + * Copyright 2011-2014 Sergey Tarasevich
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + *******************************************************************************/
  16 +package com.nostra13.universalimageloader.core;
  17 +
  18 +import android.graphics.Bitmap;
  19 +import android.os.Handler;
  20 +import android.os.Looper;
  21 +import android.text.TextUtils;
  22 +import android.view.View;
  23 +import android.widget.ImageView;
  24 +import com.nostra13.universalimageloader.cache.disc.DiskCache;
  25 +import com.nostra13.universalimageloader.cache.memory.MemoryCache;
  26 +import com.nostra13.universalimageloader.core.assist.FailReason;
  27 +import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
  28 +import com.nostra13.universalimageloader.core.assist.ImageSize;
  29 +import com.nostra13.universalimageloader.core.assist.LoadedFrom;
  30 +import com.nostra13.universalimageloader.core.assist.ViewScaleType;
  31 +import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  32 +import com.nostra13.universalimageloader.core.imageaware.ImageViewAware;
  33 +import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
  34 +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  35 +import com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener;
  36 +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
  37 +import com.nostra13.universalimageloader.utils.ImageSizeUtils;
  38 +import com.nostra13.universalimageloader.utils.L;
  39 +import com.nostra13.universalimageloader.utils.MemoryCacheUtils;
  40 +
  41 +/**
  42 + * Singletone for image loading and displaying at {@link ImageView ImageViews}<br />
  43 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before any other method.
  44 + *
  45 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  46 + * @since 1.0.0
  47 + */
  48 +public class ImageLoader {
  49 +
  50 + public static final String TAG = ImageLoader.class.getSimpleName();
  51 +
  52 + static final String LOG_INIT_CONFIG = "Initialize ImageLoader with configuration";
  53 + static final String LOG_DESTROY = "Destroy ImageLoader";
  54 + static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";
  55 +
  56 + private static final String WARNING_RE_INIT_CONFIG = "Try to initialize ImageLoader which had already been initialized before. " + "To re-init ImageLoader with new configuration call ImageLoader.destroy() at first.";
  57 + private static final String ERROR_WRONG_ARGUMENTS = "Wrong arguments were passed to displayImage() method (ImageView reference must not be null)";
  58 + private static final String ERROR_NOT_INIT = "ImageLoader must be init with configuration before using";
  59 + private static final String ERROR_INIT_CONFIG_WITH_NULL = "ImageLoader configuration can not be initialized with null";
  60 +
  61 + private ImageLoaderConfiguration configuration;
  62 + private ImageLoaderEngine engine;
  63 +
  64 + private ImageLoadingListener defaultListener = new SimpleImageLoadingListener();
  65 +
  66 + private volatile static ImageLoader instance;
  67 +
  68 + /** Returns singleton class instance */
  69 + public static ImageLoader getInstance() {
  70 + if (instance == null) {
  71 + synchronized (ImageLoader.class) {
  72 + if (instance == null) {
  73 + instance = new ImageLoader();
  74 + }
  75 + }
  76 + }
  77 + return instance;
  78 + }
  79 +
  80 + protected ImageLoader() {
  81 + }
  82 +
  83 + /**
  84 + * Initializes ImageLoader instance with configuration.<br />
  85 + * If configurations was set before ( {@link #isInited()} == true) then this method does nothing.<br />
  86 + * To force initialization with new configuration you should {@linkplain #destroy() destroy ImageLoader} at first.
  87 + *
  88 + * @param configuration {@linkplain ImageLoaderConfiguration ImageLoader configuration}
  89 + * @throws IllegalArgumentException if <b>configuration</b> parameter is null
  90 + */
  91 + public synchronized void init(ImageLoaderConfiguration configuration) {
  92 + if (configuration == null) {
  93 + throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
  94 + }
  95 + if (this.configuration == null) {
  96 + L.d(LOG_INIT_CONFIG);
  97 + engine = new ImageLoaderEngine(configuration);
  98 + this.configuration = configuration;
  99 + } else {
  100 + L.w(WARNING_RE_INIT_CONFIG);
  101 + }
  102 + }
  103 +
  104 + /**
  105 + * Returns <b>true</b> - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with
  106 + * configuration}; <b>false</b> - otherwise
  107 + */
  108 + public boolean isInited() {
  109 + return configuration != null;
  110 + }
  111 +
  112 + /**
  113 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn. <br/>
  114 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  115 + * configuration} will be used.<br />
  116 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  117 + *
  118 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  119 + * @param imageAware {@linkplain ImageAware Image aware view}
  120 + * which should display image
  121 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  122 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  123 + */
  124 + public void displayImage(String uri, ImageAware imageAware) {
  125 + displayImage(uri, imageAware, null, null, null);
  126 + }
  127 +
  128 + /**
  129 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  130 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  131 + * configuration} will be used.<br />
  132 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  133 + *
  134 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  135 + * @param imageAware {@linkplain ImageAware Image aware view}
  136 + * which should display image
  137 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  138 + * UI thread if this method is called on UI thread.
  139 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  140 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  141 + */
  142 + public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
  143 + displayImage(uri, imageAware, null, listener, null);
  144 + }
  145 +
  146 + /**
  147 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  148 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  149 + *
  150 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  151 + * @param imageAware {@linkplain ImageAware Image aware view}
  152 + * which should display image
  153 + * @param options {@linkplain DisplayImageOptions Options} for image
  154 + * decoding and displaying. If <b>null</b> - default display image options
  155 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  156 + * from configuration} will be used.
  157 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  158 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  159 + */
  160 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options) {
  161 + displayImage(uri, imageAware, options, null, null);
  162 + }
  163 +
  164 + /**
  165 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  166 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  167 + *
  168 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  169 + * @param imageAware {@linkplain ImageAware Image aware view}
  170 + * which should display image
  171 + * @param options {@linkplain DisplayImageOptions Options} for image
  172 + * decoding and displaying. If <b>null</b> - default display image options
  173 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  174 + * from configuration} will be used.
  175 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  176 + * UI thread if this method is called on UI thread.
  177 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  178 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  179 + */
  180 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
  181 + ImageLoadingListener listener) {
  182 + displayImage(uri, imageAware, options, listener, null);
  183 + }
  184 +
  185 + /**
  186 + * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
  187 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  188 + *
  189 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  190 + * @param imageAware {@linkplain ImageAware Image aware view}
  191 + * which should display image
  192 + * @param options {@linkplain DisplayImageOptions Options} for image
  193 + * decoding and displaying. If <b>null</b> - default display image options
  194 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  195 + * from configuration} will be used.
  196 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  197 + * events on UI thread if this method is called on UI thread.
  198 + * @param progressListener {@linkplain ImageLoadingProgressListener
  199 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  200 + * is called on UI thread. Caching on disk should be enabled in
  201 + * {@linkplain DisplayImageOptions options} to make
  202 + * this listener work.
  203 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  204 + * @throws IllegalArgumentException if passed <b>imageAware</b> is null
  205 + */
  206 + public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
  207 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  208 + checkConfiguration();
  209 + if (imageAware == null) {
  210 + throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
  211 + }
  212 + if (listener == null) {
  213 + listener = defaultListener;
  214 + }
  215 + if (options == null) {
  216 + options = configuration.defaultDisplayImageOptions;
  217 + }
  218 +
  219 + if (TextUtils.isEmpty(uri)) {
  220 + engine.cancelDisplayTaskFor(imageAware);
  221 + listener.onLoadingStarted(uri, imageAware.getWrappedView());
  222 + if (options.shouldShowImageForEmptyUri()) {
  223 + imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
  224 + } else {
  225 + imageAware.setImageDrawable(null);
  226 + }
  227 + listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
  228 + return;
  229 + }
  230 +
  231 + ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
  232 + String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
  233 + engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
  234 +
  235 + listener.onLoadingStarted(uri, imageAware.getWrappedView());
  236 +
  237 + Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
  238 + if (bmp != null && !bmp.isRecycled()) {
  239 + L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
  240 +
  241 + if (options.shouldPostProcess()) {
  242 + ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
  243 + options, listener, progressListener, engine.getLockForUri(uri));
  244 + ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
  245 + defineHandler(options));
  246 + if (options.isSyncLoading()) {
  247 + displayTask.run();
  248 + } else {
  249 + engine.submit(displayTask);
  250 + }
  251 + } else {
  252 + options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
  253 + listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
  254 + }
  255 + } else {
  256 + if (options.shouldShowImageOnLoading()) {
  257 + imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
  258 + } else if (options.isResetViewBeforeLoading()) {
  259 + imageAware.setImageDrawable(null);
  260 + }
  261 +
  262 + ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
  263 + options, listener, progressListener, engine.getLockForUri(uri));
  264 + LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
  265 + defineHandler(options));
  266 + if (options.isSyncLoading()) {
  267 + displayTask.run();
  268 + } else {
  269 + engine.submit(displayTask);
  270 + }
  271 + }
  272 + }
  273 +
  274 + /**
  275 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn. <br/>
  276 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  277 + * configuration} will be used.<br />
  278 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  279 + *
  280 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  281 + * @param imageView {@link ImageView} which should display image
  282 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  283 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  284 + */
  285 + public void displayImage(String uri, ImageView imageView) {
  286 + displayImage(uri, new ImageViewAware(imageView), null, null, null);
  287 + }
  288 +
  289 + /**
  290 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  291 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  292 + *
  293 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  294 + * @param imageView {@link ImageView} which should display image
  295 + * @param options {@linkplain DisplayImageOptions Options} for image
  296 + * decoding and displaying. If <b>null</b> - default display image options
  297 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  298 + * from configuration} will be used.
  299 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  300 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  301 + */
  302 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
  303 + displayImage(uri, new ImageViewAware(imageView), options, null, null);
  304 + }
  305 +
  306 + /**
  307 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  308 + * Default {@linkplain DisplayImageOptions display image options} from {@linkplain ImageLoaderConfiguration
  309 + * configuration} will be used.<br />
  310 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  311 + *
  312 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  313 + * @param imageView {@link ImageView} which should display image
  314 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  315 + * UI thread if this method is called on UI thread.
  316 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  317 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  318 + */
  319 + public void displayImage(String uri, ImageView imageView, ImageLoadingListener listener) {
  320 + displayImage(uri, new ImageViewAware(imageView), null, listener, null);
  321 + }
  322 +
  323 + /**
  324 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  325 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  326 + *
  327 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  328 + * @param imageView {@link ImageView} which should display image
  329 + * @param options {@linkplain DisplayImageOptions Options} for image
  330 + * decoding and displaying. If <b>null</b> - default display image options
  331 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  332 + * from configuration} will be used.
  333 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on
  334 + * UI thread if this method is called on UI thread.
  335 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  336 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  337 + */
  338 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
  339 + ImageLoadingListener listener) {
  340 + displayImage(uri, imageView, options, listener, null);
  341 + }
  342 +
  343 + /**
  344 + * Adds display image task to execution pool. Image will be set to ImageView when it's turn.<br />
  345 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  346 + *
  347 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  348 + * @param imageView {@link ImageView} which should display image
  349 + * @param options {@linkplain DisplayImageOptions Options} for image
  350 + * decoding and displaying. If <b>null</b> - default display image options
  351 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  352 + * from configuration} will be used.
  353 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  354 + * events on UI thread if this method is called on UI thread.
  355 + * @param progressListener {@linkplain ImageLoadingProgressListener
  356 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  357 + * is called on UI thread. Caching on disk should be enabled in
  358 + * {@linkplain DisplayImageOptions options} to make
  359 + * this listener work.
  360 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  361 + * @throws IllegalArgumentException if passed <b>imageView</b> is null
  362 + */
  363 + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options,
  364 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  365 + displayImage(uri, new ImageViewAware(imageView), options, listener, progressListener);
  366 + }
  367 +
  368 + /**
  369 + * Adds load image task to execution pool. Image will be returned with
  370 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  371 + * <br />
  372 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  373 + *
  374 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  375 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
  376 + * thread if this method is called on UI thread.
  377 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  378 + */
  379 + public void loadImage(String uri, ImageLoadingListener listener) {
  380 + loadImage(uri, null, null, listener, null);
  381 + }
  382 +
  383 + /**
  384 + * Adds load image task to execution pool. Image will be returned with
  385 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  386 + * <br />
  387 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  388 + *
  389 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  390 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  391 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  392 + * Bitmap)} callback}. Downloaded image will be decoded
  393 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  394 + * larger) than incoming targetImageSize.
  395 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  396 + * events on UI thread if this method is called on UI thread.
  397 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  398 + */
  399 + public void loadImage(String uri, ImageSize targetImageSize, ImageLoadingListener listener) {
  400 + loadImage(uri, targetImageSize, null, listener, null);
  401 + }
  402 +
  403 + /**
  404 + * Adds load image task to execution pool. Image will be returned with
  405 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  406 + * <br />
  407 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  408 + *
  409 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  410 + * @param options {@linkplain DisplayImageOptions Options} for image
  411 + * decoding and displaying. If <b>null</b> - default display image options
  412 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  413 + * configuration} will be used.<br />
  414 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires events on UI
  415 + * thread if this method is called on UI thread.
  416 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  417 + */
  418 + public void loadImage(String uri, DisplayImageOptions options, ImageLoadingListener listener) {
  419 + loadImage(uri, null, options, listener, null);
  420 + }
  421 +
  422 + /**
  423 + * Adds load image task to execution pool. Image will be returned with
  424 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  425 + * <br />
  426 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  427 + *
  428 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  429 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  430 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  431 + * Bitmap)} callback}. Downloaded image will be decoded
  432 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  433 + * larger) than incoming targetImageSize.
  434 + * @param options {@linkplain DisplayImageOptions Options} for image
  435 + * decoding and displaying. If <b>null</b> - default display image options
  436 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  437 + * from configuration} will be used.<br />
  438 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  439 + * events on UI thread if this method is called on UI thread.
  440 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  441 + */
  442 + public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
  443 + ImageLoadingListener listener) {
  444 + loadImage(uri, targetImageSize, options, listener, null);
  445 + }
  446 +
  447 + /**
  448 + * Adds load image task to execution pool. Image will be returned with
  449 + * {@link ImageLoadingListener#onLoadingComplete(String, View, Bitmap)} callback}.
  450 + * <br />
  451 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  452 + *
  453 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  454 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned in
  455 + * {@linkplain ImageLoadingListener#onLoadingComplete(String, View,
  456 + * Bitmap)} callback}. Downloaded image will be decoded
  457 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  458 + * larger) than incoming targetImageSize.
  459 + * @param options {@linkplain DisplayImageOptions Options} for image
  460 + * decoding and displaying. If <b>null</b> - default display image options
  461 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  462 + * from configuration} will be used.<br />
  463 + * @param listener {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
  464 + * events on UI thread if this method is called on UI thread.
  465 + * @param progressListener {@linkplain ImageLoadingProgressListener
  466 + * Listener} for image loading progress. Listener fires events on UI thread if this method
  467 + * is called on UI thread. Caching on disk should be enabled in
  468 + * {@linkplain DisplayImageOptions options} to make
  469 + * this listener work.
  470 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  471 + */
  472 + public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
  473 + ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
  474 + checkConfiguration();
  475 + if (targetImageSize == null) {
  476 + targetImageSize = configuration.getMaxImageSize();
  477 + }
  478 + if (options == null) {
  479 + options = configuration.defaultDisplayImageOptions;
  480 + }
  481 +
  482 + NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
  483 + displayImage(uri, imageAware, options, listener, progressListener);
  484 + }
  485 +
  486 + /**
  487 + * Loads and decodes image synchronously.<br />
  488 + * Default display image options
  489 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  490 + * configuration} will be used.<br />
  491 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  492 + *
  493 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  494 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  495 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  496 + */
  497 + public Bitmap loadImageSync(String uri) {
  498 + return loadImageSync(uri, null, null);
  499 + }
  500 +
  501 + /**
  502 + * Loads and decodes image synchronously.<br />
  503 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  504 + *
  505 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  506 + * @param options {@linkplain DisplayImageOptions Options} for image
  507 + * decoding and scaling. If <b>null</b> - default display image options
  508 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  509 + * configuration} will be used.
  510 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  511 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  512 + */
  513 + public Bitmap loadImageSync(String uri, DisplayImageOptions options) {
  514 + return loadImageSync(uri, null, options);
  515 + }
  516 +
  517 + /**
  518 + * Loads and decodes image synchronously.<br />
  519 + * Default display image options
  520 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions) from
  521 + * configuration} will be used.<br />
  522 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  523 + *
  524 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  525 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
  526 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  527 + * larger) than incoming targetImageSize.
  528 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  529 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  530 + */
  531 + public Bitmap loadImageSync(String uri, ImageSize targetImageSize) {
  532 + return loadImageSync(uri, targetImageSize, null);
  533 + }
  534 +
  535 + /**
  536 + * Loads and decodes image synchronously.<br />
  537 + * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
  538 + *
  539 + * @param uri Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
  540 + * @param targetImageSize Minimal size for {@link Bitmap} which will be returned. Downloaded image will be decoded
  541 + * and scaled to {@link Bitmap} of the size which is <b>equal or larger</b> (usually a bit
  542 + * larger) than incoming targetImageSize.
  543 + * @param options {@linkplain DisplayImageOptions Options} for image
  544 + * decoding and scaling. If <b>null</b> - default display image options
  545 + * {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
  546 + * from configuration} will be used.
  547 + * @return Result image Bitmap. Can be <b>null</b> if image loading/decoding was failed or cancelled.
  548 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  549 + */
  550 + public Bitmap loadImageSync(String uri, ImageSize targetImageSize, DisplayImageOptions options) {
  551 + if (options == null) {
  552 + options = configuration.defaultDisplayImageOptions;
  553 + }
  554 + options = new DisplayImageOptions.Builder().cloneFrom(options).syncLoading(true).build();
  555 +
  556 + SyncImageLoadingListener listener = new SyncImageLoadingListener();
  557 + loadImage(uri, targetImageSize, options, listener);
  558 + return listener.getLoadedBitmap();
  559 + }
  560 +
  561 + /**
  562 + * Checks if ImageLoader's configuration was initialized
  563 + *
  564 + * @throws IllegalStateException if configuration wasn't initialized
  565 + */
  566 + private void checkConfiguration() {
  567 + if (configuration == null) {
  568 + throw new IllegalStateException(ERROR_NOT_INIT);
  569 + }
  570 + }
  571 +
  572 + /** Sets a default loading listener for all display and loading tasks. */
  573 + public void setDefaultLoadingListener(ImageLoadingListener listener) {
  574 + defaultListener = listener == null ? new SimpleImageLoadingListener() : listener;
  575 + }
  576 +
  577 + /**
  578 + * Returns memory cache
  579 + *
  580 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  581 + */
  582 + public MemoryCache getMemoryCache() {
  583 + checkConfiguration();
  584 + return configuration.memoryCache;
  585 + }
  586 +
  587 + /**
  588 + * Clears memory cache
  589 + *
  590 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  591 + */
  592 + public void clearMemoryCache() {
  593 + checkConfiguration();
  594 + configuration.memoryCache.clear();
  595 + }
  596 +
  597 + /**
  598 + * Returns disk cache
  599 + *
  600 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  601 + * @deprecated Use {@link #getDiskCache()} instead
  602 + */
  603 + @Deprecated
  604 + public DiskCache getDiscCache() {
  605 + return getDiskCache();
  606 + }
  607 +
  608 + /**
  609 + * Returns disk cache
  610 + *
  611 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  612 + */
  613 + public DiskCache getDiskCache() {
  614 + checkConfiguration();
  615 + return configuration.diskCache;
  616 + }
  617 +
  618 + /**
  619 + * Clears disk cache.
  620 + *
  621 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  622 + * @deprecated Use {@link #clearDiskCache()} instead
  623 + */
  624 + @Deprecated
  625 + public void clearDiscCache() {
  626 + clearDiskCache();
  627 + }
  628 +
  629 + /**
  630 + * Clears disk cache.
  631 + *
  632 + * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
  633 + */
  634 + public void clearDiskCache() {
  635 + checkConfiguration();
  636 + configuration.diskCache.clear();
  637 + }
  638 +
  639 + /**
  640 + * Returns URI of image which is loading at this moment into passed
  641 + * {@link ImageAware ImageAware}
  642 + */
  643 + public String getLoadingUriForView(ImageAware imageAware) {
  644 + return engine.getLoadingUriForView(imageAware);
  645 + }
  646 +
  647 + /**
  648 + * Returns URI of image which is loading at this moment into passed
  649 + * {@link ImageView ImageView}
  650 + */
  651 + public String getLoadingUriForView(ImageView imageView) {
  652 + return engine.getLoadingUriForView(new ImageViewAware(imageView));
  653 + }
  654 +
  655 + /**
  656 + * Cancel the task of loading and displaying image for passed
  657 + * {@link ImageAware ImageAware}.
  658 + *
  659 + * @param imageAware {@link ImageAware ImageAware} for
  660 + * which display task will be cancelled
  661 + */
  662 + public void cancelDisplayTask(ImageAware imageAware) {
  663 + engine.cancelDisplayTaskFor(imageAware);
  664 + }
  665 +
  666 + /**
  667 + * Cancel the task of loading and displaying image for passed
  668 + * {@link ImageView ImageView}.
  669 + *
  670 + * @param imageView {@link ImageView ImageView} for which display task will be cancelled
  671 + */
  672 + public void cancelDisplayTask(ImageView imageView) {
  673 + engine.cancelDisplayTaskFor(new ImageViewAware(imageView));
  674 + }
  675 +
  676 + /**
  677 + * Denies or allows ImageLoader to download images from the network.<br />
  678 + * <br />
  679 + * If downloads are denied and if image isn't cached then
  680 + * {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired with
  681 + * {@link FailReason.FailType#NETWORK_DENIED}
  682 + *
  683 + * @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> -
  684 + * to allow engine to download images from network.
  685 + */
  686 + public void denyNetworkDownloads(boolean denyNetworkDownloads) {
  687 + engine.denyNetworkDownloads(denyNetworkDownloads);
  688 + }
  689 +
  690 + /**
  691 + * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a
  692 + * href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
  693 + *
  694 + * @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b>
  695 + * - otherwise.
  696 + */
  697 + public void handleSlowNetwork(boolean handleSlowNetwork) {
  698 + engine.handleSlowNetwork(handleSlowNetwork);
  699 + }
  700 +
  701 + /**
  702 + * Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
  703 + * <br />
  704 + * Already running tasks are not paused.
  705 + */
  706 + public void pause() {
  707 + engine.pause();
  708 + }
  709 +
  710 + /** Resumes waiting "load&display" tasks */
  711 + public void resume() {
  712 + engine.resume();
  713 + }
  714 +
  715 + /**
  716 + * Cancels all running and scheduled display image tasks.<br />
  717 + * <b>NOTE:</b> This method doesn't shutdown
  718 + * {@linkplain ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
  719 + * custom task executors} if you set them.<br />
  720 + * ImageLoader still can be used after calling this method.
  721 + */
  722 + public void stop() {
  723 + engine.stop();
  724 + }
  725 +
  726 + /**
  727 + * {@linkplain #stop() Stops ImageLoader} and clears current configuration. <br />
  728 + * You can {@linkplain #init(ImageLoaderConfiguration) init} ImageLoader with new configuration after calling this
  729 + * method.
  730 + */
  731 + public void destroy() {
  732 + if (configuration != null) L.d(LOG_DESTROY);
  733 + stop();
  734 + configuration.diskCache.close();
  735 + engine = null;
  736 + configuration = null;
  737 + }
  738 +
  739 + private static Handler defineHandler(DisplayImageOptions options) {
  740 + Handler handler = options.getHandler();
  741 + if (options.isSyncLoading()) {
  742 + handler = null;
  743 + } else if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
  744 + handler = new Handler();
  745 + }
  746 + return handler;
  747 + }
  748 +
  749 + /**
  750 + * Listener which is designed for synchronous image loading.
  751 + *
  752 + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  753 + * @since 1.9.0
  754 + */
  755 + private static class SyncImageLoadingListener extends SimpleImageLoadingListener {
  756 +
  757 + private Bitmap loadedImage;
  758 +
  759 + @Override
  760 + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
  761 + this.loadedImage = loadedImage;
  762 + }
  763 +
  764 + public Bitmap getLoadedBitmap() {
  765 + return loadedImage;
  766 + }
  767 + }
  768 +}
... ...
Please register or login to post a comment