如何从AsyncTask中获取字符串?

54
我有以下类:
public class getURLData extends AsyncTask<String, Integer, String>{

@Override
protected String doInBackground(String... params) {
    String line;
    try {  
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(params[0]);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        line = EntityUtils.toString(httpEntity);

    } catch (UnsupportedEncodingException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    } catch (MalformedURLException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    } catch (IOException e) {
        line = "<results status=\"error\"><msg>Can't connect to server</msg></results>";
    }
    return line;
}

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

}

而我正在尝试这样调用它:
String output = null;
output = new getURLData().execute("http://www.domain.com/call.php?locationSearched=" + locationSearched);

但输出变量没有获取到数据,相反我收到了一个错误:
Type mismatch: cannot convert from AsyncTask<String,Integer,String> to String

你在哪里看到了 super.onPostExecute(result); 这行代码?我没有加这一行,但程序仍然可以正常运行! - Francisco Corrales Morales
8个回答

113

方法execute返回AsyncTask本身,您需要调用get

output =
    new getURLData()
        .execute("http://www.example.com/call.php?locationSearched=" + locationSearched)
        .get();

这将通过execute启动一个新的线程,同时阻塞当前线程(通过get),直到新线程的工作完成并返回结果。

如果这样做,您刚刚把您的异步任务变成了一个同步任务。

然而,使用get的问题在于它会阻塞,因此需要在工作线程上调用。但是,AsyncTask.execute()需要在主线程上调用。因此,尽管这段代码可以工作,但可能会得到一些不希望的结果。我还怀疑get()没有经过Google充分测试,并且可能存在某些bug。

参考:AsyncTask.get


5
我的天,随着进展越来越深入,这变得更加复杂了……我想做的就是当用户点击按钮时立即加载屏幕,然后在http完成后显示内容。这该怎么做? - Paul
3
@Paul:无论你在执行后需要做什么,请在onPostExecute中完成。就是这么简单。 - K-ballo
2
我希望是这样的,但如果我移动到onPostExecute方法,我就不再有params变量了,而且返回字符串时会出现更多问题。感谢您的帮助,但这已经花费了我数小时的时间,我真的认为这是一个无法解决的问题。 - Paul
4
您可以在 getURLData 中添加一个构造函数,将参数作为类成员变量存储,然后在 onPostExecute 方法中就可以使用它们了。 - K-ballo
1
谢谢你的帮助和耐心。我成功让它工作了!太棒了 :) - Paul
显示剩余3条评论

18

我宁愿创建回调函数来避免阻塞用户界面线程。 在你的类中创建一个当数据到达时被调用的方法。例如:

private void setData(String data){
    mTextView.setText(data);
}

然后在AsyncTask中实现onPostExecute:

@Override
protected void onPostExecute(String result) {
    setData(result);
}

然后在代码的某个地方执行任务:

new getURLData().execute(...

任务完成时将调用setData并填充mTextView。

AsyncTask.get()会阻塞您的UI,因此没有理由使用AsyncTask。


10
好的,但是我该如何在AsyncTask中调用setData方法呢?因为该方法在另一个Activity中(执行.execute的那个)? - Francisco Corrales Morales
2
你不需要这样做。只需在 onPostExecute() 中完成你所需的操作即可。 - Cyrille
@FranciscoCorralesMorales,最好定义接口以便像这个回答中那样使用它: https://dev59.com/emcs5IYBdhLWcg3wym_D#12575319 - Kaveh Safavi

4
如果用户点击按钮后需要等待内容加载,那他们同时可以做什么呢?
以下是一种解决方案:
1. 用户点击按钮。 2. 您检查网络连接。如果用户未连接到互联网,则告知用户。 3. 发送一个Intent来启动IntentService以发送HTTP请求。 4. 请求完成后,您发布一个通知。 5. 用户单击通知,返回到一个Activity,该Activity可以执行下一步操作。
这样用户就可以在请求被处理时去做其他事情。
IntentService在自己的线程上后台运行。当它收到一个Intent时,它会运行onHandleIntent()方法。当该方法完成时,服务被缓存:它不是活动状态,但可以在下一个Intent到达时快速重新启动。

2
  1. 在构造函数中获取上下文,代码如下:

    public GetDataTask(Context context) {}

  2. 创建一个接口,并定义方法:

    void onDataRetrieved(String data);

  3. 在创建任务对象的类(例如MainActivity)中实现该接口。

  4. 将上下文强制转换为接口类型,并调用onDataRetrieved方法。


这可能会导致内存泄漏。如果长时间运行的操作具有访问活动上下文的权限,则异步任务将阻止在配置更改或用户因某种原因按下后退键后从垃圾回收中删除该活动。 - Rahul Sainani
1
在将 Activity 传递给 AsyncTask 时,为避免内存泄漏并在 doInBackground 方法中获取它,需要使用 WeakReference<Activity> actWeak = new WeakReference<>(activity)。然后可以通过 Activity act = actWeak.get() 获取该 Activity - isabsent

0
在下面的代码中,我从AsyncTask中获取一个字符串(directorName)。
public class GetDirector {
    String id;
    private String baseURL = "http://www.omdbapi.com/?i=";
    private String finalURL = "";
    String theDirector;

    public GetDirector(String imdbID) throws ExecutionException, InterruptedException {
        id= imdbID;
        finalURL = baseURL + id + "&plot=full&r=json";
        System.out.println("GetDirector. finalURL= " + finalURL);
        theDirector = new GetDirectorInfo().execute().get();
    }

    public String getDirector (){
        return theDirector;
    }

    private class GetDirectorInfo extends AsyncTask<Void, Void,String> {
        @Override
        protected String doInBackground(Void... params) {
            String directorName = null;

            ServiceHandler sh = new ServiceHandler();

            // Making a request to url and getting response
            String jsonStr = sh.makeServiceCall(finalURL, ServiceHandler.GET);
            System.out.println("Act_DetailsPage. jsonStr= " + jsonStr);

            if (jsonStr != null) {
                try {
                    JSONObject everything = new JSONObject(jsonStr);

                    directorName = everything.getString(JSON_Tags.TAG_DIRECTOR);
                    System.out.println("directorName= "+ directorName);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("Inside GetDirector. Couldn't get any data from the url");
            }
            return directorName;
        }
    }
}

4
使用 get 方法会使代码同步执行,这与 AsyncTask 的异步执行的目的相悖。请避免使用该方法。 - mradzinski

0
在任务的构造函数中添加一个上下文参数,该参数将引用存储结果数据的对象。
class PopulateArray extends AsyncTask<Integer, Integer, ArrayList<String>>
{
    Context _context; // context may be what ever activity or object you want to store result in
    PopulateArray(Context context)
    {
        _context = context;
    }
    @Override
    protected ArrayList<String> doInBackground(Integer... integers)
    {
        ArrayList<String> data = new ArrayList<String>();
        //manipulate
        return data;
    }

    @Override
    protected void onPostExecute(ArrayList<String> strings)
    {
        _context.setData(strings);
    }
}

0

在不费力的情况下,从AsyncTask发送数据到用户界面的唯一方法是使用OttoEvent总线。在UI中注册一个处理结果的方法,在onPostExecute方法中使用post一个带有结果的message到它下面的@Subscribe注解中。


0
这个一行代码对我有用:
String result = MyasyncTask.execute(type, usrPhoneA, usrPWDA).get();

当它返回一个AsycTask变量时,这该怎么工作? - Kingsley Mitchell
在 PHP 处理登录函数后,它会将操作结果返回到变量 "result" 中。我知道这样做可能不是异步任务,但在我的情况下没问题。 - francisqueins
我得到一个asynctask变量,但并不是很有用。 - Kingsley Mitchell

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