2

I've looked at a few answers on here but they all ask different things. As a result I came up with the code below. I parse my JSON using an AsyncTask I have access to it in two places. The end of doInBackground(); and onPostExecute();

I tried the following code in both and got two different errors:

The code:

try {
   ImageView one = (ImageView)findViewById(R.id.one);
   String getImage = bookDet.get(0).image;
   Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(getImage).getContent());
   one.setImageBitmap(bitmap); 
} catch (MalformedURLException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

BookDet is the ArrayList that holds the data, it has been converted to a string and put there.

The error when I try it doInBackground():

E/AndroidRuntime(14007): FATAL EXCEPTION: AsyncTask #2
E/AndroidRuntime(14007): java.lang.RuntimeException: An error occured while executing doInBackground()
E/AndroidRuntime(14007):    at android.os.AsyncTask$3.done(AsyncTask.java:299)
E/AndroidRuntime(14007):    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
E/AndroidRuntime(14007):    at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
E/AndroidRuntime(14007):    at java.util.concurrent.FutureTask.run(FutureTask.java:239)
E/AndroidRuntime(14007):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
E/AndroidRuntime(14007):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
E/AndroidRuntime(14007):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
E/AndroidRuntime(14007):    at java.lang.Thread.run(Thread.java:856)
E/AndroidRuntime(14007): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

That's only part of it but the last line seems to be the key. There's only one AsyncTask so yeah.

Error when I do it in onPostExecute:

E/AndroidRuntime(16801): FATAL EXCEPTION: main
E/AndroidRuntime(16801): android.os.NetworkOnMainThreadException
E/AndroidRuntime(16801):    at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1117)
E/AndroidRuntime(16801):    at java.net.InetAddress.lookupHostByName(InetAddress.java:385)
E/AndroidRuntime(16801):    at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
E/AndroidRuntime(16801):    at java.net.InetAddress.getAllByName(InetAddress.java:214)
E/AndroidRuntime(16801):    at libcore.net.http.HttpConnection.<init>(HttpConnection.java:70)
E/AndroidRuntime(16801):    at libcore.net.http.HttpConnection.<init>(HttpConnection.java:50)
E/AndroidRuntime(16801):    at libcore.net.http.HttpConnection$Address.connect(HttpConnection.java:340)
E/AndroidRuntime(16801):    at libcore.net.http.HttpConnectionPool.get(HttpConnectionPool.java:87)
E/AndroidRuntime(16801):    at libcore.net.http.HttpConnection.connect(HttpConnection.java:128)
E/AndroidRuntime(16801):    at libcore.net.http.HttpEngine.openSocketConnection(HttpEngine.java:316)
E/AndroidRuntime(16801):    at libcore.net.http.HttpEngine.connect(HttpEngine.java:311)
E/AndroidRuntime(16801):    at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290)
E/AndroidRuntime(16801):    at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240)
E/AndroidRuntime(16801):    at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:81)
E/AndroidRuntime(16801):    at java.net.URLConnection.getContent(URLConnection.java:190)
E/AndroidRuntime(16801):    at java.net.URL.getContent(URL.java:447)

So I can't do it in the main thread...?

The best I can do at the moment is have a TextView that pulls that url to a string and that shows up just fine but that's obviously useless.

Any ideas? How do I get the image to show up from a parsed set of data that was done Asynchronously?

Appreciate anyones help. Thanks.

RED_
  • 2,997
  • 4
  • 40
  • 59

3 Answers3

4

I recommend you a different way that works like a charm: Android Query.

You can download that jar file from here: http://code.google.com/p/android-query/downloads/list

AQuery androidAQuery=new AQuery(this);

As an example:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

Using above code you can directly show your Image through url. Now below code is to get Bitmap Directly from the url:

androidAQuery.ajax(YOUR IMAGE URL,Bitmap.class,0,new AjaxCallback<Bitmap>(){
                        @Override
                        public void callback(String url, Bitmap object, AjaxStatus status) {
                            super.callback(url, object, status);

                            //You will get Bitmap from object.
                        }

});

This library is provided by Android itself, so use it and see the result whatever you want.

It's very fast and accurate, and using this you can find many more features like Animation when loading; getting a bitmap, if needed; etc.

