如何使用AsyncTask

11

AsyncTask问题

我已经跟随一些教程,但仍然不清楚。这是我目前拥有的代码,并在代码下面提出了一些问题。MainActivity调用SomeClassWithHTTPNeeds,然后调用JSONParser(AsyncTask<>)


MainActivity:

String station = SomeClassWithHTTPNeeds.getInstance().getStation(123);

SomeClassWithHTTPNeeds:

getStation {

JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");
}

JSON解析器(AsyncTask<String, Void, String>)

protected String doInBackground(); ==> Seperate thread
protected void onPostExecute(); ==> On GUI thread

我在思考:

--- 将HTTPRequest放在doInBackground()中;

问题是我不知道如何:

让JSONParser将JSONObject返回到getStation方法中?

我需要知道的

=> 我应该在background还是execute中返回JSONObject?

=> JSONParser一旦成为AsyncTask后如何使用?execute()函数会返回值吗?

=> AsyncTask<String,Void,String> ==> 这是如何工作的?这是返回类型吗?

非常感谢!


哦,还有,我从未见过这种表示法:“doInBackground(String... arg) {”,有人能解释一下吗? - Kevin Van Ryckegem
有很多关于它的例子。我的答案在这里:http://stackoverflow.com/questions/16577700/how-do-i-add-an-asynctask-to-this/16577742#16577742 - JustWork
4个回答

24

关于AsyncTask的常见问题和使用方法的概述

  

=> 我应该在哪里进行网络操作?我应该在哪里返回已获取的值?

通常情况下,你应该在一个独立线程 -> doInBackground();中执行网络操作,因为当网络操作耗时时,你不希望UI被冻结。所以你应该在doInBackground()方法中连接到你的服务或.php脚本或者从任何地方获取数据。然后你也可以在这里解析数据并通过指定doInBackground()方法的返回类型来返回解析后的数据,更多详情请看下文。onPostExecute() 方法将会接收到doInBackground()方法返回的值,并使用UI来展示它们。

  

=> AsyncTask< String, Integer, Long> ==> 它是如何工作的?

一般来说,AsyncTask类看起来像这样,它只是一个具有3个不同泛型类型的通用类:

AsyncTask<Params, Progress, Result>

您可以指定AsyncTask所需的参数类型、进度指示器类型以及结果(doInBackGround()方法的返回类型)的类型。

以下是AsyncTask的一个示例:

AsyncTask<String, Integer, Long>

我们为参数使用了字符串类型(String),进度使用整型(Integer),以及结果(return type of doInBackground())使用长整型(Long)。 你可以使用任何你想要的类型来代替Params、Progress和Result。

private class DownloadFilesTask extends AsyncTask<String, Integer, Long> {

 // these Strings / or String are / is the parameters of the task, that can be handed over via the excecute(params) method of AsyncTask
 protected Long doInBackground(String... params) {

    String param1 = params[0];
    String param2 = params[1];
    // and so on...
    // do something with the parameters...
    // be careful, this can easily result in a ArrayIndexOutOfBounds exception
    // if you try to access more parameters than you handed over

    long someLong;
    int someInt;

    // do something here with params
    // the params could for example contain an url and you could download stuff using this url here

    // the Integer variable is used for progress
    publishProgress(someInt);

    // once the data is downloaded (for example JSON data)
    // parse the data and return it to the onPostExecute() method
    // in this example the return data is simply a long value
    // this could also be a list of your custom-objects, ...
    return someLong;
 }

 // this is called whenever you call puhlishProgress(Integer), for example when updating a progressbar when downloading stuff
 protected void onProgressUpdate(Integer... progress) {
     setProgressPercent(progress[0]);
 }

 // the onPostexecute method receives the return type of doInBackGround()
 protected void onPostExecute(Long result) {
     // do something with the result, for example display the received Data in a ListView
     // in this case, "result" would contain the "someLong" variable returned by doInBackground();
 }
}

如何使用AsyncTask?我该如何“调用”它?如何“执行”它?

在这种情况下,AsyncTask将以字符串或字符串数组的形式作为参数(一旦调用AsyncTask,指定的参数将在execute(param)方法中使用)。

new DownloadFilesTask().execute("Somestring"); // some String as param

请注意,此调用没有返回值,你应该使用从doInBackground() 返回的唯一返回值。 使用onPostExecute()方法来利用返回的值。

还要小心这行代码:(此执行将实际返回一个值)

long myLong = new DownloadFilesTask().execute("somestring").get();

.get()调用会导致UI线程被阻塞(如果操作时间超过几毫秒,则UI将冻结),因为执行没有发生在单独的线程中。如果您删除对.get()的调用,它将以异步方式执行。

=> 这个符号 "execute(String... params)" 是什么意思?

这是一个具有所谓"可变参数"(variable arguments)参数的方法。简单来说,这意味着通过这个参数可以传递给方法的实际值的数量未指定,并且您传递给该方法的任何数量的值都将在方法内部被视为一个数组。因此,这个调用可能看起来像这样:

execute("param1");

但它也可能看起来像这样:

execute("param1", "param2");

或者甚至更多的参数。假设我们还在讨论AsyncTask,这些参数可以在doInBackground(String... params)方法中通过以下方式访问:

或甚至有更多的参数。如果我们仍然在谈论AsyncTask,则可以在doInBackground(String... params)方法中以此方式访问这些参数:

 protected Long doInBackground(String... params) {

     String str1 = params[0];
     String str2 = params[1]; // be careful here, you can easily get an ArrayOutOfBoundsException

     // do other stuff
 }

你可以在这里阅读有关AsyncTask的更多信息:http://developer.android.com/reference/android/os/AsyncTask.html

同时,也可以查看这个AsyncTask示例:https://dev59.com/0Gkw5IYBdhLWcg3wwNN5#9671602


1
谢谢你的回答!正是我想要的,详细而且精彩!我按照你说的做了,为了在执行任务的类中捕获返回值,我使用了.execute().get()方法。看起来效果很好。 - Kevin Van Ryckegem
好的,不客气。然而,请非常小心使用.get() -> 请查看我的编辑答案。 - Philipp Jahoda
是的,我已经注意到了,谢谢你告诉我。在我的情况下这不是问题。但是我不确定如何在执行AsyncTask的类中获取返回值(除了使用.get()之外的其他方式)?我需要执行的操作无法在onPostExecute()中完成,因为我无法访问正确的变量。而且我需要传递多种类型的变量到AsyncTask中才能将它们传递到那里。否则,我可以将它们定义为“Object”,然后将它们转换为正确的类型?但这似乎不太合适 :)。 - Kevin Van Ryckegem
我猜你说的是在Activity内部的变量?只需将AsyncTask作为Activity的内部类即可。这样,您就可以访问所有变量 :) - Philipp Jahoda
1
我已经写了相当多的AsyncTasks(没有使用.get()),一切都运行得非常快速和顺畅!这绝对帮助了我。 - Kevin Van Ryckegem
get()在没有线程的情况下做与禁用NetworkOnMainThreadException一样糟糕,因为它本质上是相同的结果。我认为你应该将“小心”更改为永远不要使用 - zapl

