Android image fetching - android

What is the simplest way to fetch an image from a url in an android program?

I would strongly recommend using an AsyncTask instead. I originally used URL.openStream, but it has issues.
class DownloadThread extends AsyncTask<URL,Integer,List<Bitmap>>{
protected List<Bitmap> doInBackground(URL... urls){
InputStream rawIn=null;
BufferedInputStream bufIn=null;
HttpURLConnection conn=null;
try{
List<Bitmap> out=new ArrayList<Bitmap>();
for(int i=0;i<urls.length;i++){
URL url=urls[i];
url = new URL("http://mysite/myimage.png");
conn=(HttpURLConnection) url.openConnection()
if(!String.valueOf(conn.getResponseCode()).startsWith('2'))
throw new IOException("Incorrect response code "+conn.getResponseCode()+" Message: " +getResponseMessage());
rawIn=conn.getInputStream();
bufIn=new BufferedInputStream();
Bitmap b=BitmapFactory.decodeStream(in);
out.add(b);
publishProgress(i);//Remove this line if you don't want to use AsyncTask
}
return out;
}catch(IOException e){
Log.w("networking","Downloading image failed");//Log is an Android specific class
return null;
}
finally{
try {
if(rawIn!=null)rawIn.close();
if(bufIn!=null)bufIn.close();
if(conn!=null)conn.disconnect();
}catch (IOException e) {
Log.w("networking","Closing stream failed");
}
}
}
}
Closing the stream/connection and exception handling is difficult in this case. According to Sun Documentation you should only need to close the outermost stream, however it appears to be more complicated. However, I am closing the inner most stream first to ensure it is closed if we can't close the BufferedInputStream.
We close in a finally so that an exception doesn't prevent them being closed. We account for the possibility of the streams will being null if an exception prevented them from being initialised. If we have an exception during closing, we simply log and ignore this. Even this might not work properly if a runtime error occurs .
You can use the AsyncTask class as follows. Start an animation in onPreExecute. Update the progress in onProgressUpdate. onPostExecute should handle the actual images. Use onCancel to allow the user to cancel the operation. Start it with AsyncTask.execute.
It is worth noting that the source code and the license allow us to use the class in non-Android projects.

You do it many ways but the simplist way I can think of would be something like this:
Bitmap IMG;
Thread t = new Thread(){
public void run(){
try {
/* Open a new URL and get the InputStream to load data from it. */
URL aURL = new URL("YOUR URL");
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
/* Buffered is always good for a performance plus. */
BufferedInputStream bis = new BufferedInputStream(is);
/* Decode url-data to a bitmap. */
IMG = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
// ...send message to handler to populate view.
mHandler.sendEmptyMessage(0);
} catch (Exception e) {
Log.e(DEB, "Remtoe Image Exception", e);
mHandler.sendEmptyMessage(1);
} finally {
}
}
};
t.start();
And then add a handler to your code:
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
switch(msg.what){
case 0:
(YOUR IMAGE VIEW).setImageBitmap(IMG);
break;
case 1:
onFail();
break;
}
}
};
By starting a thread and adding a handler you are able to load the images without locking up the UI during download.

Related

why using DecodeStream(stream) take longer time in a thread than it is in main thread?

