2013-05-29 21 views
1

我有一個Android應用程序,是一個簡單的聊天服務器的客戶端。我能夠連接到服務器和我的ObjectStreams。問題是當我收到一條消息時,處理我的服務器連接的線程調用顯示更新列表視圖的消息。Android問題更新ListView動態基於套接字

我收到錯誤「只有創建視圖層次結構的原始線程可以觸及其視圖。」

我敢肯定它,因爲我打電話給我的displayMessage()從我的連接線的方法,但我不知道如何安排我的線程對服務器的連接,並動態更新列表視圖我。

這是我的主要活動。

public class MainActivity extends Activity { 

private Connection serverConnection; 
private ArrayList<String> listItems = new ArrayList<String>(); 
private ArrayAdapter<String> adapter; 
/** 
* Sets the ArrayAdaptor, and starts the connectThread. 
*/ 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    runOnUiThread(new Runnable() { 
     public void run() { 
      ListView listview = (ListView) findViewById(R.id.list); 
      adapter = new ArrayAdapter<String>(MainActivity.this, 
        android.R.layout.simple_list_item_1, 
          listItems); 
      listview.setAdapter(adapter); 
     } 
    }); 
    /** 
    * Starts a new connection Thread 
    */ 
    Thread connectThread = new Thread(new Runnable(){ 
     public void run(){ 
      serverConnection = new Connection(MainActivity.this); 
      serverConnection.run(); 
     } 
    }); 
    connectThread.start(); 
} 
/** 
* Adds a message to the list view. 
* @param string - message to be added. 
*/ 
public void displayMessage(String string) { 
    listItems.add(string); 
    adapter.notifyDataSetChanged(); 
} 
} 

這是我的連接線程類。

public class Connection extends Thread { 

private Socket client; 
private ObjectOutputStream output; 
private ObjectInputStream input; 
private MainActivity mainActivity; 
private String message; 
/** 
* Constructor starts the socket and ObjectStreams 
* 
* @param mainActivity - reference to the MainActivity 
*/ 
public Connection(MainActivity mainActivity) { 
    this.mainActivity = mainActivity; 
    try { 
     client = new Socket("192.168.1.105", 50499); 
     mainActivity.displayMessage("Connected to: " 
       + client.getInetAddress().getHostName()); 
     output = new ObjectOutputStream(client.getOutputStream()); 
     output.flush(); 
     input = new ObjectInputStream(client.getInputStream()); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

} 
/** 
* Run method for the Thread. 
*/ 
public void run() { 
    for (;;) { 
     try { 
      message = (String) input.readObject(); 
      mainActivity.displayMessage(message); 
     } catch (OptionalDataException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } catch (ClassNotFoundException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
} 
} 

回答

1

您正在後臺線程上更新Ui。您應該在ui線程上更新ui。移動後臺線程中更新ui的代碼。您正在後臺線程上刷新您的列表視圖。

mainActivity.displayMessage("Connected to: " 
      + client.getInetAddress().getHostName()); 
mainActivity.displayMessage(message); 

public void displayMessage(String string) { 
    listItems.add(string); 
    adapter.notifyDataSetChanged(); 
    } 

以上應該在線程之外或者您可以在線程內使用runonuithread來更新ui。

 runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       // update ui 
      } 
     }); 

另一種方法是使用asynctask。在doInbackground()中執行所有與網絡相關的操作並更新onPostExecute()中的ui。

Async Task

編輯:不知道你正在嘗試做的。

public class MainActivity extends Activity { 

private Connection serverConnection; 
private ArrayList<String> listItems = new ArrayList<String>(); 
private ArrayAdapter<String> adapter; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
     ListView listview = (ListView) findViewById(R.id.lv); 
     adapter = new ArrayAdapter<String>(MainActivity.this, 
       android.R.layout.simple_list_item_1, 
         listItems); 
     listview.setAdapter(adapter); 
    // use a button and on button click start the thread. 

Thread connectThread = new Thread(new Runnable(){ 
    public void run(){ 
     serverConnection = new Connection(MainActivity.this); 
     serverConnection.run(); 
    } 
}); 
connectThread.start(); 
} 

public void displayMessage(String string) { 
listItems.add(string); 
adapter.notifyDataSetChanged(); 
} 
class Connection extends Thread { 

private Socket client; 
private ObjectOutputStream output; 
private ObjectInputStream input; 
private MainActivity mainActivity; 
private String message; 

public Connection(MainActivity mainActivity) { 
this.mainActivity = mainActivity; 
try { 
    client = new Socket("192.168.1.105", 50499); 
    runOnUiThread(new Runnable() { 
     @Override 
     public void run() { 
      displayMessage("Connected to: " 
        ); 
     } 
    }); 

    output = new ObjectOutputStream(client.getOutputStream()); 
    output.flush(); 
    input = new ObjectInputStream(client.getInputStream()); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
} 

public void run() { 
for (;;) { 
    try { 
      message = (String) input.readObject(); 
      runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
       displayMessage(message); 
       } 
      }); 



    } catch (OptionalDataException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (ClassNotFoundException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 
} 
} 
} 
+0

我對你告訴我要移動的位置和內容有些困惑。我的MainActivity中的displayMessage()在runOnUiThread之外。我會研究異步任務,只是想了解我的線程是如何在這裏工作的。 – Collin

+1

@Collin在你的displayMessage方法中,你正在刷新listview。並且你的顯示方法從線程被調用。所以請使用runOnUiThread在ui線程上更新ui或使用asyntask。檢查鏈接中提供的鏈接 – Raghunandan

+0

如何將消息從連接線程傳遞到runOnUiThread?我無法在Connection類中創建runOnUiThread。 – Collin