我100%确定这里的问题与实际图像有关。但是我希望解决方案是图像的一些属性,将来会帮助其他人。
图片:
the photo in question http://soundwave.robotsidekick.com/mlsphotos.jpg
我尝试过多种方式加载此图片。我已下载并尝试将其加载到ImageView
:
final ImageView v = (ImageView) findViewById(R.id.image);
v.setImageResource(R.drawable.photo);
v.invalidate();
我尝试从网址加载:
final String[] params = new String[] {
"",
};
(new AsyncTask<String, Bitmap, Bitmap>()
{
@Override
protected Bitmap doInBackground(final String... params)
{
Bitmap ret = null;
for (final String url : params)
{
try
{
Log.e(TAG, url);
ret = BitmapFactory.decodeStream((new URL(url)).openStream());
publishProgress(ret);
}
catch (final MalformedURLException e)
{
Log.e(TAG, "Malformed URL", e);
}
catch (final IOException e)
{
Log.e(TAG, "IO Exception", e);
}
}
return ret;
}
@Override
protected void onProgressUpdate(final Bitmap... values)
{
super.onProgressUpdate(values);
for (final Bitmap result : values)
{
if (result != null)
{
final ImageView v = (ImageView) MainActivity.this.findViewById(R.id.image);
v.setImageBitmap(result);
v.invalidate();
}
}
}
}).execute(params);
我还尝试在WebView
中加载图片,如下所示:
final WebView webview = (WebView) findViewById(R.id.webview);
webview.loadData("<html><body><img src=\"" + url + "\"></body></html>", "text/html", "utf-8");
webview.invalidate();
我也尝试在浏览器(应用程序)中加载图像,但这不起作用。
这些都不起作用,但是如果我在Android上将网址加载到Chrome中它运行良好(不在浏览器中),如果我在桌面浏览器(Chrome,Firefox等)上加载图像,它会加载很多。我已经检查过mime类型与扩展名匹配,我只是不知所措。
修改
来自InputStream
的图像有一种解决方法,在流完成之前,位图处理会在流上的数据用完。问题工作记录在this question和this bug。
然而,这是一个损坏的图像,其数据过早结束。我知道这意味着我已经陷入了困境,但我希望能有一些比Android更好的错误处理方式让我回到null
并且我输了。 iOS,Chrome(在设备和计算机上)以及大多数其他地方似乎有更好的错误处理。我可以在Android上做些什么来处理损坏的jpgs吗?
编辑2
这里必须有一个解决方案,因为我的设备上的Chrome可以优雅地处理这种情况。然而,我最接近修复这个是以下代码:
final InputStream is = (new URL(url)).openStream();
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final int size = 1024;
int len = -1;
byte[] buf = new byte[size];
while ((len = is.read(buf, 0, size)) != -1)
{
bos.write(buf, 0, len);
}
buf = bos.toByteArray();
// buf is now filled with the corrupted bytes of the image
ret = BitmapFactory.decodeByteArray(buf, 0, buf.length);
// ret is null because it was a corrupt jpg
使用该代码,我可以检查是否有字节并且图像未被解码。然后,至少我可以告诉我有一个损坏的图像(或不是图像),并可以向用户报告一些稍微有用的东西(比如嘿我这里有16K的图像,但我肯定不知道该怎么做) 。
任何人都知道Chrome如何在损坏之前尽可能多地解码图像?
答案 0 :(得分:2)
我在Photoshop CS6中打开了文件,它说文件可能已损坏,可能已被截断或不完整。该文件可以打开。如果我将其保存在Photoshop中而不进行任何更改,则可以在Android中使用。我恐怕不知道图像到底有什么问题。
答案 1 :(得分:2)
以下是关于JPGs from Wikipedia和此处a question that ultimate led me to the solution的重要信息。
我只是将图像字节的两个关闭jpeg结尾附加到流中,以便说服解码器流完成了图像数据。这种方法存在缺陷,因为JPG可以在其中包含JPG,这意味着附加一组图像字节结束,并不能保证我们关闭所有图像。
在下面的两个解决方案中,我假设is
是JPG图像的输入流。这两个常量也是定义的:
private static final int JPEG_EOI_1 = 0xFF;
private static final int JPEG_EOI_2 = 0xD9;
这个方法我们将所有字节读入内存然后尝试解码字节:
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final int size = 1024;
int len = -1;
final byte[] buf = new byte[size];
try
{
while ((len = is.read(buf, 0, size)) != -1)
{
bos.write(buf, 0, len);
}
bos.write(JPEG_EOI_1);
bos.write(JPEG_EOI_2);
final byte[] bytes = bos.toByteArray();
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
catch (final IOException ex)
{
return null;
}
catch (final Exception ex)
{
return null;
}
此方法创建一个流包装器,确保最后两个字节是图像字节的JPG结尾:
return BitmapFactory.decodeStream(new JpegClosedInputStream(is));
// And here's the stream wrapper
class JpegClosedInputStream extends InputStream
{
private final InputStream inputStream;
private int bytesPastEnd;
private JpegClosedInputStream(final InputStream iInputStream)
{
inputStream = iInputStream;
bytesPastEnd = 0;
}
@Override
public int read() throws IOException
{
int buffer = inputStream.read();
if (buffer == -1)
{
if (bytesPastEnd > 0)
{
buffer = JPEG_EOI_2;
}
else
{
++bytesPastEnd;
buffer = JPEG_EOI_1;
}
}
return buffer;
}
}
答案 2 :(得分:-2)
执行setcontentview并从xml中显示它:
//photo.xml
<ImageView
android:id="@+id/picture"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:src="@drawable/photo" />
/>
//Photo.java
setContentView(R.layout.photo);