2012-04-04 65 views
0

我正在開發一個用於網絡練習的Android應用程序,我遇到各種各樣的問題。我有一臺運行在服務器上的C服務器,我正在編寫的應用程序旨在連接到服務器,進行身份驗證,然後啓動從服務器到客戶端的文件傳輸。我對Android非常陌生,而且我最近才瞭解到阻止UI線程通常是被禁止的。Android與AsyncTask的網絡問題

我已經創建了一個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; 
    } 

} 

怎麼可能是造成這個問題呢?我正在運行我的服務器,並沒有顯示任何連接已建立的跡象。如果是這種情況,那麼我的代碼不應該阻止...對嗎?

+0

我沒有看到任何阻止代碼,但有一點要注意的是,「你不能在UI線程中阻塞工作」的對口是「你可以從後臺線程訪問UI「。請閱讀[本文檔](http://developer.android.com/resources/articles/painless-threading.html)以獲得對所有這些內容的良好解釋。 – kabuko 2012-04-05 00:02:01

回答

1

雖然我無法對您的問題給出明確的答案,但我仍然會在此解決一些問題。從你的問題兩個引號...

我只是最近才知道阻止UI線程通常是禁止的。

實際上它不是'一般'被禁止的,只是不要這樣做。

...操作系統告訴我,我的應用程序沒有響應,這對我沒有任何意義,因爲我正在線程化阻塞的操作!

並非完全如此,由於這條線在你authenticate方法...

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

您使用的是結果的AsyncTask其中「等待」的get()方法基本上是把你的電話來執行異步任務轉換爲同步任務。

使用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 s的文本。除了不阻止UI線程之外,線程的第二個黃金法則是永遠不要試圖從UI線程以外的任何線程操縱UI元素 - 它不起作用。

所以基本上

  1. 清理你的代碼至少辦理相關的例外 - 如果有必要把通用try{...} catch(Exception e) {...}周圍everyting在doInBackground(...)
  2. 不要使用AsyncTask
  3. get()方法擺脫任何一個試圖從doInBackground(...)
  4. 看logcat的操作UI元素,看看有什麼失敗,並在其上線。

編輯:

更加普遍,我如何能夠通過從我的AsyncTask回到UI線程信息?

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

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

+0

非常感謝您的回覆。一個問題,雖然;如果execute()不提供給我時如何訪問身份驗證的結果? execute()讓我回到另一個AsyncTask,這是沒有用的。我需要知道身份驗證的結果才能繼續執行我的程序。更一般地說,我如何將信息從我的AsyncTask傳遞迴UI線程? – 2012-04-05 03:29:14

+0

@Swgillespie:在我的答案結尾處查看我的編輯。 – Squonk 2012-04-05 06:39:26

+0

非常感謝! – 2012-04-05 15:12:11

0

如果您的計算機上運行的是C服務器,則需要安卓上的客戶端。客戶端將使用Socket。你正在使用一個ServerSocket ...

+0

不,我不是......我創建的Socket對象的名稱叫做serverSocket。我明確地調用了Socket構造器。 – 2012-04-06 00:31:40