简体   繁体   中英

AsyncTask doesn't call OnCancel() on first try

I'm using AsyncTask in Android Studio to use a TCP connection in the background. Then i have a runner in PreExecute which executes a runner with a 5 second timeout, which then calls cancel(true) on AsyncTask (since DataOutputStream doesn't have a timeout override).

The problem is, when i first open my application to test it, and try to login, it only reaches "Contacting server". The message "TCP Timeout" never gets called. If i try to login once again, it works as it should. But never on the first time. So i'm guessing the cancel(true) never gets called. I've tried to execute the timeout runner in PreExecute , and in onLoginButtonClicked code behind (which is a stupid thing to do), but none works.

What am i doing wrong?

 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();
}

OnCancelled isn't called until after doInBackground finishes. Since doInBackground is stuck trying to do TCP data transfer, that never happens.

I've actually never seen onCanceled used. But the only thing it should do is cleanup the UI. The way to end a task is to have doInBackground periodically check isCanceled() and terminate itself if its true. Remember that there is no way, in any language, to terminate a generic thread safely other than to have the thread itself determine its safe to do so. So cancel doesn't just cancel the task, it just sets a flag for the other thread to check.

If you call task.cancel(true), the socket will be interrupted causing an InterruptedException, you can catch this and allow the task to cancel itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM