Android 如何使用异步任务?

Android 如何使用异步任务?,android,android-asynctask,client-server,Android,Android Asynctask,Client Server,我正在编写一个程序,需要为服务器连接运行单独的线程,但我不知道如何正确使用AsyncTask。我已经阅读了文档,但仍然不确定如何正确使用它 将我连接到服务器的方法: public boolean start() { // try to connect to the server try { socket = new Socket(server, port); } // if it failed not much I can so catch

我正在编写一个程序,需要为服务器连接运行单独的线程,但我不知道如何正确使用
AsyncTask
。我已经阅读了文档,但仍然不确定如何正确使用它

将我连接到服务器的方法:

public boolean start()
{
    // try to connect to the server
    try {
        socket = new Socket(server, port);
    }
    // if it failed not much I can so
    catch(Exception ec) {
        display("Error connectiong to server:" + ec);
        return false;
    }

    /* Creating both Data Stream */
    try
    {
        sInput  = new ObjectInputStream(socket.getInputStream());
        sOutput = new ObjectOutputStream(socket.getOutputStream());
    }
    catch (IOException eIO) {
        display("Exception creating new Input/output Streams: " + eIO);
        return false;
    }

    // creates the Thread to listen from the server
    new ListenFromServer().start();
    // Send our username to the server this is the only message that we
    // will send as a String. All other messages will be ChatMessage objects
    try
    {
        sOutput.writeObject(username);
    }
    catch (IOException eIO) {
        display("Exception doing login : " + eIO);
        disconnect();
        return false;
    }
    // success we inform the caller that it worked
    return true;
}
上面使用的ListenFromServer类:

class ListenFromServer extends Thread {

    public void run() {
        while(true) {
            try {
                String msg = (String) sInput.readObject();
                // if console mode print the message and add back the prompt
                if(clientActivity == null) {
                    System.out.println(msg);
                    System.out.print("> ");
                }
                else {
                    clientActivity.append(msg);
                }
            }
            catch(IOException e) {
                if(clientActivity != null)
                    clientActivity.connectionFailed();
                display("Server has close the connection: " + e);
                break;
            }
            // can't happen with a String object but need the catch anyhow
            catch(ClassNotFoundException e2) {
            }
        }
    }
}
我知道我应该使用
AsyncTask
,但我不知道如何使用。

参见下面的代码

private class ListenFromServer extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(Void... params) {
        while(true) {
        try {
            String msg = (String) sInput.readObject();
            // if console mode print the message and add back the prompt
            if(clientActivity == null) {
                System.out.println(msg);
                System.out.print("> ");
            }
            else {
                clientActivity.append(msg);
            }
        }
        catch(IOException e) {
            if(clientActivity != null)
                clientActivity.connectionFailed();
            display("Server has close the connection: " + e);
            break;
        }
        // can't happen with a String object but need the catch anyhow
        catch(ClassNotFoundException e2) {
        }
    }

    }

    @Override
    protected void onPostExecute(Voidresult) {
       //after completed



     try
    {
        sOutput.writeObject(username);
    }
    catch (IOException eIO) {
        display("Exception doing login : " + eIO);
        disconnect();
        return false;
    }
    // success we inform the 
    }

    @Override
    protected void onPreExecute() {}

    @Override
    protected void onProgressUpdate(Void... values) {}
}
私有类ListenFromServer扩展异步任务{
@凌驾
受保护的Void doInBackground(Void…参数){
while(true){
试一试{
字符串msg=(字符串)sInput.readObject();
//如果是控制台模式,则打印消息并添加回提示
if(clientActivity==null){
System.out.println(msg);
系统输出打印(“>”);
}
否则{
clientActivity.append(msg);
}
}
捕获(IOE异常){
if(clientActivity!=null)
clientActivity.connectionFailed();
显示(“服务器已关闭连接:”+e);
打破
}
//字符串对象不能发生,但无论如何都需要捕获
捕获(ClassNotFoundException e2){
}
}
}
@凌驾
受保护的void onPostExecute(Voidresult){
//完成后
尝试
{
sOutput.writeObject(用户名);
}
捕获(IOO异常eIO){
显示(“登录异常:+eIO”);
断开连接();
返回false;
}
//成功我们通知
}
@凌驾
受保护的void onPreExecute(){}
@凌驾
受保护的void onProgressUpdate(void…值){}
}
称之为Asyntask


新建ListenFromServer().excute()

异步任务比看起来容易得多。有三种主要的方法需要您开始考虑。对AsyncTask调用execute()将启动以下方法:

onPreExecute(); 
这是在调用doInBackground()之前调用的。如果需要,您可以在此处进行设置。可能设置进度条的可见性或其他