I am trying to receive image stream from desktop in the android phone. When I put decodeStream() method in my thread, then put decodestream() into runOnuiThread() to display in the imageView, it takes more than 7 secs. But when I try to directly read image from asset, and convert into inputstream, then decodeStream(), it may take 500ms, I don't know why it happens. The image is 100K.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
SocketConnect = new socketConnect();
SocketConnect.connectsocket("134.129.125.126", 8080);
while (true) {
data = new byte[2048 * 2048];
try {
read = SocketConnect.getInputStream().read(data, 0,
data.length);
input = SocketConnect.getInputStream();
System.out.println("getInputStream()");
bitmap = BitmapFactory.decodeStream(input);
System.out.println("decodestream()");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
System.out.println(e);
}
runOnUiThread(new Runnable() {
public void run() {
image.setImageBitmap(bitmap);
System.out.println("setImage at less than 500");
}
});
}
}
});
thread.start();
client side should sent the image very 5 sec.
If I try to read same image from asset, Image display immedately.
try {
inputasset=getAssets().open("good1.jpg");
Bitmap bit = BitmapFactory.decodeStream(inputasset);
image.setImageBitmap(bit);
} catch (IOException e) {
TODO Auto-generated catch block
e.printStackTrace();
}
socketConnect class
public class socketConnect {
private Socket clientSocket;
private InputStream input;
private Bitmap bMap;
public InputStream getInputStream() {
return input;
}
public void connectsocket(String ipString, int port)
{
System.out.println("starts");
try {
// clientSocket = new Socket("134.129.125.172",8080);
System.out.println("starts");
clientSocket = new Socket(ipString, port);
System.out.println("AsyncTask: Connect to 134.129.125.126");
input = clientSocket.getInputStream();
System.out.println("AsyncTask: get the inputStream");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
read = SocketConnect.getInputStream().read(data, 0,
data.length);
input = SocketConnect.getInputStream();
System.out.println("getInputStream()");
bitmap = BitmapFactory.decodeStream(input);
System.out.println("decodestream()");
You read the data from the input stream, then you ask the BitmapFactory to read the data from the input stream. But you already read it! Remove the call to read -- you're stealing data from the BitmapFactory.
My guess would be that the server side is not properly signaling that the end of the file has been reached. I've encountered this previously because the web server implementation might not tell you the length of the file it is sending. If it doesn't set the content length then the input stream keeps trying to read forever. My guess is that it takes a number of minutes because this is the default socket timeout value.
So, the input stream from the socket reads as much as it can and keeps waiting for more data. After waiting several minutes (although usually 5 minutes is the default timeout) the socket closes and you get your image.
I'm not entirely sure that a content length will help, another option might be that the server closes the connection after all the data is sent.
Clearly, if you're trying to stream new images though, you need to do something different than you are. Perhaps have your server send a file header that the reader interprets. This header might include the length of the data being sent and then the client you write stops reading after reaching this length. In this case you'll need to read the image data into a buffer and then have the BitmapFactory read the buffer instead of the stream.
An alternate approach would have the server close the connection after sending the data for each image and then the client would reconnect to get the next one.
Bottom line, not all InputStreams are created equal and a SocketInputStream doesn't know when to close without a content length or the socket being closed.

how to get bitmap information and then decode bitmap from internet-inputStream?

background
suppose i have an inputStream that was originated from the internet of a certain image file.
i wish to get information about the image file and only then to decode it.
it's useful for multiple purposes, such as downsampling and also previewing of information before the image is shown.
the problem
i've tried to mark&reset the inputStream by wrapping the inputStream with a BufferedInputStream , but it didn't work:
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
for getting the inputStream out of a url, i use:
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;
}
the question
how can i make the code handle the marking an resetting ?
it works perfectly with resources (in fact i didn't even have to create a new BufferedInputStream for this to work) but not with inputStream from the internet...
EDIT:
it seems my code is just fine, sort of...
on some websites (like this one and this one), it fails to decode the image file even after reseting.
if you decode the bitmap (and use inSampleSize) , it can decode it fine (just takes a long time).
now the question is why it happens, and how can i fix it.
I believe the problem is that the call to mark() with the large value is overwritten by a call to mark(1024). As described in the documentation:
Prior to KITKAT, if is.markSupported() returns true, is.mark(1024) would be called. As of KITKAT, this is no longer the case.
This may be resulting in a reset() fail if reads larger than this value are being done.
(Here is a solution for the same problem, but when reading from disk. I didn't realize at first your question was specifically from a network stream.)
The problem with mark & reset in general here is that BitmapFactory.decodeStream() sometimes resets your marks. Thus resetting in order to do the actual read is broken.
But there is a second problem with BufferedInputStream: it can cause the entire image to be buffered in memory along side of where ever you are actually reading it into. Depending on your use case, this can really kill your performance. (Lots of allocation means lots of GC)
There is a really great solution here:
https://stackoverflow.com/a/18665678/1366
I modified it slightly for this particular use case to solve the mark & reset problem:
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;
}
}
}
This won't allocate any extra memory during reads, and can be reset even if the marks have been cleared.
whether you can mark / reset a stream depends on the implementation of the stream. those are optional operations and aren't typically supported. your options are to read the stream into a buffer and then read from that stream 2x, or just make the network connection 2x.
the easiest thing is probably to write into a ByteArrayOutputStream,
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int count;
byte[] b = new byte[...];
while ((count = input.read(b) != -1) [
baos.write(b, 0, count);
}
now either use the result of baos.toByteArray() directly, or create a ByteArrayInputStream and use that repeatedly, calling reset() after consuming it each time.
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
that might sound silly, but there's no magic. you either buffer the data in memory, or you read it 2x from the source. if the stream did support mark / reset, it'd have to do the same thing in it's implementation.
Here is a simple method that always works for me :)
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;
}

