今更なネタです! (*’-‘*)
Android での開発を行っていると、頻繁に In/Out のStream を扱いますが、Stream を扱いながらプログレスバーを更新する方法を検索してみると、while 文などで自ら stream をループさせつつ、AsyncTask#publishProgress() するといった例が見つかります。
しかし、例えば BitmapFactory#decodeStream などのように、直接 Stream を扱うことが出来るメソッドも存在するため、Stream は Stream で、そのまま簡単に進捗状況をモニターしたい場面も多々あります。
本稿では、そのようなケースで利用できる別の方法を簡単にまとめてみました。
考え方としては、InputStream や OutputStream を、進捗状況をモニターする為の別の Stream でラップするだけです。具体的には、次のコードの強調部分を見てもらうと分かりやすいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/** * 画像のURLから非同期に画像を読み込む。 * * @param imageUrl * @param listener 独自の結果ハンドラ * @return */ public Bitmap asyncLoadImage( final String imageUrl, final MyLoadingListener listener) { new Thread() { @Override public void run() { // HTTPリクエストの作成 mHttpClient = new DefaultHttpClient(); HttpParams params = mHttpClient.getParams(); HttpConnectionParams.setConnectionTimeout(params, 5 * 1000); HttpConnectionParams.setSoTimeout(params, 15 * 1000); HttpResponse response; HttpGet request = new HttpGet(imageUrl); try { // 読み込みの実行とステータスコードの判定 response = getHttpClient().execute(request); int responseCode = response.getStatusLine().getStatusCode(); if (responseCode / HttpStatus.SC_OK != 1) { listener.onLoadingFailed(); return; } // Stream と Content size の取得 InputStream is = response.getEntity().getContent(); final long contentSize = (int) response .getEntity().getContentLength(); // 進捗状況を監視する別の Stream でラップする is = new MonitorInputStream(is) { // MonitorInputStreamListener を通じて進捗状況をモニターする @Override public void onStreamRead(long totalReadSize, int size) { listener.onLoadingProgressUpdated(totalReadSize, contentSize); } }; is = new BufferedInputStream(is); // Bitmap を生成してコールバックに渡す Bitmap bitmap = BitmapFactory.decodeStream(is); listener.onLoadingSuccess(bitmap); } catch (ClientProtocolException e) { e.printStackTrace(); listener.onLoadingFailed(); } catch (IOException e) { e.printStackTrace(); listener.onLoadingFailed(); } finally { listener.onLoadingFinally(); } } }.start(); } |
ここで、「MonitorInputStream」クラスが、進捗状況を監視するための InputStream です。このクラスは、FilterInputStream を継承し、read メソッドをオーバーライドして読み込まれたデータの流量を監視し、「MonitorInputStreamListener」インターフェースを介して進捗状況を外部に送信します。
なお、MonitorInputStream 自体が MonitorInputStreamListener を implements していますので、上の例では MonitorInputStream の onStreamRead をオーバーライドしています。
MonitorInputStream クラス
MonitorInputStream クラスは下記のような実装になっています。特に難しいことはしていません。read メソッドをオーバーライドし、進捗状況を監視して、指定の MonitorInputStreamListener に進捗状況を通知します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
package jp.foreignkey.java.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** * データの流量を監視する InputStream * 監視には、MonitorInputStreamListener を利用する。 * * @author yuka2py */ public class MonitorInputStream extends FilterInputStream implements MonitorInputStreamListener { private int mReadSize = 0; private MonitorInputStreamListener mListener; public MonitorInputStream(InputStream in) { super(in); mReadSize = 0; mListener = this; } @Override public void reset() throws IOException { super.reset(); if (markSupported()) { mReadSize = 0; } } @Override public int read() throws IOException { int size = super.read(); publishProgress(1); return size; } @Override public int read(byte[] b) throws IOException { int size = super.read(b); publishProgress(size); return size; } @Override public int read(byte[] b, int off, int len) throws IOException { int size = super.read(b, off, len); publishProgress(size); return size; } /** * リスナーをセットする * * @param listener */ public void setStreamMonitor(MonitorInputStreamListener listener) { mListener = listener; } private void publishProgress(int size) { if (0 < size) { mReadSize = mReadSize + size; mListener.onStreamRead(mReadSize, size); } if (-1 == size) { mListener.onStreamEnd(mReadSize); } } @Override public void onStreamRead(long totalReadSize, int size) { } @Override public void onStreamEnd(long totalReadSize) { } } |
MonitorInputStreamListener インターフェース
進捗状態の通知を受け付けるインターフェースです。こんな感じで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package jp.foreignkey.java.io; /** * 読み込みサイズをモニターする為のインターフェース * * @author yuka2py */ public interface MonitorInputStreamListener { /** * @param totalReadSize 読み込まれた全サイズ * @param read 今回読み込まれたサイズ */ public void onStreamRead(long totalReadSize, int read); /** * @param totalReadSize 読み込まれた全サイズ */ public void onStreamEnd(long totalReadSize); } |
以上です。
全て InputStream の例で示しますが、OutputStream も同様の考え方で実現できますし、要は Stream なので汎用性も高いですので、こういうやり方も一つの方法として、是非お手元にどうぞーでございます。