UrlImageViewの動きを早くしてみた
Android開発でURLの画象を読み込むときにお世話になっているsharakovaさんの
UrlImageView(元のUrlImageViewのgithub)のコードを少しいじってキャッシュ時の動きを早くしてみました。
動きが重くなっていた場所
sharakovaさんのREADMEで
「画像のキャッシュデータをファイルに書き出す際に若干もたつくので、今後の課題です。」と
書いてあるとおり今回作るアプリではキャッシュ時に呼んでいるImageCache#saveBitmapメソッド内の
以下の部分が動きが重くなっていた原因でした。
bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
理由はよくわかりませんが、Bitmapデータを圧縮するのに時間かかるんだなー
と考えたので圧縮しないで、データを都度書きこむ形にしようと思い今回UrlImageViewを
改造しました。
改造した部分
ImageCache.java
package jp.sharakova.android.urlimageview; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.HashMap; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; public class ImageCache { private ImageCache() { } private static HashMap<String, SoftReference<Bitmap>> cache = new HashMap<String, SoftReference<Bitmap>>(); private static String getFileName(String url) { int hash = url.hashCode(); return String.valueOf(hash); } public static void saveBitmap(File cacheDir, String url, Bitmap bitmap) { cache.put(url, new SoftReference<Bitmap>(bitmap)); } public static SoftReference<Bitmap> getImage(File cacheDir, String url) { SoftReference<Bitmap> ref = cache.get(url); if (ref != null && ref.get() != null) { return ref; } String fileName = getFileName(url); File localFile = new File(cacheDir, fileName); SoftReference<Bitmap> bitmap = null; try { bitmap = new SoftReference<Bitmap>( BitmapFactory.decodeFile(localFile.getPath())); } catch (Exception e) { e.printStackTrace(); } catch (OutOfMemoryError e) { e.printStackTrace(); cache.clear(); } return bitmap; } public static void memoryCacheClear() { cache.clear(); } public static void deleteAll(File cacheDir) { if (!cacheDir.isDirectory()) { return; } File[] files = cacheDir.listFiles(); for (File file : files) { if (file.isFile()) { if (!file.delete()) { Log.v("file", "file delete false"); } } } } public static long dirSize(File cacheDir) { long size = 0L; if (cacheDir == null) { return size; } if (cacheDir.isDirectory()) { for (File file : cacheDir.listFiles()) { size += file.length(); } } else { size = cacheDir.length(); } return size; } }
WorkerThread.java
package jp.sharakova.android.urlimageview; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.HttpURLConnection; import java.net.URL; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public final class WorkerThread extends Thread { private final Channel channel; public WorkerThread(String name, Channel channel) { super(name); this.channel = channel; } @Override public void run() { while (true) { Request request = channel.takeRequest(); request.setStatus(Request.Status.LOADING); SoftReference<Bitmap> image = ImageCache.getImage( request.getCacheDir(), request.getUrl() ); if (image == null || image.get() == null) { image = getImage(request); if (image != null && image.get() != null) { ImageCache.saveBitmap(request.getCacheDir(), request.getUrl(), image.get()); } } request.setStatus(Request.Status.LOADED); request.getRunnable().run(); } } private SoftReference<Bitmap> getImage(Request request) { try { return new SoftReference<Bitmap>(getBitmapFromURL(request)); } catch (Exception e) { e.printStackTrace(); return null; } catch (OutOfMemoryError e) { e.printStackTrace(); return null; } } private Bitmap getBitmapFromURL(Request request) throws IOException { String fileName = ImageCache.getFileName(request.getUrl()); File localFile = new File(request.getCacheDir(), fileName); FileOutputStream fos = null; HttpURLConnection con = null; InputStream in = null; try { URL url = new URL(request.getUrl()); con = (HttpURLConnection) url.openConnection(); con.setUseCaches(true); con.setRequestMethod("GET"); con.setReadTimeout(500000); con.setConnectTimeout(50000); con.connect(); in = con.getInputStream(); //ImageCache#saveBitmap内で行っているbitmap.compress(Bitmap.CompressFormat.PNG, 90, fos)が重いためこちらに変更 byte[] buf = new byte[1024]; int len = 0; fos = new FileOutputStream(localFile); while((len = in.read(buf)) > -1){ fos.write(buf, 0, len); } fos.flush(); } finally { try { if (con != null) con.disconnect(); if (in != null) in.close(); if (fos != null) fos.close(); } catch (Exception e) { e.printStackTrace(); } } return BitmapFactory.decodeFile(localFile.getPath()); } }
ImageCache#saveBitmapメソッド内では引数で渡されてきたbitmapを
HashMapに保存するだけの動きに変更しました。
WorkerThreadではgetBitmapFromURLメソッドの中身を少し手を加えて
以下の「in」にinputStreamのインスタンスを保存するところは同じですが、
URL url = new URL(request.getUrl()); con = (HttpURLConnection) url.openConnection(); con.setUseCaches(true); con.setRequestMethod("GET"); con.setReadTimeout(500000); con.setConnectTimeout(50000); con.connect(); in = con.getInputStream();
その後以下のようにinputStreamインスタンスからbitmapをすぐに生成するのではなく
まず一度端末内にlocalFile(Fileのインスタンス)で指定したところにFileOutputStreamで
データを書きこんで、最後にBitmapを戻り値として返すためにBitmapFactory.decodeFileで
画象を保存したパスを指定してBitmapを返している。
byte[] buf = new byte[1024]; int len = 0; fos = new FileOutputStream(localFile); while((len = in.read(buf)) > -1){ fos.write(buf, 0, len); } fos.flush(); ・・・略 return BitmapFactory.decodeFile(localFile.getPath());
改造したUrlImageViewはgithubに
改造したUrlImageViewは僕のgithubに
アップしておきました。