[安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示
?一、前言:
一般情況下從TCP服務器讀取數據是放在一個線程里讀的,但是刷新界面又不得不放在線程外面,所以需要用消息傳遞把線程里從TCP里獲得的數據傳送出來,然后根據數據對頁面進行相應的刷新。
?
二、業務邏輯:
?
這里包含2個layout,第一個用于登陸的(即輸入服務器對應的IP和端口號),點擊確定進行跳轉到相應的監控界面,監控界面包括加熱、關閉、和顯示溫度3個按鈕,以及一個用于繪制溫度的SurfaceView。
?
三、詳細介紹:
3-1、2個activity介紹:
登陸頁面對應的activity,從上面的代碼可以看出:29~31行使用intent進行頁面跳轉,然后所有邏輯均在ControlActivity里實現了。
1 public class MainActivity extends ActionBarActivity 2 { 3 private final String TAG = "MainActivity"; 4 private EditText et01; 5 private EditText et02; 6 private Button btOK; 7 private Button btCancel; 8 public static String userIP = "192.168.1.130"; //IP和端口號 9 public static int userPort = 8000; 10 public static int wen_du; //當前溫度 11 public static int shui_wei; //當前水位 12 public static int state; //當前狀態0關閉;1燒水;2保溫 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 et01 = (EditText)findViewById(R.id.et_01); 18 et02 = (EditText)findViewById(R.id.et_02); 19 btOK = (Button)findViewById(R.id.bt_OK); 20 btCancel = (Button)findViewById(R.id.bt_Cancel); 21 22 23 btOK.setOnClickListener(new OnClickListener(){ 24 public void onClick(View v) 25 { 26 //userIP = et01.getText().toString(); 27 //userPort = Integer.parseInt(et02.getText().toString()); 28 //跳到控制界面 29 Intent intent = new Intent(MainActivity.this,ControlActivity.class); 30 Log.i(TAG, "跳轉前"); 31 startActivity(intent); 32 } 33 }); 34 btCancel.setOnClickListener(new OnClickListener(){ 35 public void onClick(View v) 36 { 37 et01.setText(""); 38 et02.setText(""); 39 } 40 }); 41 42 } 43 44 45 @Override 46 public boolean onCreateOptionsMenu(Menu menu) { 47 // Inflate the menu; this adds items to the action bar if it is present. 48 getMenuInflater().inflate(R.menu.main, menu); 49 return true; 50 } 51 52 @Override 53 public boolean onOptionsItemSelected(MenuItem item) { 54 // Handle action bar item clicks here. The action bar will 55 // automatically handle clicks on the Home/Up button, so long 56 // as you specify a parent activity in AndroidManifest.xml. 57 int id = item.getItemId(); 58 if (id == R.id.action_settings) { 59 return true; 60 } 61 return super.onOptionsItemSelected(item); 62 } 63 }另一個activity的框架如下圖:主要的有①、②、③三個函數,另外三個是Callback附帶要實現的。其主要邏輯為:在onCreate中實例化按鈕和surfaceView,然后對按鈕進行事件綁定;每當按鈕事件觸發,則啟動線程和TCP服務器進行通信;線程將處理的結果通過msg傳給Handler,Handler根據相應消息來更新頁面。
1 public class ControlActivity extends Activity implements Callback { 2 3 private final String TAG = "ControlActivity"; 4 private final String mAddress = MainActivity.userIP; 5 private final int mPort = MainActivity.userPort; 6 private Socket socket = null; 7 private Button btHeat,btShut,btUpdata; 8 private SurfaceView mSurface; //繪圖區 9 private SurfaceHolder mHolder; 10 //消息句柄(線程里無法進行界面更新,所以要把消息從線程里發送出來在消息句柄里進行處理) 11 public Handler myHandler = new Handler() { 12 @Override 13 public void handleMessage(Message msg) 14 { 15 Bundle bundle = msg.getData(); 16 String now = bundle.getString("msg"); 17 //SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式 18 if (msg.what == 0x01) 19 { 20 toast_show("飲水機開始加熱!"); 21 } 22 else if (msg.what == 0x02) 23 { 24 toast_show("飲水機關閉!"); 25 } 26 else if (msg.what == 0x03) 27 { 28 toast_show("飲水機實時狀態更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei); 29 draw(MainActivity.wen_du); 30 } 31 else 32 { 33 toast_show("出現錯誤!"); 34 } 35 } 36 //toast顯示用 37 private void toast_show(String msg) { 38 Toast toast = Toast.makeText(getApplicationContext(), 39 msg, Toast.LENGTH_LONG); 40 toast.setGravity(Gravity.CENTER, 0, 0); 41 toast.show(); 42 } 43 //畫圖像 44 private void draw(int wen_du) { 45 int y = 260 - wen_du * 2; 46 Canvas canvas = mHolder.lockCanvas(); 47 Paint mPaint = new Paint(); 48 mPaint.setColor(Color.WHITE); 49 canvas.drawRect(40, 50, 60, 280, mPaint); 50 Paint paintCircle = new Paint(); 51 paintCircle.setColor(Color.RED); 52 Paint paintLine = new Paint(); 53 paintLine.setColor(Color.BLUE); 54 canvas.drawRect(40, y, 60, 280, paintCircle); 55 canvas.drawCircle(50, 300, 25, paintCircle); 56 int ydegree = 260; 57 int tem = 0;//刻度0~100 58 while (ydegree > 55) { 59 canvas.drawLine(60, ydegree, 67, ydegree, mPaint); 60 if (ydegree % 20 == 0) { 61 canvas.drawLine(60, ydegree, 72, ydegree, paintLine); 62 canvas.drawText(tem + "", 70, ydegree + 4, mPaint); 63 tem+=10; 64 } 65 ydegree = ydegree - 2; 66 } 67 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕顯示內容 68 } 69 }; 70 71 protected void onCreate(Bundle savedInstanceState) 72 { 73 super.onCreate(savedInstanceState); 74 setContentView(R.layout.control_activity); 75 btHeat = (Button)findViewById(R.id.bt_heat); 76 btShut = (Button)findViewById(R.id.bt_shut); 77 btUpdata = (Button)findViewById(R.id.bt_updata); 78 mSurface = (SurfaceView) findViewById(R.id.surface); 79 mHolder = mSurface.getHolder(); 80 mHolder.addCallback(this); 81 82 btHeat.setOnClickListener(new OnClickListener() { 83 @Override 84 public void onClick(View v) 85 { 86 String orderMsg="Heat"; 87 //啟動線程 向服務器發送和接收信息 88 Log.i(TAG, "Start thread"); 89 new MyThread(orderMsg).start(); 90 } 91 }); 92 93 btHeat.setOnClickListener(new OnClickListener() { 94 @Override 95 public void onClick(View v) 96 { 97 String orderMsg="Heat"; 98 //啟動線程 向服務器發送和接收信息 99 Log.i(TAG, "Start thread"); 100 new MyThread(orderMsg).start(); 101 } 102 }); 103 104 btShut.setOnClickListener(new OnClickListener() { 105 @Override 106 public void onClick(View v) 107 { 108 String orderMsg="Shut"; 109 //啟動線程 向服務器發送和接收信息 110 Log.i(TAG, "Start thread"); 111 new MyThread(orderMsg).start(); 112 } 113 }); 114 115 btUpdata.setOnClickListener(new OnClickListener() { 116 @Override 117 public void onClick(View v) 118 { 119 String orderMsg="Updata"; 120 //啟動線程 向服務器發送和接收信息 121 Log.i(TAG, "Start thread"); 122 new MyThread(orderMsg).start(); 123 } 124 }); 125 } 126 127 class MyThread extends Thread 128 { 129 String orderMsg; 130 MyThread(String str) 131 { 132 orderMsg=str; 133 } 134 @SuppressLint("SimpleDateFormat") 135 public void run() 136 { 137 OutputStream out = null; 138 InputStream in = null; 139 DataInputStream DataIn = null;//數據傳輸輸入輸出流 140 DataOutputStream DataOut = null; 141 byte data_of_get_server = 0;//從服務器返回的數據 142 Message msg = new Message();//消息 143 Bundle bundle = new Bundle(); 144 bundle.clear(); 145 try 146 { 147 socket = new Socket(); 148 socket.connect(new InetSocketAddress(mAddress, mPort), 8000); 149 150 //輸入輸出流實例化 151 out=socket.getOutputStream(); 152 in=socket.getInputStream(); 153 DataIn = new DataInputStream(in); 154 DataOut=new DataOutputStream(out); 155 156 //讀取服務器的返回數據 157 //服務器采用單byte數據進行發送 158 /* 159 TCP客戶端:輸入命令從服務器獲得數據 160 PS:服務器只接受1個char,返回也是一個char,上述數據均為16進制 161 */ 162 if(orderMsg.equals("Heat"))//加熱命令 163 { 164 msg.what = 0x01;//消息類別 165 DataOut.writeByte('0'); 166 Log.i(TAG, "flush 前"); 167 out.flush(); 168 Log.i(TAG, "flush 后"); 169 data_of_get_server=DataIn.readByte(); 170 Log.i(TAG, "讀取數據后"); 171 } 172 else if(orderMsg.equals("Shut")) 173 { 174 msg.what = 0x02;//消息類別 175 DataOut.writeByte('0');//停止加熱 176 out.flush(); 177 data_of_get_server=DataIn.readByte(); 178 } 179 else if(orderMsg.equals("Updata")) 180 { 181 msg.what = 0x03;//消息類別 182 DataOut.writeByte('w');//刷新溫度信息 183 out.flush(); 184 data_of_get_server=DataIn.readByte(); 185 MainActivity.wen_du=data_of_get_server; 186 187 DataOut.writeByte('s');//刷新深度信息 188 out.flush(); 189 data_of_get_server=DataIn.readByte(); 190 MainActivity.shui_wei=data_of_get_server; 191 } 192 //將消息發送給UI刷新消息句柄處 193 bundle.putByte("msg",data_of_get_server); 194 msg.setData(bundle); 195 myHandler.sendMessage(msg); 196 } 197 catch(Exception e){ 198 e.printStackTrace(); 199 //Intent intent = new Intent(ControlActivity.this,MainActivity.class); 200 //Log.i(TAG, "跳轉前"); 201 //startActivity(intent); 202 //將消息發送給UI刷新消息句柄處 203 msg.what = 0x04;//消息類別 204 bundle.putByte("msg",data_of_get_server); 205 msg.setData(bundle); 206 myHandler.sendMessage(msg); 207 }finally{ 208 try{ 209 if(in!=null)in.close();Log.i(TAG, "讀取數據后1"); 210 if(out!=null)out.close();Log.i(TAG, "讀取數據后2"); 211 if(DataOut!=null)DataOut.close();Log.i(TAG, "讀取數據后3"); 212 if(DataIn!=null)DataIn.close();Log.i(TAG, "讀取數據后4"); 213 if(socket!=null)socket.close();Log.i(TAG, "讀取數據后5"); 214 }catch(Exception e){} 215 } 216 } 217 } 218 219 @Override 220 public void surfaceCreated(SurfaceHolder holder) { 221 // TODO Auto-generated method stub 222 223 } 224 225 @Override 226 public void surfaceChanged(SurfaceHolder holder, int format, int width, 227 int height) { 228 // TODO Auto-generated method stub 229 230 } 231 232 @Override 233 public void surfaceDestroyed(SurfaceHolder holder) { 234 // TODO Auto-generated method stub 235 236 } 237 } 全部代碼:3-2、消息傳遞詳細介紹:
如下,第9-10行要加入回調函數;12-21、23-32、34-43以及45-54分別是幾個按鈕的點擊監聽函數,其中對于不同的情況,通過設置orderMsg進行區別,然后啟動相應的線程和服務器通信。
1 protected void onCreate(Bundle savedInstanceState) 2 { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.control_activity); 5 btHeat = (Button)findViewById(R.id.bt_heat); 6 btShut = (Button)findViewById(R.id.bt_shut); 7 btUpdata = (Button)findViewById(R.id.bt_updata); 8 mSurface = (SurfaceView) findViewById(R.id.surface); 9 mHolder = mSurface.getHolder(); 10 mHolder.addCallback(this); 11 12 btHeat.setOnClickListener(new OnClickListener() { 13 @Override 14 public void onClick(View v) 15 { 16 String orderMsg="Heat"; 17 //啟動線程 向服務器發送和接收信息 18 Log.i(TAG, "Start thread"); 19 new MyThread(orderMsg).start(); 20 } 21 }); 22 23 btHeat.setOnClickListener(new OnClickListener() { 24 @Override 25 public void onClick(View v) 26 { 27 String orderMsg="Heat"; 28 //啟動線程 向服務器發送和接收信息 29 Log.i(TAG, "Start thread"); 30 new MyThread(orderMsg).start(); 31 } 32 }); 33 34 btShut.setOnClickListener(new OnClickListener() { 35 @Override 36 public void onClick(View v) 37 { 38 String orderMsg="Shut"; 39 //啟動線程 向服務器發送和接收信息 40 Log.i(TAG, "Start thread"); 41 new MyThread(orderMsg).start(); 42 } 43 }); 44 45 btUpdata.setOnClickListener(new OnClickListener() { 46 @Override 47 public void onClick(View v) 48 { 49 String orderMsg="Updata"; 50 //啟動線程 向服務器發送和接收信息 51 Log.i(TAG, "Start thread"); 52 new MyThread(orderMsg).start(); 53 } 54 }); 55 }從上面知道,我們必須有一個MyThread的類:構造函數就是把上面說的用于區分命令的orderMsg賦值給MyThread成員變量,然后在run函數中:11-14行來定義用于和TCP服務器通信的輸入輸出流;第15行的變量是記錄從服務器返回的數據(這里服務器每次只返回一個byte類,這個取決于通信協議的約定!);第16-18行實例化的msg、bundle用于傳送消息,對應的第77-80行,想要發送消息要:①首先設置消息類別(這里出錯消息類別為0x04:msg.what = 0x04;加熱為0x01;停止加熱為0x02等)②然后用bundle將信息合成bundle.putByte("msg",data_of_get_server);其中第一個string為key,第二個為value ③然后將msg的信息設置為handle,即:msg.setData(bundle); ④最后用myHandler將消息發出:myHandler.sendMessage(msg);
1 class MyThread extends Thread 2 { 3 String orderMsg; 4 MyThread(String str) 5 { 6 orderMsg=str; 7 } 8 @SuppressLint("SimpleDateFormat") 9 public void run() 10 { 11 OutputStream out = null; 12 InputStream in = null; 13 DataInputStream DataIn = null;//數據傳輸輸入輸出流 14 DataOutputStream DataOut = null; 15 byte data_of_get_server = 0;//從服務器返回的數據 16 Message msg = new Message();//消息 17 Bundle bundle = new Bundle(); 18 bundle.clear(); 19 try 20 { 21 socket = new Socket(); 22 socket.connect(new InetSocketAddress(mAddress, mPort), 8000); 23 24 //輸入輸出流實例化 25 out=socket.getOutputStream(); 26 in=socket.getInputStream(); 27 DataIn = new DataInputStream(in); 28 DataOut=new DataOutputStream(out); 29 30 //讀取服務器的返回數據 31 //服務器采用單byte數據進行發送 32 /* 33 TCP客戶端:輸入命令從服務器獲得數據 34 PS:服務器只接受1個char,返回也是一個char,上述數據均為16進制 35 */ 36 if(orderMsg.equals("Heat"))//加熱命令 37 { 38 msg.what = 0x01;//消息類別 39 DataOut.writeByte('0'); 40 Log.i(TAG, "flush 前"); 41 out.flush(); 42 Log.i(TAG, "flush 后"); 43 data_of_get_server=DataIn.readByte(); 44 Log.i(TAG, "讀取數據后"); 45 } 46 else if(orderMsg.equals("Shut")) 47 { 48 msg.what = 0x02;//消息類別 49 DataOut.writeByte('0');//停止加熱 50 out.flush(); 51 data_of_get_server=DataIn.readByte(); 52 } 53 else if(orderMsg.equals("Updata")) 54 { 55 msg.what = 0x03;//消息類別 56 DataOut.writeByte('w');//刷新溫度信息 57 out.flush(); 58 data_of_get_server=DataIn.readByte(); 59 MainActivity.wen_du=data_of_get_server; 60 61 DataOut.writeByte('s');//刷新深度信息 62 out.flush(); 63 data_of_get_server=DataIn.readByte(); 64 MainActivity.shui_wei=data_of_get_server; 65 } 66 //將消息發送給UI刷新消息句柄處 67 bundle.putByte("msg",data_of_get_server); 68 msg.setData(bundle); 69 myHandler.sendMessage(msg); 70 } 71 catch(Exception e){ 72 e.printStackTrace(); 73 //Intent intent = new Intent(ControlActivity.this,MainActivity.class); 74 //Log.i(TAG, "跳轉前"); 75 //startActivity(intent); 76 //將消息發送給UI刷新消息句柄處 77 msg.what = 0x04;//消息類別 78 bundle.putByte("msg",data_of_get_server); 79 msg.setData(bundle); 80 myHandler.sendMessage(msg); 81 }finally{ 82 try{ 83 if(in!=null)in.close();Log.i(TAG, "讀取數據后1"); 84 if(out!=null)out.close();Log.i(TAG, "讀取數據后2"); 85 if(DataOut!=null)DataOut.close();Log.i(TAG, "讀取數據后3"); 86 if(DataIn!=null)DataIn.close();Log.i(TAG, "讀取數據后4"); 87 if(socket!=null)socket.close();Log.i(TAG, "讀取數據后5"); 88 }catch(Exception e){} 89 } 90 } 91 }接下來就是myHandler了:第6-7行是取消息的過程,其和放消息有種逆過程的感覺,首先用bundle取出消息:Bundle bundle = msg.getData(); 然后通過String now = bundle.getString("msg");獲得鍵值為“msg”的value,然后分類處理即可。其中toast_show是自己封裝的用于顯示toast消息的函數,draw是用來繪制那個溫度計的函數。
1 //消息句柄(線程里無法進行界面更新,所以要把消息從線程里發送出來在消息句柄里進行處理) 2 public Handler myHandler = new Handler() { 3 @Override 4 public void handleMessage(Message msg) 5 { 6 Bundle bundle = msg.getData(); 7 String now = bundle.getString("msg"); 8 //SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式 9 if (msg.what == 0x01) 10 { 11 toast_show("飲水機開始加熱!"); 12 } 13 else if (msg.what == 0x02) 14 { 15 toast_show("飲水機關閉!"); 16 } 17 else if (msg.what == 0x03) 18 { 19 toast_show("飲水機實時狀態更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei); 20 draw(MainActivity.wen_du); 21 } 22 else 23 { 24 toast_show("出現錯誤!"); 25 } 26 } 27 //toast顯示用 28 private void toast_show(String msg) { 29 Toast toast = Toast.makeText(getApplicationContext(), 30 msg, Toast.LENGTH_LONG); 31 toast.setGravity(Gravity.CENTER, 0, 0); 32 toast.show(); 33 } 34 //畫圖像 35 private void draw(int wen_du) { 36 int y = 260 - wen_du * 2; 37 Canvas canvas = mHolder.lockCanvas(); 38 Paint mPaint = new Paint(); 39 mPaint.setColor(Color.WHITE); 40 canvas.drawRect(40, 50, 60, 280, mPaint); 41 Paint paintCircle = new Paint(); 42 paintCircle.setColor(Color.RED); 43 Paint paintLine = new Paint(); 44 paintLine.setColor(Color.BLUE); 45 canvas.drawRect(40, y, 60, 280, paintCircle); 46 canvas.drawCircle(50, 300, 25, paintCircle); 47 int ydegree = 260; 48 int tem = 0;//刻度0~100 49 while (ydegree > 55) { 50 canvas.drawLine(60, ydegree, 67, ydegree, mPaint); 51 if (ydegree % 20 == 0) { 52 canvas.drawLine(60, ydegree, 72, ydegree, paintLine); 53 canvas.drawText(tem + "", 70, ydegree + 4, mPaint); 54 tem+=10; 55 } 56 ydegree = ydegree - 2; 57 } 58 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕顯示內容 59 } 60 };?
?
?
?本文鏈接:http://www.cnblogs.com/zjutlitao/p/4230360.html
?更多精彩:http://www.cnblogs.com/zjutlitao/
?工程鏈接:http://pan.baidu.com/s/1i3zhMVr?
?GitHub鏈接:https://github.com/beautifulzzzz/SmartDrink?
?
轉載于:https://www.cnblogs.com/zjutlitao/p/4230360.html
總結
以上是生活随笔為你收集整理的[安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片上传知识点梳理
- 下一篇: Java多线程系列---“JUC锁”01