http://www.cnblogs.com/zealoct/p/3193160.html
寫一個簡單的Android TCP Client的測試程序,可以向Emulator外的TCP Server發送消息,並顯示服務器的返回信息。
因爲這是個很簡單的小應用,本來就沒想要多線程,結果在運行的時候出現如下錯誤:
W/System.err( 755): android.os.NetworkOnMainThreadException
原來在主進程中進行網絡操作會被Android Framework給斃掉,所以新建了一個線程來進行tcp讀寫,再次運行又出現如下錯誤:
W/System.err( 853): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
原來只有創建UI的那個線程可以對UI進行修改,我實現的時候方便起見在新開線程中直接修改了TextView的內容,自然會出錯。
這種很嚴格的程序限制可以增強應用的可靠性,或許會覺得Android給開發者設定諸多限制會影響開發效率,難道寫個這麼短小的東西都要實現進程間通信麼?其實Android Framework本身就提供了相應的解決方案,我們可以利用下面幾個方法來實現這一功能:
void Activity.runOnUiThread(Runnable action)
該方法把action添加到指定的UI進程的事件隊列中去,如果當前進程就是該UI進程,那麼action會立刻被執行。
Boolean View.post(Runnable action)
Booleqn View. postDelayed (Runnable action, long delayMillis)
這兩個方法把action添加到特定UI元素的事件隊列中區,前者直接加入消息隊列等待執行,後者等待指定的時間間隔後執行。
利用以上API,很好的解決了UI修改的問題:
public class MainActivity extends Activity implements OnClickListener {
public static final String TAG = "testServer";
TextView TextViewOutput;
EditText EditTextMsg;
String msg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button BtnSend = (Button) this.findViewById(R.string.BtnSend);
TextViewOutput = (TextView) this.findViewById(R.string.TextViewOutput);
EditTextMsg = (EditText) this.findViewById(R.string.EditTextMsg);
BtnSend.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
msg = EditTextMsg.getText().toString().trim();
if(msg.length() == 0) {
TextViewOutput.append("cannot send blank msg\n");
return;
}
Thread thread = new Thread(new NetThread(), "thread1");
thread.start();
EditTextMsg.setText("");
}
class NetThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Socket socket=new Socket("10.0.2.2", 2000);
PrintWriter pw =new PrintWriter(socket.getOutputStream());
TextViewOutput.post(new ChangeText("Sending:" + msg));
pw.println(msg);
pw.flush();
MainActivity.this.runOnUiThread(new ChangeText("...finished!\n"));
Scanner scan = new Scanner(socket.getInputStream());
String ret = scan.nextLine();
TextViewOutput.post(new ChangeText("Return:" + ret + "\n"));
pw.close();
scan.close();
socket.close();
} catch (Exception EE) {
EE.printStackTrace();
}
}
}
class ChangeText implements Runnable {
String text;
ChangeText(String text) {
this.text = text;
}
@Override
public void run() {
// TODO Auto-generated method stub
TextViewOutput.append(text);
}
}
}
另貼上一小段Server.rb
require 'socket'
server = TCPServer.new(2000)
loop {
client = server.accept
thread = Thread.new {
while msg = client.gets
puts "RECV: #{msg}"
client.puts "ret #{msg}"
end
}
}