【问题标题】:AsyncTask doesn't call OnCancel() on first tryAsyncTask 在第一次尝试时不调用 OnCancel()
【发布时间】:2016-04-19 10:26:09
【问题描述】:

我在 Android Studio 中使用 AsyncTask 在后台使用 TCP 连接。然后我在PreExecute 中有一个跑步者,它执行一个有5 秒超时的跑步者,然后在AsyncTask 上调用cancel(true)(因为DataOutputStream 没有超时覆盖)。

问题是,当我第一次打开我的应用程序进行测试并尝试登录时,它只到达“正在联系服务器”。消息“TCP Timeout”永远不会被调用。如果我再次尝试登录,它会正常工作。但从来没有第一次。所以我猜cancel(true) 永远不会被调用。我尝试在 PreExecuteonLoginButtonClicked 后面的代码中执行超时运行程序(这是一件愚蠢的事情),但没有任何效果。

我做错了什么?

 private class AsynchronisedTask extends AsyncTask<String, Void, String> {

    @Override
    protected String doInBackground(String... strings) {
        String serverAnswer = "";
        if (!isCancelled())
            serverAnswer = new TCP().SendToServer(strings[0]);
        return serverAnswer;
    }

    @Override
    protected void onPostExecute(String result) {
        if (result.contains("Accepted")) {
            Toast.makeText(LoginActivity.this, "Logged in.", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(LoginActivity.this, ProfilePageActivity.class);
            Profile profile = new Profile();
            profile.setProfileName(usernameText.getText().toString());
            intent.putExtra("Username", usernameText.getText().toString());
            startActivity(intent);
        } else {
            Toast.makeText(LoginActivity.this, "Invalid username or password.", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onPreExecute() {
        handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (asynchronisedTask.getStatus() == AsyncTask.Status.RUNNING) {
                    asynchronisedTask.cancel(true);
                }
            }
        }, 5000);
        Toast.makeText(LoginActivity.this, "Contacting server.", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onCancelled(String result) {
        Toast.makeText(LoginActivity.this, "TCP Timeout.", Toast.LENGTH_SHORT).show();
    }
}



public class TCP implements ITCP {
private String IP = "192.168.1.33"; // LOCAL "192.168.1.33" PUBLIC **.***.***.***
private int PORT = 9000;

@Override
public String SendToServer(String stringToServer) {
    String answer = "";
    Socket socket = null;
    DataOutputStream dataOutputStream = null;
    DataInputStream dataInputStream = null;

    try {
        socket = new Socket(IP, PORT);
        dataOutputStream = new DataOutputStream(socket.getOutputStream());
        dataInputStream = new DataInputStream(socket.getInputStream());
        dataOutputStream.writeUTF(stringToServer);
        answer = dataInputStream.readUTF();

    } catch (UnknownHostException e) {
        e.printStackTrace();

    } catch (IOException e) {
        e.printStackTrace();

    } finally {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (dataOutputStream != null) {
            try {
                dataOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (dataInputStream != null) {
            try {
                dataInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    if (answer instanceof String)
        return answer;
    else return answer.toString();
}

【问题讨论】:

    标签: java android multithreading android-asynctask


    【解决方案1】:

    直到 doInBackground 完成后才会调用 OnCancelled。由于 doInBackground 在尝试进行 TCP 数据传输时被卡住了,所以这种情况永远不会发生。

    我实际上从未见过 onCanceled 被使用过。但它唯一应该做的就是清理 UI。结束任务的方法是让 doInBackground 定期检查 isCanceled() 并在其为真时自行终止。请记住,在任何语言中,除了让线程本身确定其安全性之外,没有其他方法可以安全地终止通用线程。所以 cancel 不只是取消任务,它只是设置一个标志供其他线程检查。

    如果你调用 task.cancel(true),socket 会被中断导致一个 InterruptedException,你可以捕捉到这个并允许任务自行取消。

    【讨论】:

    • 当有另一个线程执行 5 秒超时然后在线程上调用 cancel(true) 时,为什么只在 doInBackground 之后调用 OnCancelled?我想我已经在 doInBackground 中检查了 isCanceled() 吗?
    • 它直到 doInBackground 之后才被调用,因为这就是文档所说的:) 在取消任务的情况下,基本上调用 onCanceled 而不是 onPostExecute,并且用于相同的目的(UI 清理)。至于检查 isCanceled- 值可以改变。请记住 doInBackground 是在并行线程上完成的。所以 isCanceled 在开始时可能为假,但在一毫秒后变为真。所以一开始是假的,不代表以后不会取消。
    猜你喜欢
    • 1970-01-01
    • 2016-10-17
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多