簡體   English   中英

Android AsyncTask 的網絡問題

[英]Android Networking Problems with AsyncTask

我正在為網絡練習開發 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 線程。

實際上,這不僅僅是“普遍”禁止的,只是永遠不要這樣做。

...操作系統告訴我我的應用程序沒有響應,這對我來說沒有任何意義,因為我正在處理阻塞的操作!

由於您的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塊中,您試圖設置兩個TextView的文本。 線程的第二條黃金法則(除了不阻塞 UI 線程外)是永遠不要嘗試從 UI 線程以外的任何線程操作 UI 元素——這是行不通的。

所以基本上

  1. 清理你的代碼以至少處理相關的異常 - 如有必要,在doInBackground(...)中放置一個通用的try{...} catch(Exception e) {...}
  2. 不要使用AsyncTaskget()方法
  3. 擺脫任何試圖從doInBackground(...)操縱 UI 元素的東西
  4. 查看 logcat 以查看失敗的原因以及發生在哪一行。

編輯:

更一般地說,如何將信息從我的 AsyncTask 傳遞回 UI 線程?

這完全是在 UI 線程上執行的AsyncTaskonPostExecute(...)方法的要點。

使用doInBackground(...)不僅可以進行身份驗證,還可以下載您需要的文件。 如果有任何問題,將false作為結果傳遞給onPostExecute(...)並讓該方法創建某種警報或錯誤消息,然后進行清理。

如果您的計算機上運行着 C 服務器,那么您需要在 android 上運行客戶端。客戶端將使用 Socket。 您正在使用 ServerSocket ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM