Imageloader tasks are now submitted to an executor service

This commit is contained in:
daniel oeh 2012-08-03 17:00:00 +02:00
parent a0cedbcbed
commit 93812142da
9 changed files with 94 additions and 58 deletions

View File

@ -6,6 +6,7 @@
<ImageView <ImageView
android:id="@+id/imgvChannelimage" android:id="@+id/imgvChannelimage"
android:src="@drawable/default_cover"
android:layout_width="55dip" android:layout_width="55dip"
android:layout_height="55dip" android:layout_height="55dip"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"

View File

@ -103,6 +103,8 @@ public class MiroGuideMainActivity extends SherlockListActivity {
} catch (MiroGuideException e) { } catch (MiroGuideException e) {
e.printStackTrace(); e.printStackTrace();
exception = e; exception = e;
} finally {
service.close();
} }
return null; return null;
} }

View File

@ -10,6 +10,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.FeedImageLoader;
import de.danoeh.antennapod.miroguide.model.MiroChannel; import de.danoeh.antennapod.miroguide.model.MiroChannel;
public class MiroGuideChannelListAdapter extends ArrayAdapter<MiroChannel> { public class MiroGuideChannelListAdapter extends ArrayAdapter<MiroChannel> {
@ -41,9 +42,9 @@ public class MiroGuideChannelListAdapter extends ArrayAdapter<MiroChannel> {
holder = (Holder) convertView.getTag(); holder = (Holder) convertView.getTag();
} }
holder.cover.setVisibility(View.GONE);
holder.title.setText(channel.getName()); holder.title.setText(channel.getName());
holder.cover.setTag(channel);
FeedImageLoader.getInstance().loadMiroGuideThumbnail(channel, holder.cover);
return convertView; return convertView;
} }

View File

@ -6,6 +6,7 @@ import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.widget.ImageView; import android.widget.ImageView;
import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.AppConfig;
@ -13,8 +14,7 @@ import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
import de.danoeh.antennapod.util.BitmapDecoder; import de.danoeh.antennapod.util.BitmapDecoder;
public abstract class BitmapDecodeWorkerTask extends public abstract class BitmapDecodeWorkerTask extends Thread {
AsyncTask<Void, Void, Void> {
protected int PREFERRED_LENGTH; protected int PREFERRED_LENGTH;
@ -30,8 +30,12 @@ public abstract class BitmapDecodeWorkerTask extends
protected String fileUrl; protected String fileUrl;
public BitmapDecodeWorkerTask(ImageView target, String fileUrl, int length) { private Handler handler;
public BitmapDecodeWorkerTask(Handler handler, ImageView target,
String fileUrl, int length) {
super(); super();
this.handler = handler;
this.target = target; this.target = target;
this.fileUrl = fileUrl; this.fileUrl = fileUrl;
this.baseLength = length; this.baseLength = length;
@ -44,9 +48,7 @@ public abstract class BitmapDecodeWorkerTask extends
*/ */
abstract protected boolean tagsMatching(ImageView target); abstract protected boolean tagsMatching(ImageView target);
@Override protected void onPostExecute() {
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// check if imageview is still supposed to display this image // check if imageview is still supposed to display this image
if (tagsMatching(target)) { if (tagsMatching(target)) {
target.setImageBitmap(bitmap); target.setImageBitmap(bitmap);
@ -57,12 +59,7 @@ public abstract class BitmapDecodeWorkerTask extends
} }
@Override @Override
protected void onPreExecute() { public void run() {
super.onPreExecute();
}
@Override
protected Void doInBackground(Void... params) {
File f = null; File f = null;
if (fileUrl != null) { if (fileUrl != null) {
f = new File(fileUrl); f = new File(fileUrl);
@ -81,7 +78,18 @@ public abstract class BitmapDecodeWorkerTask extends
} else { } else {
onInvalidFileUrl(); onInvalidFileUrl();
} }
return null; endBackgroundTask();
}
protected final void endBackgroundTask() {
handler.post(new Runnable() {
@Override
public void run() {
onPostExecute();
}
});
} }
protected void onInvalidFileUrl() { protected void onInvalidFileUrl() {
@ -98,13 +106,4 @@ public abstract class BitmapDecodeWorkerTask extends
loader.addBitmapToThumbnailCache(fileUrl, bitmap); loader.addBitmapToThumbnailCache(fileUrl, bitmap);
} }
} }
@SuppressLint("NewApi")
public void executeAsync() {
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} else {
execute();
}
}
} }

View File

@ -1,6 +1,9 @@
package de.danoeh.antennapod.asynctask; package de.danoeh.antennapod.asynctask;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
@ -8,6 +11,7 @@ import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.os.Handler;
import android.support.v4.util.LruCache; import android.support.v4.util.LruCache;
import android.util.Log; import android.util.Log;
import android.widget.ImageView; import android.widget.ImageView;
@ -34,6 +38,9 @@ public class FeedImageLoader {
private static final int CACHE_SIZE = 20 * 1024 * 1024; private static final int CACHE_SIZE = 20 * 1024 * 1024;
private static final int VALUE_SIZE = 500 * 1024; private static final int VALUE_SIZE = 500 * 1024;
private Handler handler;
private ExecutorService executor;
/** /**
* Stores references to loaded bitmaps. Bitmaps can be accessed by the id of * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of
* the FeedImage the bitmap belongs to. * the FeedImage the bitmap belongs to.
@ -50,6 +57,18 @@ public class FeedImageLoader {
private LruCache<String, Bitmap> thumbnailCache; private LruCache<String, Bitmap> thumbnailCache;
private FeedImageLoader() { private FeedImageLoader() {
handler = new Handler();
executor = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors(), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setPriority(Thread.MIN_PRIORITY);
return t;
}
});
coverCache = new LruCache<String, Bitmap>(coverCacheSize) { coverCache = new LruCache<String, Bitmap>(coverCacheSize) {
@SuppressLint("NewApi") @SuppressLint("NewApi")
@ -110,8 +129,8 @@ public class FeedImageLoader {
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask( FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask(
target, image, LENGTH_BASE_COVER); handler, target, image, LENGTH_BASE_COVER);
worker.executeAsync(); executor.submit(worker);
} }
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
@ -126,30 +145,36 @@ public class FeedImageLoader {
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask( FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask(
target, image, LENGTH_BASE_THUMBNAIL); handler, target, image, LENGTH_BASE_THUMBNAIL);
worker.executeAsync(); executor.submit(worker);
} }
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
} }
} }
public void loadMiroGuideThumbnail(MiroChannel channel, ImageView target) { public void loadMiroGuideThumbnail(MiroChannel channel, ImageView target) {
if (channel.getThumbnailUrl() != null) { if (channel.getThumbnailUrl() != null) {
Bitmap bitmap = getBitmapFromThumbnailCache(channel.getThumbnailUrl()); Bitmap bitmap = getBitmapFromThumbnailCache(channel
if (bitmap != null) { .getThumbnailUrl());
if (bitmap == null) {
boolean isInDiskCache = false; boolean isInDiskCache = false;
try { try {
isInDiskCache = isInThumbnailDiskCache(channel.getThumbnailUrl()); isInDiskCache = isInThumbnailDiskCache(channel
.getThumbnailUrl());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
Log.e(TAG, "Error when trying to read disk cache"); Log.e(TAG, "Error when trying to read disk cache");
} }
if (isInDiskCache) { if (isInDiskCache) {
new MiroGuideDiskCacheLoader(target, channel, LENGTH_BASE_THUMBNAIL).executeAsync(); executor.submit(new MiroGuideDiskCacheLoader(handler,
target, channel, LENGTH_BASE_THUMBNAIL));
} else { } else {
new MiroGuideThumbnailDownloader(target, channel, LENGTH_BASE_THUMBNAIL).executeAsync(); executor.submit(new MiroGuideThumbnailDownloader(handler,
target, channel, LENGTH_BASE_THUMBNAIL));
} }
} else {
target.setImageBitmap(bitmap);
} }
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
@ -184,7 +209,7 @@ public class FeedImageLoader {
public void addBitmapToCoverCache(String key, Bitmap bitmap) { public void addBitmapToCoverCache(String key, Bitmap bitmap) {
coverCache.put(key, bitmap); coverCache.put(key, bitmap);
} }
public boolean isInThumbnailDiskCache(String key) throws IOException { public boolean isInThumbnailDiskCache(String key) throws IOException {
DiskLruCache cache = openThubmnailDiskCache(); DiskLruCache cache = openThubmnailDiskCache();
return cache.get(key) != null; return cache.get(key) != null;
@ -196,9 +221,9 @@ public class FeedImageLoader {
protected FeedImage image; protected FeedImage image;
public FeedImageDecodeWorkerTask(ImageView target, FeedImage image, public FeedImageDecodeWorkerTask(Handler handler, ImageView target,
int length) { FeedImage image, int length) {
super(target, image.getFile_url(), length); super(handler, target, image.getFile_url(), length);
this.image = image; this.image = image;
} }
@ -227,14 +252,13 @@ public class FeedImageLoader {
private MiroChannel channel; private MiroChannel channel;
public MiroGuideDiskCacheLoader(ImageView target, MiroChannel channel, public MiroGuideDiskCacheLoader(Handler handler, ImageView target,
int length) { MiroChannel channel, int length) {
super(target, channel.getThumbnailUrl(), length); super(handler, target, channel.getThumbnailUrl(), length);
this.channel = channel; this.channel = channel;
} }
@Override public void run() {
protected Void doInBackground(Void... params) {
try { try {
DiskLruCache cache = openThubmnailDiskCache(); DiskLruCache cache = openThubmnailDiskCache();
Snapshot snapshot = cache.get(fileUrl); Snapshot snapshot = cache.get(fileUrl);
@ -244,13 +268,13 @@ public class FeedImageLoader {
e.printStackTrace(); e.printStackTrace();
exception = e; exception = e;
} }
return null; endBackgroundTask();
} }
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute() {
if (exception != null) { if (exception != null) {
super.onPostExecute(result); super.onPostExecute();
} else { } else {
Log.e(TAG, "Failed to load bitmap from disk cache"); Log.e(TAG, "Failed to load bitmap from disk cache");
} }

View File

@ -19,6 +19,7 @@ import de.danoeh.antennapod.miroguide.model.MiroChannel;
import de.danoeh.antennapod.util.BitmapDecoder; import de.danoeh.antennapod.util.BitmapDecoder;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.widget.ImageView; import android.widget.ImageView;
@ -30,30 +31,29 @@ public class MiroGuideThumbnailDownloader extends BitmapDecodeWorkerTask {
private MiroChannel miroChannel; private MiroChannel miroChannel;
public MiroGuideThumbnailDownloader(ImageView target, public MiroGuideThumbnailDownloader(Handler handler, ImageView target,
MiroChannel miroChannel, int length) { MiroChannel miroChannel, int length) {
super(target, miroChannel.getThumbnailUrl(), length); super(handler, target, miroChannel.getThumbnailUrl(), length);
this.miroChannel = miroChannel; this.miroChannel = miroChannel;
} }
@Override @Override
protected void onPostExecute(Void result) { protected void onPostExecute() {
if (exception != null) { if (exception != null) {
super.onPostExecute(result); super.onPostExecute();
} else { } else {
Log.e(TAG, "Failed to download thumbnail"); Log.e(TAG, "Failed to download thumbnail");
} }
} }
@Override public void run() {
protected Void doInBackground(Void... params) {
// Download file to cache folder // Download file to cache folder
URL url = null; URL url = null;
try { try {
url = new URL(fileUrl); url = new URL(fileUrl);
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
e.printStackTrace(); e.printStackTrace();
return null; endBackgroundTask();
} }
File destination = new File(PodcastApp.getInstance().getCacheDir(), File destination = new File(PodcastApp.getInstance().getCacheDir(),
Integer.toString(fileUrl.hashCode())); Integer.toString(fileUrl.hashCode()));
@ -73,6 +73,7 @@ public class MiroGuideThumbnailDownloader extends BitmapDecodeWorkerTask {
output.write(inputBuffer, 0, count); output.write(inputBuffer, 0, count);
} }
output.close(); output.close();
connection.disconnect();
if (AppConfig.DEBUG) Log.d(TAG, "MiroGuide thumbnail downloaded"); if (AppConfig.DEBUG) Log.d(TAG, "MiroGuide thumbnail downloaded");
// Get a smaller version of the bitmap and store it inside the // Get a smaller version of the bitmap and store it inside the
// LRU // LRU
@ -91,13 +92,13 @@ public class MiroGuideThumbnailDownloader extends BitmapDecodeWorkerTask {
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return null; endBackgroundTask();
} finally { } finally {
if (destination.exists()) { if (destination.exists()) {
destination.delete(); destination.delete();
} }
} }
return null; endBackgroundTask();
} }
@Override @Override

View File

@ -227,6 +227,8 @@ public class MiroGuideChannellistFragment extends SherlockListFragment {
} catch (MiroGuideException e) { } catch (MiroGuideException e) {
exception = e; exception = e;
e.printStackTrace(); e.printStackTrace();
} finally {
service.close();
} }
return null; return null;
} }

View File

@ -15,10 +15,11 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import android.net.Uri; import android.net.Uri;
import android.net.http.AndroidHttpClient;
/** Executes HTTP requests and returns the results. */ /** Executes HTTP requests and returns the results. */
public class MiroGuideConnector { public class MiroGuideConnector {
private HttpClient httpClient; private AndroidHttpClient httpClient;
private static final String HOST_URL = "https://www.miroguide.com/api/"; private static final String HOST_URL = "https://www.miroguide.com/api/";
private static final String PATH_GET_CHANNELS = "get_channels"; private static final String PATH_GET_CHANNELS = "get_channels";
@ -26,11 +27,11 @@ public class MiroGuideConnector {
private static final String PATH_GET_CHANNEL = "get_channel"; private static final String PATH_GET_CHANNEL = "get_channel";
public MiroGuideConnector() { public MiroGuideConnector() {
httpClient = new DefaultHttpClient(); httpClient = AndroidHttpClient.newInstance(null);
} }
public void shutdown() { public void shutdown() {
httpClient.getConnectionManager().shutdown(); httpClient.close();
} }
private Uri.Builder getBaseURIBuilder(String path) { private Uri.Builder getBaseURIBuilder(String path) {
@ -78,6 +79,7 @@ public class MiroGuideConnector {
BufferedReader reader = new BufferedReader( BufferedReader reader = new BufferedReader(
new InputStreamReader(in)); new InputStreamReader(in));
result = reader.readLine(); result = reader.readLine();
in.close();
} }
} else { } else {
throw new MiroGuideException(response.getStatusLine() throw new MiroGuideException(response.getStatusLine()

View File

@ -40,6 +40,10 @@ public class MiroGuideService {
public MiroGuideService() { public MiroGuideService() {
connector = new MiroGuideConnector(); connector = new MiroGuideConnector();
} }
public void close() {
connector.shutdown();
}
public String[] getCategories() throws MiroGuideException { public String[] getCategories() throws MiroGuideException {
JSONArray resultArray = connector.getArrayResponse(connector JSONArray resultArray = connector.getArrayResponse(connector