假设我有一个源自某个图像文件的互联网的inputStream。
我希望获得有关图像文件的信息,然后才能对其进行解码。
它可用于多种用途,例如下采样以及在显示图像之前预览信息。
我试图通过使用BufferedInputStream包装inputStream来标记和重置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
用于从网址中获取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 ...
编辑:
似乎我的代码很好,有点......
在某些网站上(例如this one和this one),即使重置后也无法解码图像文件。
如果您解码位图(并使用inSampleSize),它可以解码它(只需要很长时间)。
现在的问题是它为什么会发生,我该如何解决它。
答案 0 :(得分:0)
我认为问题是调用mark(1024)会覆盖带有大值的mark()。如文档中所述:
在KITKAT之前,如果is.markSupported()返回true,则会调用is.mark(1024)。从KITKAT开始,情况就不再如此了。
如果读取的值大于此值,则可能导致reset()失败。
答案 1 :(得分:0)
(这是针对同一问题的解决方案,但是从磁盘读取时。我一开始并没有意识到你的问题是专门来自网络流。)
mark& amp;的问题一般情况下重置是BitmapFactory.decodeStream()
有时会重置您的商标。因此,重置以便进行实际读取被打破。
但是BufferedInputStream存在第二个问题:它可以使整个图像在你实际读取它的地方的内存中缓冲。根据您的使用情况,这可能会严重影响您的性能。 (很多分配意味着很多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;
}
}
}
这不会在读取期间分配任何额外的内存,即使标记已被清除也可以重置。
答案 2 :(得分:-1)
是否可以标记/重置流取决于流的实现。这些是可选操作,通常不受支持。您可以选择将流读入缓冲区,然后从该流中读取2x,或者只进行2x网络连接。
最简单的事情可能是写入ByteArrayOutputStream
,
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int count;
byte[] b = new byte[...];
while ((count = input.read(b) != -1) [
baos.write(b, 0, count);
}
现在要么直接使用baos.toByteArray()
的结果,要么创建ByteArrayInputStream
并重复使用,每次使用后都会调用reset()
。
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
可能听起来很傻,但没有魔力。您要么将数据缓冲在内存中,要么从源中读取2次。如果流确实支持标记/重置,它必须在它的实现中做同样的事情。
答案 3 :(得分:-1)
这是一个总是适用于我的简单方法:)
private Bitmap downloadBitmap(String url) {
// initilize the default HTTP client object
final DefaultHttpClient client = new DefaultHttpClient();
//forming a HttoGet request
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
//check 200 OK for success
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode +
" while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
// getting contents from the stream
inputStream = entity.getContent();
// decoding stream data back into image Bitmap that android understands
image = BitmapFactory.decodeStream(inputStream);
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// You Could provide a more explicit error message for IOException
getRequest.abort();
Log.e("ImageDownloader", "Something went wrong while" +
" retrieving bitmap from " + url + e.toString());
}
return image;
}