Android - AsyncTask返回的结果未被返回到主Activity

9
我正在尝试使用一个扩展了AsyncTask的类来处理连接到URL、解析JSON、在解析期间显示一个不确定的ProgressDialog,并将结果作为键值对存储在HashMap中返回给主Activity。然后,主Activity将读取HashMap的结果并将其放入表单字段中。然而,即使我在我的AsyncTask中填充了HashMap(通过println语句证明),调用主Activity中返回HashMap的方法仍然得到空结果。我无法确定这是我做错了什么,还是我误解了AsyncTask的功能。
我正在考虑将扩展了AsyncTask的类转换为一个Activity。基本上,在这个数据搜索/解析过程中,用户不应该能做任何其他事情,并且应该等待ProgressDialog消失后才能与应用程序交互(或通过按下返回按钮)。此外,我的应用程序需要能够处理AsyncTask中捕获异常的某些情况(无法连接到URL、错误的JSON、要搜索的产品ID找不到),并为这些异常量身定制错误对话框。如果这个类是一个Activity,我可以在调用finish()时发送不同的结果代码,具体取决于是否捕获到异常。
再次强调,我不确定AsyncTask是否是最佳解决方案,因为当收集和解析信息时,用户将不会做任何其他事情。请告诉我是否新建一个Activity会有意义,或者我只是在搞砸后台线程的实现。
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;
    }

}
2个回答

5
当您发出.execute()时,它作为线程运行并且不等待结果。
因此,无论您在此之后调用什么,都为空,因为数据尚未加载。
您需要在PostExecuted中直接将结果设置为Activity,通过setter MainActivity.this.setProductInfo(result)

1
当我尝试这样做时,我会收到一个错误,它不允许我编译:“在范围内无法访问MainActivity类型的封闭实例”。 - Keeb13r
2
在你的MainActivity中创建AsyncTask作为私有子类。 - Pentium10
只需将代码放置在您的活动方法级别(就像是一个方法,而不是在方法中)即可。 - Pentium10
很好,这完美地运作了。只是好奇 - 如果BookLookupInfo现在是MainActivity的子类,为什么要调用“MainActivity.this.setProductInfo(result)”而不是直接调用“setProductInfo(result)”呢? - Keeb13r
这就是Java在子类和外部类中调用setProductInfo(result)的区别。setProductInfo也可以是子类中的一个方法。 - Pentium10
显示剩余3条评论

3

您存在一些误解。在调用AsyncTask.execute()之后的语句将会立即执行,而您的doInBackground是在其他线程中执行的。在这里,当您使用productInfo映射时,doInBackground尚未完成,因此结果没有在那里填充。

对您来说,简单的解决方案是在onPostExecute方法中使用结果。

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

    }

applyProductInfoToFormFields() 方法在 MainActivity 中,它将该类的数据成员(表示表单字段)设置为新值(例如从我的 JSON 解析的值)。我认为我不能从 ProductLookup 调用该方法。此外,如果我要修改/与 GUI 交互,我应该继续从 MainActivity 进行操作。如果不保存它们的新实例,ProductInfo 怎么知道我的字段的数据成员是什么? - Keeb13r
我没有完全理解你的意思。我认为你需要访问你的GUI控件,从applyProductInfoToFormFields()方法中进行访问。如果是这样的话,最好的做法是将所有的GUI组件声明为全局变量,这样你就可以从任何地方访问它们了。 - Labeeb Panampullan
我该如何从ProductLookup中引用MainActivity的数据成员?我需要对它们做哪些改变才能使它们全局可用? - Keeb13r
如果您在MainActivity.java中声明ProductLookup,则将每个控件声明为每个类的外部。如果您正在使用不同的Java类,则声明为静态。如果您在MainActivity.java中有Static Button按钮,则可以使用MainActivity.button进行引用。 - Labeeb Panampullan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接