BitmapFactory.decodeStream возвращает ноль при загрузке изображения из Интернета.
Я пытаюсь загрузить изображение с URL-адреса, используя страницу примера Google. Я прочитал, когда я использую InputStream в методе BitmapFactory.decodeStream, я не могу использовать дважды. Я пытаюсь это сделать, но это не работает, потому что оно возвращает ноль в декодированном изображении, и я не знаю, что я могу сделать.
Это мой код:
Эта часть находится в методе doInBackground в классе AsyncTask
Bitmap bitmapImage;
URL imageUrl = null;
try {
imageUrl = new URL(url[0]);
HttpGet httpRequest = null;
httpRequest = new HttpGet(imageUrl.toURI());
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = (HttpResponse) httpclient.execute(httpRequest);
HttpEntity entity = response.getEntity();
BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
InputStream instream = bufHttpEntity.getContent();
bitmapImage = CommonMethods.decodeSampledBitmapFromResource(instream, thumb_width, thumb_width);
instream.close();
return bitmapImage;
} catch (URISyntaxException e) {
e.printStackTrace();
return null;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
public static Bitmap decodeSampledBitmapFromResource(InputStream instream,
int reqWidth, int reqHeight) throws IOException {
//Copy instream for decode twice
ByteArrayOutputStream out = new ByteArrayOutputStream();
copy(instream,out);
ByteArrayInputStream instream2 = new ByteArrayInputStream(out.toByteArray());
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(instream, null, options);
instream2.close();
options.inJustDecodeBounds = false;
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
return BitmapFactory.decodeStream(instream, null, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}
//Copy instream method
public static void copy(InputStream input, OutputStream output) throws IOException{
byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
}
5 ответов
Нашел код, который будет работать на вас
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inSampleSize = 2;
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream, null, options);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
Пожалуйста, замените соответствующую переменную, и если вы хотите масштабировать изображение, вы можете масштабировать его после получения растрового изображения.
BitmapFactory.decodeStream возвращает значение NULL, потому что inputtream используется дважды, я не пробовал ваш код, но он выглядит нормально, или, возможно, я ошибаюсь. Во всяком случае, у меня есть лучшее решение. Просто используйте BufferedInputStream, чтобы обернуть inputStream, и перед вашим вторым чтением сначала вызовите "reset". Обратите внимание, что обычные inputStreams не поддерживают "сброс", вы можете вызвать его, но ничего не произойдет. Мой код:
public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream,
int reqWidth, int reqHeight)
throws IOException {
if (!widthHeightCheck(reqWidth, reqHeight))
return BitmapFactory.decodeStream(inputStream);
// First decode with inJustDecodeBounds=true to check dimensions
if (!(inputStream instanceof BufferedInputStream)) {
inputStream = new BufferedInputStream(inputStream);
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Rect rect = new Rect(-1, -1, -1, -1);
BitmapFactory.decodeStream(inputStream, rect, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
inputStream.reset();
return BitmapFactory.decodeStream(inputStream, rect, options);
}
Я думаю, что вы можете достичь этого, обернув поток, который вы получаете от httpEntity, в пользовательский WrappedStream. Этот WrappedStream будет передавать второй поток ввода при чтении исходного потока. (это делается с помощью PipedStream)
После получения размера изображения с этим кодом:
options.inJustDecodeBounds = true;
WrappedStream wrappedStream = new WrappedStream(instream);
BitmapFactory.decodeStream(wrappedStream, null, options);
Ты можешь позвонить
InputStream reReadStream = wrappedStream.getReReadStream();
options.inJustDecodeBounds = false;
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
return BitmapFactory.decodeStream(reReadStream, null, options);
И, наконец, вот реализация WrappedStream (он просто делегирует все вызовы обернутому inputStream и записывает все байты, которые читаются (или пропускаются) в pipedOutputStream)
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
/** Simple class wrapping an InputStream and feeding a secondary InputStream
* to re-read the data that was originally available in the inputStream.
**/
public class WrappedStream extends InputStream {
private InputStream urlStream;
private PipedOutputStream pipedStream;
public WrappedStream(InputStream urlStream) {
this.urlStream = urlStream;
this.pipedStream = new PipedOutputStream();
}
/**
* return a fresh InputStream to re-read the data
*/
public InputStream getReReadStream() throws IOException {
return new PipedInputStream(pipedStream);
}
@Override
public int available() throws IOException {
return urlStream.available();
}
@Override
public void close() throws IOException {
urlStream.close();
}
@Override
public void mark(int readlimit) {
urlStream.mark(readlimit);
}
@Override
public boolean markSupported() {
return urlStream.markSupported();
}
@Override
public int read() throws IOException {
int b = urlStream.read();
pipedStream.write(b);
return b;
}
@Override
public int read(byte[] buffer) throws IOException {
int l = urlStream.read(buffer);
pipedStream.write(buffer);
return l;
}
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
int l = urlStream.read(buffer, offset, length);
pipedStream.write(buffer, offset, length);
return l;
}
@Override
public void reset() throws IOException {
urlStream.reset();
}
@Override
//bytes skipped must available on the re-read stream so we read and write them.
public long skip(long byteCount) throws IOException {
long bytesToSkip = byteCount;
long skippedBytes = 0;
//ugly trick required to not loosing bytes if we ever skip more than Integer.MAX_VALUE bytes
while(bytesToSkip>Integer.MAX_VALUE){
_skip(Integer.MAX_VALUE);
bytesToSkip -=Integer.MAX_VALUE;
skippedBytes +=Integer.MAX_VALUE;
}
byte[] b = new byte[(int)bytesToSkip];
skippedBytes += read(b);
return skippedBytes;
}
private int _skip(int byteCount) throws IOException {
byte[] b = new byte[(int)byteCount];
return read(b);
}
}
Обратите внимание, что я не тестировал этот код. Это просто, чтобы дать вам несколько идей о том, как решить вашу проблему.
Еще один момент: даже если этот код никогда не создаст огромный битовый массив, весь поток будет храниться в памяти до тех пор, пока не будет создан масштабированный битовый массив.
Вот метод загрузки растрового изображения с сервера с меньшим количеством кода, который вы можете выполнить по вашему требованию
Bitmap downloadBitmap(String url)
{
Bitmap image = null;
InputStream in = null;
try
{
in = new java.net.URL(url).openStream();
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
image = BitmapFactory.decodeStream(new FlushedInputStream(in),null,opts);
in.close();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
return image;
}
в приведенном выше коде мы используем opts.inSampleSize = 2; это означает, что растровое изображение будет уменьшено до половины его первоначального размера, чтобы избежать исключения памяти, мы должны сделать это, если мы загружаем много изображений
какой-то другой класс используется в нем
static class FlushedInputStream extends FilterInputStream
{
public FlushedInputStream( InputStream inputStream )
{
super(inputStream);
}
@Override
public long skip(long n) throws IOException
{
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n)
{
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L)
{
int byte1 = read();
if (byte1 < 0)
{
break; // we reached EOF
}
else
{
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
Пожалуйста, используйте приведенный ниже код для загрузки и отображения изображения в режиме просмотра изображений.
public class image extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap bitmap = DownloadImage("http://www.gophoto.it/view.php?i=http://1.bp.blogspot.com/-2LTvCCufBKc/T3L3KgcTj2I/AAAAAAAABbQ/Ki60e1LU9sE/s1600/Sachin%2BTendulkar.png");
ImageView img = (ImageView) findViewById(R.id.img);
img.setImageBitmap(bitmap);
}
private InputStream OpenHttpConnection(String urlString) throws IOException {
InputStream in = null;
int response = -1;
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
if (!(conn instanceof HttpURLConnection))
throw new IOException("Not an HTTP connection");
try {
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
response = httpConn.getResponseCode();
if (response == HttpURLConnection.HTTP_OK) {
in = httpConn.getInputStream();
}
} catch (Exception ex) {
throw new IOException("Error connecting");
}
return in;
}
private Bitmap DownloadImage(String URL) {
Bitmap bitmap = null;
InputStream in = null;
try {
in = OpenHttpConnection(URL);
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return bitmap;
}
}