9

バックグラウンド

特定の画像ファイルのインターネットから発信されたinputStreamがあるとします。

画像ファイルに関する情報を取得してから、それをデコードしたいと考えています。

ダウンサンプリングや、画像が表示される前の情報のプレビューなど、複数の目的に役立ちます。

問題

inputStream を BufferedInputStream でラップして、inputStream をマーク&リセットしようとしましたが、うまくいきませんでした:

inputStream=new BufferedInputStream(inputStream);
inputStream.mark(Integer.MAX_VALUE);
final BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeStream(inputStream,null,options);
//this works fine. i get the options filled just right.

inputStream.reset();
final Bitmap bitmap=BitmapFactory.decodeStream(inputStream,null,options);
//this returns null

URLからinputStreamを取得するには、次を使用します。

public static InputStream getInputStreamFromInternet(final String urlString)
  {
  try
    {
    final URL url=new URL(urlString);
    final HttpURLConnection urlConnection=(HttpURLConnection)url.openConnection();
    final InputStream in=urlConnection.getInputStream();
    return in;
    }
  catch(final Exception e)
    {
    e.printStackTrace();
    }
  return null;
  }

質問

コードがリセットのマーキングを処理するようにするにはどうすればよいですか?

リソースでは完全に機能します(実際、これが機能するために新しいBufferedInputStreamを作成する必要さえありませんでした)が、インターネットからのinputStreamでは機能しません...


編集:

私のコードは問題ないようです...

一部の Web サイト (このサイトやこのサイトなど) では、リセットしても画像ファイルのデコードに失敗します。

ビットマップをデコードする (そして inSampleSize を使用する) 場合、問題なくデコードできます (時間がかかります)。

問題は、なぜそれが起こるのか、どうすれば修正できるのかということです。

4

4 に答える 4

0

これは同じ問題の解決策ですが、ディスクから読み取る場合です。最初は、あなたの質問が特にネットワークストリームからのものであることに気づきませんでした。

ここでの一般的なマークとリセットの問題は、マークがリセットされるBitmapFactory.decodeStream() ことがあるということです。したがって、実際の読み取りを行うためのリセットは壊れています。

しかし、BufferedInputStream には 2 つ目の問題があります。実際に画像を読み込んでいる場所とは別に、画像全体がメモリにバッファリングされる可能性があります。ユースケースによっては、これによりパフォーマンスが大幅に低下する可能性があります。(たくさんの割り当てはたくさんの GC を意味します)

ここに本当に素晴らしい解決策があります: https://stackoverflow.com/a/18665678/1366

マークとリセットの問題を解決するために、この特定のユースケース用に少し変更しました。

public class MarkableFileInputStream extends FilterInputStream
{
    private static final String TAG = MarkableFileInputStream.class.getSimpleName();

    private FileChannel m_fileChannel;
    private long m_mark = -1;

    public MarkableFileInputStream( FileInputStream fis )
    {
        super( fis );
        m_fileChannel = fis.getChannel();
    }

    @Override
    public boolean markSupported()
    {
        return true;
    }

    @Override
    public synchronized void mark( int readlimit )
    {
        try
        {
            m_mark = m_fileChannel.position();
        }
        catch( IOException ex )
        {
            Log.d( TAG, "Mark failed" );
            m_mark = -1;
        }
    }

    @Override
    public synchronized void reset() throws IOException
    {
        // Reset to beginning if mark has not been called or was reset
        // This is a little bit of custom functionality to solve problems
        // specific to Android's Bitmap decoding, and is slightly non-standard behavior
        if( m_mark == -1 )
        {
            m_fileChannel.position( 0 );
        }
        else
        {
            m_fileChannel.position( m_mark );
            m_mark = -1;
        }
    }
}

これにより、読み取り中に余分なメモリが割り当てられず、マークがクリアされた場合でもリセットできます。

于 2014-06-03T18:52:58.487 に答える