0
package com.example.jsontest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
import android.util.Log;

public class HttpClient {
    private static final String TAG = "HttpClient";

    public static JSONObject SendHttpPost(String URL, JSONObject jsonObjSend) {

        try {
            DefaultHttpClient httpclient = new DefaultHttpClient();
            HttpPost httpPostRequest = new HttpPost(URL);

            StringEntity se;
            se = new StringEntity(jsonObjSend.toString());

            httpPostRequest.setEntity(se);
            httpPostRequest.setHeader("Accept", "application/json");
            httpPostRequest.setHeader("Content-type", "application/json");
            httpPostRequest.setHeader("Accept-Encoding", "gzip"); 

            long t = System.currentTimeMillis();
            HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
            Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");

            HttpEntity entity = response.getEntity();

            if (entity != null) {
                InputStream instream = entity.getContent();
                Header contentEncoding = response.getFirstHeader("Content-Encoding");
                if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
                    instream = new GZIPInputStream(instream);
                }

                String resultString= convertStreamToString(instream);
                instream.close();
                resultString = resultString.substring(0,resultString.length()-1); 

                JSONObject jsonObjRecv = new JSONObject(resultString);
                Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");

                return jsonObjRecv;
            } 

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }


    private static String convertStreamToString(InputStream is) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();

        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
                Log.e("JSON", ""+line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

}

异步任务:

public class callCarWeb extends AsyncTask {

    @Override
    protected void onPreExecute() {
        mDialog = new ProgressDialog(MainActivity.this);
        mDialog.setMessage("Please wait...");
        mDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        mDialog.setIndeterminate(true);
        mDialog.setCancelable(false);
        mDialog.show();

    }

    @Override
    protected Void doInBackground(Void... params) {

        try {
            JSONObject jsonObjSend = new JSONObject();
            jsonObjSend.put("username", username);
            jsonObjSend.put("password", passwd);
            Log.e("SEND", jsonObjSend.toString());
            JSONObject json = HttpClient.SendHttpPost("http://10.0.2.2/json/login.php", jsonObjSend);
            String status = json.getString("status");
            if(status.equalsIgnoreCase("pass")){
                String id = json.getString("user_id");
                Log.e("id", id);
                String name = json.getString("name");
                Log.e("name", name);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }



        return null;

}

    @Override
    protected void onPostExecute(Void result) {
        mDialog.cancel();
    }

} ## 标题 ##


0

阅读一些 泛型.

现在,当你编写异步任务JSONParser时,params的类型为Stringprogress的类型为Voidresult的类型为String。请阅读this

通常人们重写两个方法doInBackground()onPostExecute(),第一个方法接收params并返回一个result,第二个方法接收该result。这些是受保护的方法,您无法直接调用它们。然后你可能会问如何将param发送到doInBackground(),请看execute() API。

doInBackground()在后台线程上运行,不会阻塞调用!

不要这样做,

JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");

不要在 JSONParser 或其他地方写,应该使用 interface

public interface OnParseCompleteListener {
     void onParseComplete(JSONObject obj);
}

现在你的JSONParser类会像这样:

public class JSONParser extends AsyncTask<String, Void, String> {
     private OnParseCompleteListener mOnParseCompleteListener;

     public void setOnParseCompleteListener(OnParseCompleteListener listener) {
         mOnParseCompleteListener = listener;
     }

     protected String doInBackground(String... params) {
         /*
          * do http request and return a result
          */
     }

     protected void onPostExecute(String... result) {
         /*
          * parse the resulting json string or you can parse same string in 
          * doInBackground and can send JSONObject as a result directly.
          * at this stage say you have a JSONObject obj, follow 
          */
          if (mOnParseCompleteListener != null) {
              mOnParseCompleteListener.onParseComplete(obj);
          }
     }
}

当您创建一个 JSONParser 对象时,请设置 OnParseCompleteListener

JSONParser parser = new JSONParser();
parser.setOnParseCompleteListener(listener);
parse.execute("may be an url");

现在您可以决定从哪里传递或创建自己的监听器。


0

我认为你可以在Async任务的doInBackground中执行你的HTTPRequest,并在onPostExecute中执行JSONParser


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