0

I have almost finished my project, only one thing remain to improve.

My app is a music quiz, that retrieves all the informations about author, song title, previewUrl ecc. from the Itunes Store using the apposite Search API.

When a user choose a genre to play with, I must say to the user to wait for 4-5 seconds because of the computation that fills the List containing all the informations.

I call the Asynctask that retrieve these informations like this:

JsonCanzoni recuperoCanzoni = new JsonCanzoni(arrayGenere,Canzone.this);

    try {
        recuperoCanzoni.execute().get();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ExecutionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

The class (extends Asynctask) that do these operation is the following:

class JsonCanzoni extends AsyncTask<Void, Void, Void>
{
    List<String> canzoni = new ArrayList<String>(5);
    ProgressDialog pDialog;
    int[] arrayGenere;
    Context context;

    public JsonCanzoni(int[] arrayGenere,Context context) 
    {
        this.arrayGenere = arrayGenere;
        this.context = context;
    }



    @Override
    protected void onPostExecute(Void result)
    {
        super.onPostExecute(result);

        pDialog.dismiss();
    }



    @Override
    protected void onPreExecute() 
    {
        super.onPreExecute();

        pDialog = new ProgressDialog(context);
        pDialog.setMessage("Preparazione round...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(false);
        pDialog.show();
    }



    protected Void doInBackground(Void... params) 
    {
        try
        {       
            int randomLookupId = 0;
            JSONObject obj;                         
            JSONArray jsonArray;    

            for(int i = 0; i < 10; i++)
            {
                canzoni = new ArrayList<String>();

                Log.d("GENERE", arrayGenere.toString());

                obj = getJSONObject(scegliClassifica(arrayGenere));
                jsonArray = obj.getJSONArray("resultIds");

                Log.d("dimensione JsonArray", String.valueOf(jsonArray.length()));
                try
                {
                    randomLookupId = new Random().nextInt(jsonArray.length()-1);
                }
                catch(IllegalArgumentException errore)
                {
                    new AlertDialog.Builder(context)
                    .setTitle("Connessione non attiva!")
                    .setMessage("Connessione di rete debole, uscita dal programma!");
                }
                Log.d("randomLookupID", String.valueOf(randomLookupId));

                JSONObject finalObj = getJSONObject("http://itunes.apple.com/lookup?id="+jsonArray.getString(randomLookupId)); 
                Log.d("URL","http://itunes.apple.com/lookup?id="+jsonArray.getString(randomLookupId));

                while(finalObj.getJSONArray("results").length() == 0)
                {
                    Log.d("Array VUOTO!!","Non è possibile!!!!");
                    randomLookupId = new Random().nextInt(jsonArray.length()-1);
                    Log.d("randomID rigenerato", String.valueOf(randomLookupId));

                    finalObj = getJSONObject("http://itunes.apple.com/lookup?id="+jsonArray.getString(randomLookupId));
                    Log.d("URL Rigenerato","http://itunes.apple.com/lookup?id="+jsonArray.getString(randomLookupId));
                }

                JSONArray finalJsonArray = finalObj.getJSONArray("results");

                JSONObject returnObj = finalJsonArray.getJSONObject(0);
                Log.d("returnObj.length",String.valueOf(returnObj.length()));

                canzoni.add(returnObj.getString("previewUrl"));
                canzoni.add(returnObj.getString("artistName"));
                canzoni.add(returnObj.getString("trackName"));
                canzoni.add(returnObj.getString("artistViewUrl"));
                canzoni.add(returnObj.getString("artworkUrl100"));
//              GTTapp app=(GTTapp) ((Activity)context).getApplication();
//              app.dieciCanzoni;
                Canzone.dieciCanzoni.add(i, new ArrayList<String>(canzoni));
            }
        }   
        catch (JSONException ignored)
        {
            ignored.getCause();
        }
        catch (MalformedURLException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IOException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    private String scegliClassifica(int[] arrayGenere)
    {
        int randomArrayPosition = new Random().nextInt(arrayGenere.length);
        return "http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/charts?cc=us&g="+arrayGenere[randomArrayPosition]+"&name=Songs&limit=200";
    }

    JSONObject getJSONObject(String url) throws IOException, MalformedURLException, JSONException
    {

        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();

        InputStream in = conn.getInputStream();

        try
        {
            StringBuilder sb = new StringBuilder();
            BufferedReader r = new BufferedReader(new InputStreamReader(new DoneHandlerInputStream(in)));
            for (String line = r.readLine(); line != null; line = r.readLine())
            {
                sb.append(line);
            }
            return new JSONObject(sb.toString());
        }
        finally
        {
            in.close();
        }
    }
}

THE PROBLEM: Using the .get() method make the app waiting for the entire computation of the AsyncTask, but it block the UI thread too!! So the user will remain with a black screen for 5 secs or more, and that's not a good thing!

If I don't use the .get() method, I receive an IndexOutOfBounds Exception, because I begin to play the music stream but the list is has not been filled yet.

Can you suggest me a workarund for this situation?

Thank you!

The Condor
  • 1,034
  • 4
  • 16
  • 44
  • 4
    Don't use get ;) just show user some spinner that will let him know he sth happen. after receiving all of needed data populate your list, hide spinner and start playing music :) – fgeorgiew Dec 03 '12 at 19:07
  • You cannot call GET if you don't want to block the thread UI. get() Waits if necessary for the computation to complete, and then retrieves its result. – dougcunha Dec 03 '12 at 19:07
  • 1
    I noticed that in `catch(IllegalArgumentException errore) {}` you will try to create an AlertDialog. Understand that this will crash your app with an exception about trying to access the UI from another thread... You cannot / should not access the UI in `doInBackground()`. – Sam Dec 03 '12 at 19:12
  • @Sam thank you for the suggestion, I've fixed that. My idea was to use something like an AlertDialog that lasts for 5 seconds and then it goes to the next activity, where the songs will be played. – The Condor Dec 03 '12 at 19:22
  • @TheCondor I like that you were handling an error gracefully and notifying the user of it. But you simply need to create the dialog in `onPostExecute()` where you have access to the UI thread. – Sam Dec 03 '12 at 19:28
  • Yep, done it ;) but how can I make what @fgeorgiew suggested in the first comment? – The Condor Dec 03 '12 at 19:38
  • This question: [Android: The progress bar in the window's title does not display](http://stackoverflow.com/q/3092291/1267661) shows how to use the small ProgressBar in your title bar. Or this one: [Download a file with Android, and showing the progress in a ProgressDialog](http://stackoverflow.com/q/3028306/1267661) covers ProgressBars in general. – Sam Dec 03 '12 at 19:51

3 Answers3

3

remove .get() it will block the UI till completion of the task.

Start any task like (playing video) which is dependent on AsycTask in

@Override
protected void onPostExecute(Void result)
{
    super.onPostExecute(result);

    pDialog.dismiss();
      //You can start music stream here
}
Mohsin Naeem
  • 12,542
  • 3
  • 39
  • 53
  • Ok I tried it, but the problem is that the method that play the stream is in another activity class, and it must access all the elements of the UI...how can I do to follow your suggestion? – The Condor Dec 03 '12 at 19:20
0

The get() call is blocking. From the docs:

Waits if necessary for the computation to complete, and then retrieves its result.

What you should do is wait asynchronously the computation to finish before you start to play the music.

If you do not want to expose the asynctask outside your class, you can set a callback into your JSonCanzoni class to be called into the onPostExecute method of the asynctask.

Something like

public interface CanzoniDownloadedInterface {
   public void onCanzoniDownloaded();
}


public JsonCanzoni(int[] arrayGenere, CanzoniDownloadedInterface callback, Context context){
    this.arrayGenere = arrayGenere;
    this.context = context;
    this.callback = callback;
}

and in your onPostExecute():

@Override
protected void onPostExecute(Void result)
{
    super.onPostExecute(result);
    this.callback.onCanzoniDownloaded();
    pDialog.dismiss();
}

If you let your activity implement the interface, you can pass it to your class.

The onCanzoniDownloaded() implementation is where you need to start playing.

fedepaol
  • 6,834
  • 3
  • 27
  • 34
0

Finally I solved my problem, some advices were good and helped me a lot!

I simply moved the startActivity instruction to onPostExecute of JsonCanzoni AsyncTask, and changed some code to adapt to the new version and it's all right! ;)

The Condor
  • 1,034
  • 4
  • 16
  • 44