Pratik Dasa
  • 7,439
  • 4
  • 30
  • 44
  • So I can just chuck this in my onPostExecute then and it'll work? I don't know the Image url, it's in a string called from JSON. – RED_ Oct 17 '13 at 13:03
  • no need to call this in onPostExecute(), just fetch yur image url from Json and pass it here in Android Query. It will download and display your image. – Pratik Dasa Oct 17 '13 at 13:04
  • Wait I don't understand. In onPostExecute I am putting the rest of my JSON data into TextViews. It has to be somewhere in my AsyncTask because thats' the only place I can have access to the imageurl, in the form of a string. – RED_ Oct 17 '13 at 13:06
  • yeah then execute android query code there, you can use it. After getting your parsed data use it. I have given you 2 demo code, one for download and display image and another one is to get Bitmap from url. – Pratik Dasa Oct 17 '13 at 13:10
  • The only trouble I seem to be having with this is that AjaxStatus doesn't seem to import and getDeviceWidth(), can I replace that with something else? – RED_ Oct 17 '13 at 13:22
  • dont use ajax method if you dont need Bitmap only, use above line that will display the image directly to your ImageView. – Pratik Dasa Oct 17 '13 at 13:26
  • Ah I see, I thought they had to be used together. Right OK that worked. Just need to resize things so it looks good against the device width. Thanks a lot. – RED_ Oct 17 '13 at 13:34
0

You should get the file asynchronously and then set it for your imageview.

I have used a lot this lightweight library to ease all the operations of making async HTTP calls: http://loopj.com/android-async-http/.

Example:

AsyncHttpClient client = new AsyncHttpClient();
String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
client.get("http://example.com/file.png", new BinaryHttpResponseHandler(allowedContentTypes) {
    @Override
    public void onSuccess(byte[] fileData)
    {
         Bitmap bmp = BitmapFactory.decodeByteArray(fileData, 0, fileData.length);

         if(imageView != null)
         {
             imageView.setImageBitmap(bmp);
         }
    }
});

Where imageView is a reference of your ImageView that you have previously got with:

imageView = (ImageView) view.findViewById(R.id.YOUR_IMAGEVIEW_ID);

Hope it helps.

fasteque
  • 4,309
  • 8
  • 38
  • 50
  • Is there a way I can do this without a library? What's the gist of it? Could I start a new AsyncTask under the other one and just pull the data again but only pull the image url or via the other one's onPostExecute? – RED_ Oct 17 '13 at 12:39
  • Of course there is, but here with 1 minute of integration and very few lines of code you have a very stable and robust solution (this library is used by Instagram and Pinterest by the way). Then, if this is the only place in your app where you need to do an async HTTP call, I can understand you don't want to integrate a library, so yes, use an AsyncTask as explained here: http://stackoverflow.com/questions/13085951/asynctask-imageview-from-image-web-using-setimagebitmap – fasteque Oct 17 '13 at 12:42
  • Cool, I'll give it go. That one has the URL already though and I won't get mine until I parse a chunk of data together. Hopefully it still works and I don't get the same error as above. In fact looking at that, I don't think it will work. – RED_ Oct 17 '13 at 12:48
  • Sure, remember to have set this permission in your manifest: – fasteque Oct 17 '13 at 12:50
  • Damn, still get a force close. – RED_ Oct 17 '13 at 12:56
  • Update your question. – fasteque Oct 17 '13 at 12:58
  • It's the same force close as before.The top one in my question. Because I don't know the URL, I have to reparse everything so I'm doing what I did in the first AsyncTask. – RED_ Oct 17 '13 at 13:00
  • The first crash is because probably you are manipulating a widget (view) in the doBackground method, while you can only do it in the onPostExecute(). – fasteque Oct 17 '13 at 13:04
0

you have to download the image in doBackground() and then set the result of doBackground as a Bitmap you have just downloaded. In onPostExecute(Bitmap bmp), you can set it to the desired imageView. Do not forget to change parameters of AsyncTask:

AsyncTask<SomeObject, Integer, Bitmap>

Code should look like this.

public class DownloadFilesTask extends AsyncTask<SomeObject, Integer, Bitmap> {
    protected Long doInBackground(SomeObject object) {

        //do the networking and parse json here

        try {
            String getImage = bookDet.get(0).image;
            Bitmap bitmap = BitmapFactory.decodeStream((InputStream)new URL(getImage).getContent()); 
            return bitmap;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } 
        return null;
    }

    protected void onPostExecute(Bitmap bmp) {

        //set the bitmap to image here

        ImageView one = (ImageView)findViewById(R.id.one);
        one.setImageBitmap(bmp);
    }
}
Alex
  • 688
  • 6
  • 8
  • My current AsyncTask is like this: public class HttpTask extends AsyncTask> {} So do you reckon start a new one or I can just fill in the Voids of this one? – RED_ Oct 17 '13 at 13:11
  • The first spot (first Void) is a spot for input parameter of doBackgorund(), the 2nd is a spot for progress change and the 3rd spot is for onBackgroud output and onPostExecute input. So you can create new AsycTask and start this one for this situation. For aech situation you can have different AssyncTask – Alex Oct 17 '13 at 13:20
  • Thanks for the but the answer below is easier since I only have to add one line of code. I think I can get away with using a small google provided library. I would have doubled my code with this. Shame a whole new AsyncTask is needed for this type of stuff but it makes sense why. – RED_ Oct 17 '13 at 13:37