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
}
}