2010-12-02 26 views
9

J'essaie d'utiliser une classe AsyncTask-extended pour gérer la connexion à une URL, analyser JSON, afficher un ProgressDialog indéterminé pendant l'analyse et retourner les résultats en tant que valeur-clé paires dans une HashMap à l'activité principale. Les résultats du HashMap seront alors lus par l'activité principale et mis dans des champs de formulaire. Cependant, même si je remplis le HashMap dans mon AsyncTask (attesté par les instructions println), appeler une méthode dans l'activité principale qui retourne le HashMap donne un résultat vide. Je ne peux pas comprendre si c'est quelque chose que je fais mal, ou si je ne comprends pas les capacités d'AsyncTask.Android - résultat d'AsyncTask n'étant pas retourné à l'activité principale

Je discute de la conversion de ma classe qui étend AsyncTask à une activité. Essentiellement, l'utilisateur ne devrait pas pouvoir faire autre chose pendant cette recherche/analyse de données et devrait attendre jusqu'à ce que ProgressDialog s'en aille avant qu'il puisse à nouveau interagir avec l'application (ou en appuyant sur le bouton de retour). De plus, mon application doit pouvoir gérer certains cas dans mon AsyncTask où les exceptions sont interceptées (impossible de se connecter à l'URL, JSON incorrect, l'ID du produit à rechercher est introuvable) et les boîtes de dialogue d'erreur personnalisées sont adaptées à ces exceptions. Je pourrais facilement faire ceci si cette classe était une activité, car je pourrais renvoyer différents codes de résultat en appelant finish(), selon si une exception est attrapée. Encore une fois, je ne suis pas sûr si AsyncTask est la meilleure solution ici, puisque l'utilisateur ne fera rien d'autre pendant que l'information est recueillie et analysée. Veuillez me dire si une nouvelle activité aurait du sens ou si je ne faisais que modifier ma mise en place d'un fil rouge.

MainActivity.java

mInitiateProductLookupButton.setOnClickListener(new View.OnClickListener() { 
      public void onClick(View v) { 
       ProductLookup pl = new ProductLookup(id, MainActivity.this); 
       pl.execute(); 

       // The below variable is always empty! 
       HashMap<String, String> productInfo = pl.getProductInfo(); 
       applyProductInfoToFormFields(productInfo); 
      } 
     }); 

ProductLookup.java

public class ProductLookup extends AsyncTask<Object, Void, HashMap<String, String>> { 
    private String mProductID; 
    private Context mContext; 
    HashMap<String, String> mProductInfo; 
    ProgressDialog mDialog; 

    public ProductLookup(String id, Context applicationContext) { 
     mProductID = id; 
     mContext = applicationContext; 
     mProductInfo = new HashMap<String, String>(); 
    } 

    @Override 
    protected void onPreExecute() { 
     mDialog = new ProgressDialog(mContext); 
     mDialog.setMessage("Loading product info. Please wait..."); 
     mDialog.setIndeterminate(true); 
     mDialog.setCancelable(false); 
     mDialog.show(); 
    } 

    @Override 
    protected void onPostExecute(HashMap<String, String> result){ 
     super.onPostExecute(result); 
     mDialog.dismiss(); 
     mProductInfo = result; 
    } 


    @Override 
    protected HashMap<String, String> doInBackground(Object... params) { 
     try { 
      // Connect to URL, parse JSON, and add key-value pairs to mProductInfo... 

     } catch (MalformedURLException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } catch (JSONException e) { 
      e.printStackTrace(); 
     } 
     finally { 
      try { 
       // Close input/output reader variables 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
     return mProductInfo; 

    } 

    public HashMap<String, String> getProductInfo(){ 
     return this.mProductInfo; 
    } 

} 

Répondre

5

Lorsque vous émettez .execute() qui fonctionne comme fileté et ne pas attendre le résultat.

Donc tout ce que vous appelez après cela, est vide car les données n'ont pas encore été chargées.

Vous devez mettre sur PostExecuted le résultat directement à votre activité via un setter MainActivity.this.setProductInfo(result)

+1

Lorsque je tente de le faire, je reçois une erreur qui ne me laisse pas compiler: « Pas instance englobante du MainActivity de type est accessible portée » – Keeb13r

+2

Créer l'AsyncTask à l'intérieur de votre MainActivity comme enfant classe privée . – Pentium10

+0

Comment créez-vous exactement une classe privée enfant? – Keeb13r

3

Il y a un concept de manquer pour vous. L'instruction après AsyncTask.execute() sera exécutée juste après l'appel. Pendant que votre doInBackground est en train de jouer dans un autre thread. Ici, lorsque vous utilisez la carte productInfo, le champ doInBackground n'a pas été renseigné et le résultat n'est pas renseigné.

Une solution simple pour vous consiste à utiliser le résultat dans la méthode onPostExecute.

protected void onPostExecute(HashMap<String, String> result){ 
     mDialog.dismiss(); 
     mProductInfo = result; 
applyProductInfoToFormFields(productInfo); 

    } 
+0

La méthode applyProductInfoToFormFields() est dans MainActivity, cependant, et définit les membres de données de cette classe (qui représentent les champs de formulaire) pour avoir de nouvelles valeurs (par exemple les valeurs analysées de mon JSON) . Je ne pensais pas que je pourrais faire un appel à cette méthode à partir de ProductLookup. En outre, je pensais que si je devais modifier/interagir avec mon interface graphique, je devrais continuer à le faire depuis ma MainActivity. Comment ProductInfo sait-il quels sont les membres de données pour mes champs sans enregistrer de nouvelles instances? – Keeb13r

+0

Je ne vous comprends pas correctement. Je pense que vous devez accéder à votre formulaire de contrôle de l'interface graphique applyProductInfoToFormFields(). Si oui, la meilleure chose à faire pour vous est de déclarer votre composant global de l'interface graphique pour que vous puissiez y accéder de n'importe où. –

+0

Et comment faire référence aux membres de MainActivity de ProductLookup? De quoi aurais-je besoin pour changer à leur sujet pour les rendre globaux? – Keeb13r