昨天正式開始 Android 編程學習與實踐,由於 Android 模擬器在 WinXP 下一直未安裝成功(見帖子: http://www.cnblogs.com/91program/p/5190888.html),所在將閑置很久的 Android 手機: 聯想 A750 找到用於調試。
A750 是 Android 版本是: 2.3.6,在手機 上打開 USB 調試功能后,就可以通過 USB 線與 PC 連接進行調試了。
調試的主要功能是 Socket 通訊,手機做為服務器端。先用 PC 做為客戶端。后期的客戶端是車機,車機的系統可能是 WinCE 或 Android。
開始之前,先了解了一下 Android 編程的基本知識(后附個人學習記錄的知識點),然后學習了如下鏈接:http://www.cnblogs.com/lknlfy/archive/2012/03/04/2379628.html 關於 Socket 編程的知識。
其中 一個關鍵的知識點是線程與主進程之間的消息傳遞機制,主要是線程中的消息傳遞到主進程。例如:
mHandler.sendMessage(msg);
手機端代碼如下(XML就不提供了,很簡單,大家看圖就知識的):
/*
* 通過 WIFI 網絡進行 Socket 通訊成功, 手機的 IP: 172.25.103.4(隨個人網絡環境變化)
* 測試使用 360 隨身 WIFI, PC 機的 IP: 172.25.103.1
* */
1 package com.jia.leozhengfirstapp; 2 3 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.UnsupportedEncodingException; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 10 11 import android.support.v7.app.ActionBarActivity; 12 import android.annotation.SuppressLint; 13 import android.os.Bundle; 14 import android.os.Handler; 15 import android.os.Message; 16 import android.util.Log; 17 import android.view.Menu; 18 import android.view.MenuItem; 19 import android.widget.TextView; 20 import android.widget.Toast; 21 22 23 24 25 public class MainActivity extends ActionBarActivity { 26 27 28 private Socket clientSocket = null; 29 private ServerSocket mServerSocket = null; 30 31 32 private Handler mHandler = null; 33 34 35 private AcceptThread mAcceptThread = null; 36 private ReceiveThread mReceiveThread = null; 37 private boolean stop = true; 38 39 private TextView ipText; 40 private TextView rcvText; 41 private TextView ConnectStatusText; 42 43 @SuppressLint("HandlerLeak") 44 @Override 45 protected void onCreate(Bundle savedInstanceState) { 46 super.onCreate(savedInstanceState); 47 setContentView(R.layout.activity_main); 48 49 ipText = (TextView)findViewById(R.id.textView1); 50 rcvText = (TextView)findViewById(R.id.textView3); 51 ConnectStatusText = (TextView)findViewById(R.id.textView4); 52 53 ConnectStatusText.setText("連接狀態: 未連接"); 54 55 // 消息處理 56 mHandler = new Handler() 57 { 58 @Override 59 public void handleMessage(Message msg) 60 { 61 switch(msg.what) 62 { 63 case 0: 64 { 65 String strAddress = (msg.obj).toString(); 66 ipText.setText("IP地址: " + strAddress); 67 Log.v("Leo: IP: ", strAddress); 68 ConnectStatusText.setText("連接狀態: 已連接"); 69 // 顯示客戶端IP 70 // ipTextView.setText((msg.obj).toString()); 71 // 使能發送按鈕 72 // sendButton.setEnabled(true); 73 break; 74 } 75 case 1: 76 { 77 String strRcv = (msg.obj).toString(); 78 rcvText.setText("接收到數據: " + strRcv); 79 Log.v("Leo: Rcv: ", strRcv); 80 // 顯示接收到的數據 81 // mTextView.setText((msg.obj).toString()); 82 break; 83 } 84 } 85 86 } 87 }; 88 89 mAcceptThread = new AcceptThread(); 90 // 開啟監聽線程 91 mAcceptThread.start(); 92 } 93 94 95 96 97 @Override 98 public boolean onCreateOptionsMenu(Menu menu) { 99 // Inflate the menu; this adds items to the action bar if it is present. 100 getMenuInflater().inflate(R.menu.main, menu); 101 return true; 102 } 103 104 105 @Override 106 public boolean onOptionsItemSelected(MenuItem item) { 107 // Handle action bar item clicks here. The action bar will 108 // automatically handle clicks on the Home/Up button, so long 109 // as you specify a parent activity in AndroidManifest.xml. 110 int id = item.getItemId(); 111 if (id == R.id.action_settings) { 112 return true; 113 } 114 return super.onOptionsItemSelected(item); 115 } 116 117 // 顯示Toast函數 118 private void displayToast(String s) 119 { 120 Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); 121 } 122 123 private class AcceptThread extends Thread 124 { 125 @Override 126 public void run() 127 { 128 try { 129 // 實例化ServerSocket對象並設置端口號為 12589 130 mServerSocket = new ServerSocket(12589); 131 } catch (IOException e) { 132 // TODO Auto-generated catch block 133 e.printStackTrace(); 134 } 135 136 try { 137 // 等待客戶端的連接(阻塞) 138 clientSocket = mServerSocket.accept(); 139 } catch (IOException e) { 140 // TODO Auto-generated catch block 141 e.printStackTrace(); 142 } 143 144 mReceiveThread = new ReceiveThread(clientSocket); 145 stop = false; 146 // 開啟接收線程 147 mReceiveThread.start(); 148 149 Message msg = new Message(); 150 msg.what = 0; 151 // 獲取客戶端IP 152 msg.obj = clientSocket.getInetAddress().getHostAddress(); 153 // 發送消息 154 mHandler.sendMessage(msg); 155 } 156 } 157 158 159 160 161 private class ReceiveThread extends Thread 162 { 163 private InputStream mInputStream = null; 164 private byte[] buf; 165 private String str = null; 166 167 ReceiveThread(Socket s) 168 { 169 try { 170 // 獲得輸入流 171 this.mInputStream = s.getInputStream(); 172 } catch (IOException e) { 173 // TODO Auto-generated catch block 174 e.printStackTrace(); 175 } 176 } 177 178 @Override 179 public void run() 180 { 181 while(!stop) 182 { 183 this.buf = new byte[512]; 184 185 // 讀取輸入的數據(阻塞讀) 186 try { 187 this.mInputStream.read(buf); 188 } catch (IOException e1) { 189 // TODO Auto-generated catch block 190 e1.printStackTrace(); 191 } 192 193 // 字符編碼轉換 194 try { 195 this.str = new String(this.buf, "GB2312").trim(); 196 } catch (UnsupportedEncodingException e) { 197 // TODO Auto-generated catch block 198 e.printStackTrace(); 199 } 200 201 Message msg = new Message(); 202 msg.what = 1; 203 msg.obj = this.str; 204 // 發送消息 205 mHandler.sendMessage(msg); 206 } 207 } 208 } 209 }
1) 先通過 WIFI 進行 Socket 通訊
運行后,在手機上顯示的未連接界面如下圖:
在 PC 上運行類似於 SocketTool.exe 的工具,SocketTool.exe 的顯示界面如下:
Socket 連接后,手機上顯示的界面如下圖:
2) 通過 USB 實現 PC 與手機的通訊
在 PC 上實現一個 Java 小應用,應用的主要代碼如下:
1 try 2 { 3 Runtime.getRuntime().exec("adb forward tcp:12581 tcp:12589"); 4 } 5 catch (IOException e) 6 { 7 e.printStackTrace(); 8 } 9 10 11 Socket socket = null; 12 try 13 { 14 InetAddress serverAddr = null; 15 OutputStream outStream = null; 16 byte[] msgBuffer = null; 17 18 serverAddr = InetAddress.getByName("127.0.0.1"); 19 System.out.println("TCP 1" + "C: Connecting..."); 20 21 22 socket = new Socket(serverAddr, 12581); // 12581 是 PC 的端口,已重定向到 Device 的 12589 端口 23 24 String message = "PC ADB,send message"; 25 msgBuffer = message.getBytes("GB2312"); 26 27 outStream = socket.getOutputStream(); 28 outStream.write(msgBuffer); 29 Thread.sleep(10000); 30 } 31 catch (UnknownHostException e1) 32 { 33 System.out.println("TCP 2" + "ERROR: " + e1.toString()); 34 } 35 catch (IOException e2) 36 { 37 System.out.println("TCP 3" + "ERROR: " + e2.toString()); 38 } catch (InterruptedException e3) 39 { 40 // Thread.sleep(1000); 增加的異常處理 41 e3.printStackTrace(); 42 } 43 finally 44 { 45 try 46 { 47 if (socket != null) 48 { 49 socket.close(); // 關閉時會導致接收到的數據被清空,所以延時 10 秒顯示 50 } 51 } 52 catch (IOException e) 53 { 54 System.out.println("TCP 4" + "ERROR: " + e.toString()); 55 } 56 }
運行后,在手機上顯示的界面如下圖: