/*******************************************************************************
* Copyright 2011, 2012, 2013 fanfou.com, Xiaoke, Zhang
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.fanfou.app.opensource.cache;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.PriorityBlockingQueue;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;
import com.fanfou.app.opensource.AppContext;
import com.fanfou.app.opensource.http.SimpleClient;
import com.fanfou.app.opensource.util.ImageHelper;
/**
* @author mcxiaoke
* @version 1.0 2011.09.23
* @version 2.0 2011.09.27
* @version 2.1 2011.11.04
* @version 2.5 2011.11.23
* @version 2.6 2011.11.28
* @version 3.0 2011.11.29
* @version 4.0 2011.12.02
* @version 4.1 2011.12.06
* @version 5.0 2011.12.08
* @version 5.1 2011.12.09
* @version 5.2 2011.12.13
*
*/
class ImageLoaderImpl implements ImageLoader {
private final class Daemon extends Thread {
@Override
public void run() {
while (true) {
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "Daemon is running.");
}
try {
final Task task = ImageLoaderImpl.this.mTaskQueue.take();
download(task);
// final Worker worker = new Worker(task, mCache, mClient);
// mExecutorService.execute(worker);
} catch (final InterruptedException e) {
if (AppContext.DEBUG) {
e.printStackTrace();
}
}
}
}
@Override
public synchronized void start() {
super.start();
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "Daemon is start.");
}
}
}
private class InnerHandler extends Handler {
@Override
public void handleMessage(final Message msg) {
final String url = msg.getData().getString(ImageLoader.EXTRA_URL);
final ImageView view = ImageLoaderImpl.this.mViewsMap.remove(url);
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "InnerHandler what=" + msg.what
+ " url=" + url + " view=" + view);
}
switch (msg.what) {
case MESSAGE_FINISH:
final Bitmap bitmap = (Bitmap) msg.obj;
if ((bitmap != null) && (view != null)) {
if (!ImageLoaderImpl.isExpired(view, url)) {
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG,
"InnerHandler onFinish() url=" + url);
}
view.setImageBitmap(bitmap);
}
}
break;
case MESSAGE_ERROR:
break;
default:
break;
}
}
}
private static final class Task {
public final String url;
public final Handler handler;
public final long timestamp;
public Task(final String url, final Handler handler) {
this.url = url;
this.handler = handler;
this.timestamp = System.nanoTime();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Task other = (Task) obj;
if (this.url == null) {
if (other.url != null) {
return false;
}
} else if (!this.url.equals(other.url)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result)
+ ((this.url == null) ? 0 : this.url.hashCode());
return result;
}
@Override
public String toString() {
return new StringBuilder().append("[Task] url:").append(this.url)
.append(" handler:").append(this.handler)
.append(" timestamp:").append(this.timestamp).toString();
}
}
private static final class TaskComparator implements Comparator<Task> {
@Override
public int compare(final Task t1, final Task t2) {
if (t1.timestamp > t2.timestamp) {
return -1;
} else if (t1.timestamp < t2.timestamp) {
return 1;
} else {
return 0;
}
}
}
@SuppressWarnings("unused")
private static final class Worker implements Runnable {
private final String url;
private final Handler handler;
private final ImageCache cache;
private final SimpleClient conn;
public Worker(final Task pair, final ImageCache cache,
final SimpleClient conn) {
this.url = pair.url;
this.handler = pair.handler;
this.cache = cache;
this.conn = conn;
}
private void download() {
Bitmap bitmap = this.cache.get(this.url);
if (bitmap == null) {
try {
bitmap = this.conn.getBitmap(this.url);
} catch (final IOException e) {
Log.e(ImageLoader.TAG, "download error:" + e.getMessage());
}
if (bitmap != null) {
this.cache.put(this.url, bitmap);
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download put bitmap to cache ");
}
}
}
if (this.handler != null) {
final Message message = this.handler.obtainMessage();
if (bitmap != null) {
message.what = ImageLoader.MESSAGE_FINISH;
message.obj = bitmap;
} else {
message.what = ImageLoader.MESSAGE_ERROR;
}
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download send message bitmap= "
+ bitmap);
}
message.getData().putString(ImageLoader.EXTRA_URL, this.url);
this.handler.sendMessage(message);
} else {
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download handle is null, bitmap= "
+ bitmap);
}
}
}
@Override
public void run() {
download();
}
}
private static boolean isExpired(final ImageView view, final String url) {
if (view == null) {
return true;
}
final String tag = (String) view.getTag();
return (tag == null) || !tag.equals(url);
}
// private final ExecutorService mExecutorService;
private final PriorityBlockingQueue<Task> mTaskQueue = new PriorityBlockingQueue<Task>(
60, new TaskComparator());
private final Map<String, ImageView> mViewsMap;
private final ImageCache mCache;
private final Handler mHandler;
private final SimpleClient mClient;
private final Thread mDaemon;
public ImageLoaderImpl() {
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "ImageLoader new instance.");
}
// this.mExecutorService =
// Executors.newFixedThreadPool(CORE_POOL_SIZE,new
// NameCountThreadFactory());
// this.mExecutorService = Executors.newSingleThreadExecutor(new
// NameCountThreadFactory());
this.mCache = ImageCache.getInstance();
this.mViewsMap = new HashMap<String, ImageView>();
this.mClient = new SimpleClient(AppContext.getAppContext());
this.mHandler = new InnerHandler();
this.mDaemon = new Daemon();
this.mDaemon.start();
// this.mExecutorService.execute(this);
}
private void addInnerTask(final String url, final ImageView view) {
final Task task = new Task(url, this.mHandler);
if (this.mTaskQueue.contains(task)) {
return;
}
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "addInnerTask url=" + url + " mHandler="
+ this.mHandler);
}
this.mTaskQueue.add(task);
this.mViewsMap.put(url, view);
}
private void addTask(final String url, final Handler handler) {
final Task task = new Task(url, handler);
if (this.mTaskQueue.contains(task)) {
return;
}
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "addTask url=" + url + " handler=" + handler);
}
this.mTaskQueue.add(task);
}
@Override
public void clearCache() {
this.mCache.clear();
}
@Override
public void clearQueue() {
this.mTaskQueue.clear();
this.mViewsMap.clear();
}
@Override
public void displayImage(final String url, final ImageView view,
final int iconId) {
if (TextUtils.isEmpty(url) || (view == null)) {
return;
}
final Bitmap bitmap = this.mCache.get(url);
if (bitmap == null) {
view.setImageResource(iconId);
addInnerTask(url, view);
} else {
view.setImageBitmap(ImageHelper.getRoundedCornerBitmap(bitmap, 6));
}
}
private void download(final Task task) {
final String url = task.url;
final Handler handler = task.handler;
Bitmap bitmap = this.mCache.get(url);
if (bitmap == null) {
try {
bitmap = this.mClient.getBitmap(url);
} catch (final Exception e) {
Log.e(ImageLoader.TAG, "download error:" + e.getMessage());
}
if (bitmap != null) {
this.mCache.put(url, bitmap);
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download put bitmap to cache ");
}
}
}
if (handler != null) {
final Message message = handler.obtainMessage();
message.getData().putString(ImageLoader.EXTRA_URL, url);
message.what = bitmap == null ? ImageLoader.MESSAGE_ERROR
: ImageLoader.MESSAGE_FINISH;
message.obj = bitmap;
handler.sendMessage(message);
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download handle can use, bitmap= "
+ bitmap);
}
} else {
if (AppContext.DEBUG) {
Log.d(ImageLoader.TAG, "download handle is null, bitmap= "
+ bitmap);
}
}
}
@Override
public Bitmap getImage(final String key, final Handler handler) {
if (TextUtils.isEmpty(key)) {
return null;
}
final Bitmap bitmap = this.mCache.get(key);
if ((bitmap == null) && (handler != null)) {
addTask(key, handler);
}
return bitmap;
}
@Override
public void shutdown() {
clearQueue();
clearCache();
}
}