簡單的Android TCP Client(Android非UI線程修改界面)

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
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章