Android stop download

In my application I download and parse a html page. However, I want to be able to stop the download in its tracks (i.e. when the user hits cancel).
This is the code I use now, which is being called from doInBackground from ASyncTask.
How do I cancel this request from outside of the ASyncTask?
I currently use htmlcleaner
HtmlCleaner cleaner = new HtmlCleaner();
CleanerProperties props = cleaner.getProperties();
props.setAllowHtmlInsideAttributes(true);
props.setAllowMultiWordAttributes(true);
props.setRecognizeUnicodeChars(true);
props.setOmitComments(true);
try {
URL url = new URL(urlstring);
URLConnection conn = url.openConnection();
TagNode node = cleaner.clean(new InputStreamReader(conn.getInputStream()));
return node;
} catch (Exception e) {
failed = true;
return;
}
Can't you use AsyncTask.cancel()? You should be able to then use the onCancelled callback to return to the main activity..
Ok, I believe I've solved this.
In my Activity class I have a variable (boolean) failed. Also, I have a private Downloader class within the activity which extends ASyncTask. This way, the Downloader class has access to the failed boolean. When the Activity launches, it starts the Downloader task and a progress dialog pops up. When the task finishes, it closes the dialog and then goes on processing the downloaded content.
However, when the user cancels the progress dialog, failed is set to true, and the user is sent back to the previous activity by a call to finished. In the meantime, Downloader is still busy downloading. Because the results are now unneccessary, we want it to stop using resources asap. In order to accomplish this, I have broken up the doInBackground method in as much steps as possible. After each step I check if failed is still false, when it is set to true, it simply doesn't go to the next step. See it in action below. Furthemore, the BufferedReader reader is public, and in the onCancelled method I execute reader.close(). This will throw all sorts of exceptions, but these are properly caught.
public void DoInBackground(.........) {
try {
URL url = new URL(uri);
URLConnection conn = url.openConnection();
if (!failed) {
isr = new InputStreamReader(conn.getInputStream());
if (!failed) {
reader = new BufferedReader(isr);
publishProgress(1);
if (!failed) {
TagNode node = cleaner.clean(reader);
publishProgress(2);
return node;
}
}
}
} catch (Exception e) {
failed = true;
Log.v("error",""+e);
}
}
#Override
protected void onCancelled() {
failed = true;
if (reader != null)
try {
reader.close();
} catch (IOException e) {
failed = true;
}
if (isr != null)
try {
isr.close();
} catch (IOException e) {
}
}
I know that I could have broken up the downloading process in even tinier bits, but I am downloading very small files, so it's not that important.

Intermittent HttpClient GET issue on android

