【问题标题】:Android Networking Problems with AsyncTaskAsyncTask 的 Android 网络问题
【发布时间】:2012-04-04 23:50:57
【问题描述】:

我正在开发一个用于网络练习的 Android 应用程序,但遇到了各种各样的问题。我有一个在我的计算机上运行的工作 C 服务器,我正在编写的应用程序旨在连接到服务器、进行身份验证,然后启动从服务器到客户端的文件传输。我对 Android 很陌生,最近才知道阻塞 UI 线程通常是被禁止的。

我创建了一个 AsyncTask 来帮助我解决这个问题。但是,当我调用它时,它完全失败了。但是,它并没有明显失败。我看到的唯一问题是我的主屏幕缩小并奇怪地四处移动。有时 UI 线程完全阻塞,操作系统告诉我我的应用程序没有响应,这对我来说没有任何意义,因为我正在处理阻塞的操作!

这是我用于 AsyncTask 的代码:

private class LoginTask extends AsyncTask<String, Void, Boolean> {

    @Override
    protected Boolean doInBackground(String... input) {
        int count = input.length;
        if (count != 4)
            return false;
        String ipAddress = input[0];
        int portNumberInt = Integer.parseInt(input[1]);
        String username = input[2];
        String password = input[3];
        // Step 0: Establish a connection to the server
        PrintWriter out;
        BufferedReader in;
        try {
            serverSocket = new Socket(ipAddress, portNumberInt);
            out = new PrintWriter(serverSocket.getOutputStream(), true);
            in = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        // Step 1: send "Authorize" to the server
        out.print("Authorize");
        // Step 2: server sends a random 64 character challenge string
        char[] buffer = new char[64];
        String challenge = null;
        try {
            in.read(buffer);
            challenge = new String(buffer);
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        challenge = username + password + challenge;
        // Step 3: Compute MD5 hash of username + password + challenge and send it to the server
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            return false;
        }
        byte[] digest = md5.digest(challenge.getBytes());
        out.print(digest);
        // Step 4: Server computes the same hash, determines whether or not to accept the connection
        String authResult;
        try {
            in.read(buffer);
            authResult = new String(buffer);
        } catch (IOException e) {
            ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
            ((TextView)findViewById(R.id.textView2)).setText("");
            return false;
        }
        if (authResult.equals("Pass")) {
            return true;
        } else {
            return false;
        }
    }

}

这是调用 AsyncTask 的代码:

public boolean authenticate(final String ipAddress, final int portNumberInt, final String username, final String password) {
    try {
        Toast.makeText(getApplicationContext(), "Starting async task...", Toast.LENGTH_LONG).show();
        boolean result = new LoginTask().execute(ipAddress, Integer.toString(portNumberInt), username, password).get();
        Toast.makeText(getApplicationContext(), "Async task done!", Toast.LENGTH_LONG).show();
        return result;
    } catch (InterruptedException e) {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        return false;
    } catch (ExecutionException e) {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        return false;
    }

}

什么可能导致这个问题?我正在运行我的服务器,但它没有显示任何已建立连接的迹象。如果是这种情况,那么我的代码应该永远不会阻塞......对吗?

【问题讨论】:

  • 我也没有看到任何阻塞代码,但需要注意的一点是“你不能在 UI 线程中做阻塞工作”的对应物是“你不能访问来自后台线程的 UI”。阅读 this doc 以获得对所有这些的很好的解释。

标签: android multithreading networking


【解决方案1】:

虽然我无法对您的问题给出明确的答案,但我会在这里解决许多问题。您的问题中的两句话...

我最近才知道阻塞 UI 线程通常是被禁止的。

实际上,这不仅仅是“一般”禁止的,只是永远不要这样做。

...操作系统告诉我我的应用没有响应,这对我来说没有任何意义,因为我正在处理阻塞的操作!

由于您的 authenticate 方法中的这一行,这并不完全正确...

boolean result = new LoginTask().execute(<cut-for-clarity>).get();

您正在使用 AsyncTaskget() 方法,该方法“等待”结果,基本上将您的调用以执行异步任务变为同步任务。

使用get()AsyncTask 执行网络操作可能与简单地在UI 线程上执行代码一样糟糕。我一直不明白为什么AsyncTaskget(...) 方法。

我看到的下一个问题在以下代码中......

try {
    serverSocket = new Socket(ipAddress, portNumberInt);
    out = new PrintWriter(serverSocket.getOutputStream(), true);
    in = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
} catch (IOException e) {
    ((TextView)findViewById(R.id.textView1)).setText(e.getMessage());
    ((TextView)findViewById(R.id.textView2)).setText("");
    return false;
}

首先,您的catch 块仅在寻找IOException。创建你的套接字也可能会抛出UnknownHostException。创建你的PrintWriter 也可以抛出NullPointerException 等等等等

其次,在您的catch 块中,您尝试设置两个TextViews 的文本。线程的第二条黄金法则(除了不阻塞 UI 线程)是永远不要尝试从 UI 线程以外的任何线程操作 UI 元素 - 它不会起作用。

基本上是这样

  1. 清理您的代码以至少处理相关异常 - 如有必要,在 doInBackground(...) 中的所有内容周围放置一个通用的 try{...} catch(Exception e) {...}
  2. 不要使用AsyncTaskget()方法
  3. 摆脱任何试图从doInBackground(...) 操纵 UI 元素的东西
  4. 查看 logcat 以了解失败的原因以及在哪一行。

编辑:

更一般地说,我如何将 AsyncTask 中的信息传递回 UI 线程?

这完全是在 UI 线程上执行的 AsyncTaskonPostExecute(...) 方法的重点。

使用doInBackground(...) 不仅可以进行身份​​验证,还可以下载您需要的文件。如果有任何问题,请将false 作为结果传递给onPostExecute(...),并让该方法创建某种警报或错误消息,然后进行清理。

【讨论】:

  • 非常感谢您的回复。不过,一个问题;当 execute() 不给我时,我如何访问身份验证的结果? execute() 给了我另一个 AsyncTask,这没有帮助。我需要知道身份验证的结果才能继续我的程序。更一般地说,如何将信息从 AsyncTask 传递回 UI 线程?
  • @Swgillespie:在我的答案末尾查看我的编辑。
【解决方案2】:

如果您的计算机上运行的是 C 服务器,那么您需要在 android 上运行客户端。客户端将使用 Socket。您正在使用 ServerSocket...

【讨论】:

  • 不,我不是……我正在创建的 Socket 对象的名称称为 serverSocket。我明确地调用了 Socket 构造函数。
猜你喜欢
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多