doInBackground()
这就是异步操作发生的地方。如果你需要打网络电话或其他什么,把代码放在这里

onPostExecute();
当doInBackground()完成时调用。从doInBackground()返回的任何内容都将传递到此方法。需要注意的是,这个方法是在主线程上运行的,因此您可以从主线程对UI进行更改

现在,对于实际的类本身:

public class MyTask extends AsyncTask<String, Integer, Boolean> {

}
请注意,如果不想传入execute()方法中的任何内容,可以在此处使用“Void”(大写V)。(为了清楚起见,您可以为这些参数传入任何类型的任何对象——Integer、Boolean、CustomClass等。请记住,如果您使用的是基元类型,则需要使用其包装类,例如Integer、Boolean。)

您会注意到doInBackground()方法有一个vararg参数

String... params 
是先前通过execute()传入的字符串,打包到数组中。因此字符串“thises”位于params[0],而“will”位于params[2]

第二个类型参数

Integer
从doInBackground()调用publishProgress()时将传递给onProgressUpdate()的对象类型。这用于在任务仍在执行时从主线程更新UI。我们现在不担心这件事,你可以稍后再弄清楚。如果不打算使用onProgressUpdate(),也可以在此处传递“Void”

现在是第三个也是最后一个类型参数

Boolean
这是doInBackground()将返回的对象类型。这是不言自明的。如果需要,您也可以为该参数传递'Void',只需注意您仍然需要

return null;
来自doInBackground()。那么,一旦doInBackground()完成,这个返回值会发生什么变化呢?它被发送到

@Override
protected void onPostExecute(Boolean...aBoolean) {

}
从这里,您可以对结果执行任何操作,在本例中,结果是布尔值。同样,此方法在主线程上运行,因此您可以从这里更新视图。很多时候,我会将侦听器传递给AsyncTask,并从onPostExecute()调用它。例如:

@Override
protected void onPostExecute(Boolean aBoolean) {
    mListener.onTaskFinished(aBoolean);
}
如果您的AsyncTask类直接在活动中声明,那么您可以直接从onPostExecute()更新视图或执行任何需要执行的操作

最后一件事,您还可以用更传统的方式——通过构造函数——将对象传递给任务。如果不想,不必传递参数来执行()

new MyTask("hello", 55).execute();
只要在AsyncTask类中将这些字段设置为正确的字段,就可以了。您可以像使用任何其他类一样使用它们

Edit:有一些时间,所以我想我应该展示AsyncTask的完整实现,包括用于为结果创建回调的接口

假设我需要对某个API进行网络调用,该API将返回一个JSON数据字符串(或任何类型的数据,重要的是它是一个字符串)。我的AsyncTask将需要一个url来提供给我正在使用的任何网络客户端,因此我们将通过
execute()
方法传递该url。因此,AsyncTask的第一个类型参数将是
字符串。由于我只进行了一次网络呼叫并得到一个响应,因此我不需要更新任何进度,因此第二个类型参数将是
Void
。我从API调用得到的响应是一个字符串,因此我将从
doInBackground()
方法返回该字符串。因此,第三个也是最后一个类型参数是
String

因此,我的AsyncTask类将如下所示:

public class NetworkTask extends AsyncTask<String, Void, String> {

    //I'll use this to pass the result from onPostExecute() to the
    //activity that implements the NetworkTaskListener interface, as
    //well as notify that activity that onPreExecute has been called.
    private NetworkTaskListener mListener;

    //Set the NetworkTaskListener
    public NetworkTask(NetworkTaskListener listener) {
        mListener = listener;
    }

    //onProgressUpdate() doesn't need to be overridden

    //Called after execute(), but before doInBackground()
    @Override
    protected void onPreExecute() {
        mListener.onBeforeExecution();
    }

    //Called automatically after onPreExecute(). Runs in background.
    @Override
    protected void doInBackground(String... params) {
        //"params" holds the Strings that I will pass in when I call 
        //execute() on this task. Since I'm only passing in one thing
        //(a String URL), "params" will be an array with a length of one
        //and the the String URL will be at params[0].

        String url = params[0];
        //Make the network call using whatever client you like.
        //I'll use OkHttp just as an example.

        OKHttpClient client = new OKHttpClient();
        Request request = new Request.Builder()
            .url(url)
            .build();
        Call call = client.newCall(request);

        //It's okay to use a blocking network call here since we're 
        //already off of the main thread.
        try {
            Response response = call.execute();
            //response.body().string() is the JSON data we want
            String jsonData = response.body().string();
            //This will be sent to onPostExecute()
            return jsonData;
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("NetworkTask", "Error during network call");
            //If there was an error, return null
            return null;
        }
    }

    //Called automatically after doInBackground(). The return value
    //from doInBackground() is passed in here automatically.
    @Override
    protected void onPostExecute(String aString) {
        //Check if the String is null to determine if there was an error
        //while making the network call.
        if (aString == null) {
            mListener.onNetworkCallError();
            return;
        }
        /*
        If this is executed, it means that the response was successful.
        Call the listner's onNetworkResponse() method and pass in the 
        string. The String will be used by the Activity that implements
        the NetworkTaskListener.
        */
        mListener.onNetowrkResponse(aString);
    }

}
既然接口已经处理好了,我们只需要让我们的活动实现它。为简单起见,我将让NetworkTask在活动开始后立即开始。您会注意到,我们将
传递到NetworkTask中。这意味着我们将活动作为网络任务传递
@Override
protected void onPostExecute(Boolean aBoolean) {
    mListener.onTaskFinished(aBoolean);
}
new MyTask("hello", 55).execute();
public class NetworkTask extends AsyncTask<String, Void, String> {

    //I'll use this to pass the result from onPostExecute() to the
    //activity that implements the NetworkTaskListener interface, as
    //well as notify that activity that onPreExecute has been called.
    private NetworkTaskListener mListener;

    //Set the NetworkTaskListener
    public NetworkTask(NetworkTaskListener listener) {
        mListener = listener;
    }

    //onProgressUpdate() doesn't need to be overridden

    //Called after execute(), but before doInBackground()
    @Override
    protected void onPreExecute() {
        mListener.onBeforeExecution();
    }

    //Called automatically after onPreExecute(). Runs in background.
    @Override
    protected void doInBackground(String... params) {
        //"params" holds the Strings that I will pass in when I call 
        //execute() on this task. Since I'm only passing in one thing
        //(a String URL), "params" will be an array with a length of one
        //and the the String URL will be at params[0].

        String url = params[0];
        //Make the network call using whatever client you like.
        //I'll use OkHttp just as an example.

        OKHttpClient client = new OKHttpClient();
        Request request = new Request.Builder()
            .url(url)
            .build();
        Call call = client.newCall(request);

        //It's okay to use a blocking network call here since we're 
        //already off of the main thread.
        try {
            Response response = call.execute();
            //response.body().string() is the JSON data we want
            String jsonData = response.body().string();
            //This will be sent to onPostExecute()
            return jsonData;
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("NetworkTask", "Error during network call");
            //If there was an error, return null
            return null;
        }
    }

    //Called automatically after doInBackground(). The return value
    //from doInBackground() is passed in here automatically.
    @Override
    protected void onPostExecute(String aString) {
        //Check if the String is null to determine if there was an error
        //while making the network call.
        if (aString == null) {
            mListener.onNetworkCallError();
            return;
        }
        /*
        If this is executed, it means that the response was successful.
        Call the listner's onNetworkResponse() method and pass in the 
        string. The String will be used by the Activity that implements
        the NetworkTaskListener.
        */
        mListener.onNetowrkResponse(aString);
    }

}
public interface NetworkTaskListener {
    //We call this method in onPreExecute() of the NetworkTask
    void onBeforeExecution();

    //Called from onPostExecute() when there was an error during the
    //network call
    void onNetworkCallError();

    //Called from onPostExecute() when the response was successful
    void onNetworkResponse(String jsonData);
}
public class MainActivity extends Activity implements NetworkTaskListener {
    //ProgressBar that will be displayed when NetworkTask begins
    private ProgressBar mProgressBar;

    //TextView that will be used to display the String that we get back
    //from the NetworkTask
    private TextView mTextView;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate();
        setContentView(R.layout.activity_main);

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mTextView = (TextView) findViewById(R.id.textView);

        String url = "https://www.exampleAPIurl.com";
        //The listener that we are passing in here is this Activity
        NetworkTask task = new NetworkTask(this);
        //We pass the URL through the execute() method
        task.execute(url);

        /*
        From this point, the NetworkTask will begin calling the
        NetworkTaskListener methods, which are implemented below.
        Specifically, it will call onBeginExecution(), and then either 
        onNetworkResponse(String jsonData) or onNetworkCallError();
        */
    }

   ///////Implemented methods from NetworkTaskListener

    @Override
    public void onBeginExecution() {
        //Called when the task runs onPreExecute()
        mProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void onNetworkCallError() {
        //Called from task's onPostExecute() when a Network error
        //occured
        mProgressBar.setVisibility(View.INVISIBLE);
        mTextView.setText("An error occured during the network call");
    }

    @Override
    public void onNetworkResponse(String jsonData) {
        //Called from task's onPostExecute() when network call was
        //successful
        mProgressBar.setVisibility(View.INVISIBLE);
        mTextView.setText(jsonData);
    }
}