I have an android application i have been working on that downloads an image from a server, reads it into a bitmap and displays it on an ImageView
This works great most of the time, but every so often, it goes through the process (There is a ProgressDialog saying "Fetching image...") and once its done nothing gets displayed. There has not been anything in logcat that even seems to remotely relate to this.
Here is the code:
Bitmap image = null;
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(webService + "?cmd=get");
try
{
HttpResponse resp = client.execute(get);
Log.i("PhotoRouletteDebug", "Resp buffer size: " + (int)resp.getEntity().getContentLength());
InputStream is = resp.getEntity().getContent();
BufferedInputStream buf = new BufferedInputStream(is, (int)resp.getEntity().getContentLength());
image = BitmapFactory.decodeStream(buf);
// clean up
buf.close();
is.close();
Even when nothing is getting displayed, the Resp content length always reports a correct size but still, nothing ends up getting displayed.
This code is called from an AsyncTask, but only 1 task is ever called at a time.
This is driving me insane, i have no idea why its keeps doing this.
Edit: Here is the code that sets the imageView
// AsyncTask for Getting a new image from the queue
protected class GetImageTask extends AsyncTask<String, String, Bitmap>
{
protected void onPreExecute()
{
// lets show a progress dialog so the user knows something is going on
progressDialog = ProgressDialog.show(PhotoRoulette.this, "", "Fetching image...", true);
}
protected void onPostExecute (Bitmap image)
{
// we got a new photo so lets display it where it needs to be displayed
try
{
photoView = (ImageView)findViewById(R.id.photoView);
photoView.setImageBitmap(image);
}
catch (Exception e)
{
Log.e("Debug", "Something absolutely retarded happened", e);
}
// hide the progress dialog - we're all done
progressDialog.dismiss();
}
protected Bitmap doInBackground(String... urls)
{
// Get a new Bitmap Queue Image
Bitmap image = imageHandler.getQueueImage();
return image;
}
}
You didn't show us the code for displaying the image, so we don't know for sure that that code is correct. Perhaps the problem lies there?
But assuming that the problem is that the image is getting corrupted, here's how I'd start debugging this: Wrap buf with a PushbackInputStream. Read the bytes out of buf and save them to a file; then push those same bytes back into the PushbackInputStream. Then pass the PushbackInputStream into BitmapFactory.decodeStream. If the image is displayed successfully, then delete the file (manually or programatically.) Otherwise, you can now examine the bitmap at your leisure.

Android: Problems downloading images and converting to bitmaps

I am working on an application that downloads images from a url. The problem is that only some images are being correctly downloaded and others are not.
First off, here is the problem code:
public Bitmap downloadImage(String url) {
HttpClient client = new DefaultHttpClient();
HttpResponse response = null;
try {
response = client.execute(new HttpGet(url));
} catch (ClientProtocolException cpe) {
Log.i(LOG_FILE, "client protocol exception");
return null;
} catch (IOException ioe) {
Log.i(LOG_FILE, "IOE downloading image");
return null;
} catch (Exception e) {
Log.i(LOG_FILE, "Other exception downloading image");
return null;
}
// Convert images from stream to bitmap object
try {
Bitmap image = BitmapFactory.decodeStream(response.getEntity().getContent());
if(image==null)
Log.i(LOG_FILE, "image conversion failed");
return image;
} catch (Exception e) {
Log.i(LOG_FILE, "Other exception while converting image");
return null;
}
}
So what I have is a method that takes the url as a string argument and then downloads the image, converts the HttpResponse stream to a bitmap by means of the BitmapFactory.decodeStream method, and returns it. The problem is that when I am on a slow network connection (almost always 3G rather than Wi-Fi) some images are converted to null--not all of them, only some of them. Using a Wi-Fi connection works perfectly; all the images are downloaded and converted properly.
Does anyone know why this is happening? Or better, how can I fix this? How would I even go about testing to determine the problem? Any help is awesome; thank you!
This is a known issue with the JPEG decoder. There are two solutions. Either you download the entire image in a byte[] array using a ByteInputStream and then decode the array (this is what I do in code.google.com/p/shelves.) Another solution is to create a wrapper InputStream as shown below:
public class PatchInputStream extends FilterInputStream {
public PatchInputStream(InputStream in) {
super(in);
}
public long skip(long n) throws IOException {
long m = 0L;
while (m < n) {
long _m = in.skip(n-m);
if (_m == 0L) break;
m += _m;
}
return m;
}
}
Even with a WiFi connection some bitmaps (in particular .BMPs) will not be decoded. It's just a buggy decoder that can not deal with delays. If you search stackoverflow you will find some other solutions such as wrapping the HTTP stream in a buffered http entity. That works but depending on image size can take up a lot of memory. For our commerical product we ended up downloading the http stream to sdcard and then using Bitmapfactory on the dowloaded file. It's somewhat slower but 100% reliable.

Categories

Resources