• [安卓] 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 

     

  • 相关阅读:
    linux 文件系统管理三部曲之二:创建文件系统
    linux 文件系统管理三部曲之一:磁盘分区
    Django 链接MySQL及数据操作
    redis事务
    redis配置文件详解
    redis中hash数据类型
    redis的基础知识
    redis两种持久化方式RDB和AOF
    git命令
    .gitignore中添加的某个忽略文件并不生效
  • 原文地址:https://www.cnblogs.com/zjutlitao/p/4230360.html
Copyright © 2020-2023  润新知