一、前言:

一般情况下从TCP服务器读取数据是放在一个线程里读的,但是刷新界面又不得不放在线程外面,所以需要用消息传递把线程里从TCP里获得的数据传送出来,然后根据数据对页面进行相应的刷新。

二、业务逻辑:

 

这里包含2个layout,第一个用于登陆的(即输入服务器对应的IP和端口号),点击确定进行跳转到相应的监控界面,监控界面包括加热、关闭、和显示温度3个按钮,以及一个用于绘制温度的SurfaceView。

三、详细介绍:

3-1、2个activity介绍:

登陆页面对应的activity,从上面的代码可以看出:29~31行使用intent进行页面跳转,然后所有逻辑均在ControlActivity里实现了。

 public class MainActivity extends ActionBarActivity
{
private final String TAG = "MainActivity";
private EditText et01;
private EditText et02;
private Button btOK;
private Button btCancel;
public static String userIP = "192.168.1.130"; //IP和端口号
public static int userPort = 8000;
public static int wen_du; //当前温度
public static int shui_wei; //当前水位
public static int state; //当前状态0关闭;1烧水;2保温
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et01 = (EditText)findViewById(R.id.et_01);
et02 = (EditText)findViewById(R.id.et_02);
btOK = (Button)findViewById(R.id.bt_OK);
btCancel = (Button)findViewById(R.id.bt_Cancel); btOK.setOnClickListener(new OnClickListener(){
public void onClick(View v)
{
//userIP = et01.getText().toString();
//userPort = Integer.parseInt(et02.getText().toString());
//跳到控制界面
Intent intent = new Intent(MainActivity.this,ControlActivity.class);
Log.i(TAG, "跳转前");
startActivity(intent);
}
});
btCancel.setOnClickListener(new OnClickListener(){
public void onClick(View v)
{
et01.setText("");
et02.setText("");
}
}); } @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

另一个activity的框架如下图:主要的有①、②、③三个函数,另外三个是Callback附带要实现的。其主要逻辑为:在onCreate中实例化按钮和surfaceView,然后对按钮进行事件绑定;每当按钮事件触发,则启动线程和TCP服务器进行通信;线程将处理的结果通过msg传给Handler,Handler根据相应消息来更新页面。

 public class ControlActivity extends Activity implements Callback {

     private final String TAG = "ControlActivity";
private final String mAddress = MainActivity.userIP;
private final int mPort = MainActivity.userPort;
private Socket socket = null;
private Button btHeat,btShut,btUpdata;
private SurfaceView mSurface; //绘图区
private SurfaceHolder mHolder;
//消息句柄(线程里无法进行界面更新,所以要把消息从线程里发送出来在消息句柄里进行处理)
public Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg)
{
Bundle bundle = msg.getData();
String now = bundle.getString("msg");
//SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
if (msg.what == 0x01)
{
toast_show("饮水机开始加热!");
}
else if (msg.what == 0x02)
{
toast_show("饮水机关闭!");
}
else if (msg.what == 0x03)
{
toast_show("饮水机实时状态更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei);
draw(MainActivity.wen_du);
}
else
{
toast_show("出现错误!");
}
}
//toast显示用
private void toast_show(String msg) {
Toast toast = Toast.makeText(getApplicationContext(),
msg, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
//画图像
private void draw(int wen_du) {
int y = 260 - wen_du * 2;
Canvas canvas = mHolder.lockCanvas();
Paint mPaint = new Paint();
mPaint.setColor(Color.WHITE);
canvas.drawRect(40, 50, 60, 280, mPaint);
Paint paintCircle = new Paint();
paintCircle.setColor(Color.RED);
Paint paintLine = new Paint();
paintLine.setColor(Color.BLUE);
canvas.drawRect(40, y, 60, 280, paintCircle);
canvas.drawCircle(50, 300, 25, paintCircle);
int ydegree = 260;
int tem = 0;//刻度0~100
while (ydegree > 55) {
canvas.drawLine(60, ydegree, 67, ydegree, mPaint);
if (ydegree % 20 == 0) {
canvas.drawLine(60, ydegree, 72, ydegree, paintLine);
canvas.drawText(tem + "", 70, ydegree + 4, mPaint);
tem+=10;
}
ydegree = ydegree - 2;
}
mHolder.unlockCanvasAndPost(canvas);// 更新屏幕显示内容
}
}; protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.control_activity);
btHeat = (Button)findViewById(R.id.bt_heat);
btShut = (Button)findViewById(R.id.bt_shut);
btUpdata = (Button)findViewById(R.id.bt_updata);
mSurface = (SurfaceView) findViewById(R.id.surface);
mHolder = mSurface.getHolder();
mHolder.addCallback(this); btHeat.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Heat";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btHeat.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Heat";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btShut.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Shut";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btUpdata.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Updata";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
});
} class MyThread extends Thread
{
String orderMsg;
MyThread(String str)
{
orderMsg=str;
}
@SuppressLint("SimpleDateFormat")
public void run()
{
OutputStream out = null;
InputStream in = null;
DataInputStream DataIn = null;//数据传输输入输出流
DataOutputStream DataOut = null;
byte data_of_get_server = 0;//从服务器返回的数据
Message msg = new Message();//消息
Bundle bundle = new Bundle();
bundle.clear();
try
{
socket = new Socket();
socket.connect(new InetSocketAddress(mAddress, mPort), 8000); //输入输出流实例化
out=socket.getOutputStream();
in=socket.getInputStream();
DataIn = new DataInputStream(in);
DataOut=new DataOutputStream(out); //读取服务器的返回数据
//服务器采用单byte数据进行发送
/*
TCP客户端:输入命令从服务器获得数据
PS:服务器只接受1个char,返回也是一个char,上述数据均为16进制
*/
if(orderMsg.equals("Heat"))//加热命令
{
msg.what = 0x01;//消息类别
DataOut.writeByte('0');
Log.i(TAG, "flush 前");
out.flush();
Log.i(TAG, "flush 后");
data_of_get_server=DataIn.readByte();
Log.i(TAG, "读取数据后");
}
else if(orderMsg.equals("Shut"))
{
msg.what = 0x02;//消息类别
DataOut.writeByte('0');//停止加热
out.flush();
data_of_get_server=DataIn.readByte();
}
else if(orderMsg.equals("Updata"))
{
msg.what = 0x03;//消息类别
DataOut.writeByte('w');//刷新温度信息
out.flush();
data_of_get_server=DataIn.readByte();
MainActivity.wen_du=data_of_get_server; DataOut.writeByte('s');//刷新深度信息
out.flush();
data_of_get_server=DataIn.readByte();
MainActivity.shui_wei=data_of_get_server;
}
//将消息发送给UI刷新消息句柄处
bundle.putByte("msg",data_of_get_server);
msg.setData(bundle);
myHandler.sendMessage(msg);
}
catch(Exception e){
e.printStackTrace();
//Intent intent = new Intent(ControlActivity.this,MainActivity.class);
//Log.i(TAG, "跳转前");
//startActivity(intent);
//将消息发送给UI刷新消息句柄处
msg.what = 0x04;//消息类别
bundle.putByte("msg",data_of_get_server);
msg.setData(bundle);
myHandler.sendMessage(msg);
}finally{
try{
if(in!=null)in.close();Log.i(TAG, "读取数据后1");
if(out!=null)out.close();Log.i(TAG, "读取数据后2");
if(DataOut!=null)DataOut.close();Log.i(TAG, "读取数据后3");
if(DataIn!=null)DataIn.close();Log.i(TAG, "读取数据后4");
if(socket!=null)socket.close();Log.i(TAG, "读取数据后5");
}catch(Exception e){}
}
}
} @Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub } @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub } @Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub }
}

全部代码:

3-2、消息传递详细介绍:

如下,第9-10行要加入回调函数;12-21、23-32、34-43以及45-54分别是几个按钮的点击监听函数,其中对于不同的情况,通过设置orderMsg进行区别,然后启动相应的线程和服务器通信。

 protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.control_activity);
btHeat = (Button)findViewById(R.id.bt_heat);
btShut = (Button)findViewById(R.id.bt_shut);
btUpdata = (Button)findViewById(R.id.bt_updata);
mSurface = (SurfaceView) findViewById(R.id.surface);
mHolder = mSurface.getHolder();
mHolder.addCallback(this); btHeat.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Heat";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btHeat.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Heat";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btShut.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Shut";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
}); btUpdata.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
String orderMsg="Updata";
//启动线程 向服务器发送和接收信息
Log.i(TAG, "Start thread");
new MyThread(orderMsg).start();
}
});
}

从上面知道,我们必须有一个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);

 class MyThread extends Thread
{
String orderMsg;
MyThread(String str)
{
orderMsg=str;
}
@SuppressLint("SimpleDateFormat")
public void run()
{
OutputStream out = null;
InputStream in = null;
DataInputStream DataIn = null;//数据传输输入输出流
DataOutputStream DataOut = null;
byte data_of_get_server = 0;//从服务器返回的数据
Message msg = new Message();//消息
Bundle bundle = new Bundle();
bundle.clear();
try
{
socket = new Socket();
socket.connect(new InetSocketAddress(mAddress, mPort), 8000); //输入输出流实例化
out=socket.getOutputStream();
in=socket.getInputStream();
DataIn = new DataInputStream(in);
DataOut=new DataOutputStream(out); //读取服务器的返回数据
//服务器采用单byte数据进行发送
/*
TCP客户端:输入命令从服务器获得数据
PS:服务器只接受1个char,返回也是一个char,上述数据均为16进制
*/
if(orderMsg.equals("Heat"))//加热命令
{
msg.what = 0x01;//消息类别
DataOut.writeByte('0');
Log.i(TAG, "flush 前");
out.flush();
Log.i(TAG, "flush 后");
data_of_get_server=DataIn.readByte();
Log.i(TAG, "读取数据后");
}
else if(orderMsg.equals("Shut"))
{
msg.what = 0x02;//消息类别
DataOut.writeByte('0');//停止加热
out.flush();
data_of_get_server=DataIn.readByte();
}
else if(orderMsg.equals("Updata"))
{
msg.what = 0x03;//消息类别
DataOut.writeByte('w');//刷新温度信息
out.flush();
data_of_get_server=DataIn.readByte();
MainActivity.wen_du=data_of_get_server; DataOut.writeByte('s');//刷新深度信息
out.flush();
data_of_get_server=DataIn.readByte();
MainActivity.shui_wei=data_of_get_server;
}
//将消息发送给UI刷新消息句柄处
bundle.putByte("msg",data_of_get_server);
msg.setData(bundle);
myHandler.sendMessage(msg);
}
catch(Exception e){
e.printStackTrace();
//Intent intent = new Intent(ControlActivity.this,MainActivity.class);
//Log.i(TAG, "跳转前");
//startActivity(intent);
//将消息发送给UI刷新消息句柄处
msg.what = 0x04;//消息类别
bundle.putByte("msg",data_of_get_server);
msg.setData(bundle);
myHandler.sendMessage(msg);
}finally{
try{
if(in!=null)in.close();Log.i(TAG, "读取数据后1");
if(out!=null)out.close();Log.i(TAG, "读取数据后2");
if(DataOut!=null)DataOut.close();Log.i(TAG, "读取数据后3");
if(DataIn!=null)DataIn.close();Log.i(TAG, "读取数据后4");
if(socket!=null)socket.close();Log.i(TAG, "读取数据后5");
}catch(Exception e){}
}
}
}

接下来就是myHandler了:第6-7行是取消息的过程,其和放消息有种逆过程的感觉,首先用bundle取出消息:Bundle bundle = msg.getData(); 然后通过String now = bundle.getString("msg");获得键值为“msg”的value,然后分类处理即可。其中toast_show是自己封装的用于显示toast消息的函数,draw是用来绘制那个温度计的函数。

 //消息句柄(线程里无法进行界面更新,所以要把消息从线程里发送出来在消息句柄里进行处理)
public Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg)
{
Bundle bundle = msg.getData();
String now = bundle.getString("msg");
//SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
if (msg.what == 0x01)
{
toast_show("饮水机开始加热!");
}
else if (msg.what == 0x02)
{
toast_show("饮水机关闭!");
}
else if (msg.what == 0x03)
{
toast_show("饮水机实时状态更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei);
draw(MainActivity.wen_du);
}
else
{
toast_show("出现错误!");
}
}
//toast显示用
private void toast_show(String msg) {
Toast toast = Toast.makeText(getApplicationContext(),
msg, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
//画图像
private void draw(int wen_du) {
int y = 260 - wen_du * 2;
Canvas canvas = mHolder.lockCanvas();
Paint mPaint = new Paint();
mPaint.setColor(Color.WHITE);
canvas.drawRect(40, 50, 60, 280, mPaint);
Paint paintCircle = new Paint();
paintCircle.setColor(Color.RED);
Paint paintLine = new Paint();
paintLine.setColor(Color.BLUE);
canvas.drawRect(40, y, 60, 280, paintCircle);
canvas.drawCircle(50, 300, 25, paintCircle);
int ydegree = 260;
int tem = 0;//刻度0~100
while (ydegree > 55) {
canvas.drawLine(60, ydegree, 67, ydegree, mPaint);
if (ydegree % 20 == 0) {
canvas.drawLine(60, ydegree, 72, ydegree, paintLine);
canvas.drawText(tem + "", 70, ydegree + 4, mPaint);
tem+=10;
}
ydegree = ydegree - 2;
}
mHolder.unlockCanvasAndPost(canvas);// 更新屏幕显示内容
}
};

本文链接: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

[安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示的更多相关文章

  1. 线性布局通过适配器可以动态加载view

    因为适配器的getView的返回对象是view, 所以线性布局的对象可以通过addView动态加载适配器的getView 举例: mTestAdapter = new ListAdapter(this ...

  2. 动态加载Layout 与 论Activity、 Window、View的关系

    1)动态加载Layout的代码是 getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main, null)); ...

  3. 动态加载dll的实现+远线程注入

    1.在目标进程中申请内存 2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦) 3.创建远线程执行shellcode 之前可以看到shellcode很难编写还要去依赖库,去字符串区等等 ...

  4. 【Android编程实战】源码级免杀_Dex动态加载技术_Metasploit安卓载荷傀儡机代码复现

    /文章作者:MG193.7 CNBLOG博客ID:ALDYS4 QQ:3496925334/ 在读者阅读本文章前,建议先阅读笔者之前写的一篇对安卓载荷的分析文章 [逆向&编程实战]Metasp ...

  5. Android学习笔记_31_通过后台代码生成View对象以及动态加载XML布局文件到LinearLayout

    一.布局文件part.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...

  6. 并发编程中死锁、递归锁、进程/线程池、协程TCP服务器并发等知识点

    1.死锁 定义; 类似两个人分别被囚禁在两间房子里,A手上拿着的是B囚禁房间的钥匙,而B拿着A的钥匙,两个人都没法出去,没法给对方开锁,进而造成死锁现象.具体例子代码如下: # -*-coding:u ...

  7. 安卓studio导入jra包和so包,百度地图so包加载

    导入so包 这个我只接受测试可用的一种方法 第一步:把so包放在libs目录下,可以是文件夹也可以是单独的一个个so文件 然后在src同级的目录下找到build.gradle文件下如下信息 sourc ...

  8. python day 20: 线程池与协程,多进程TCP服务器

    目录 python day 20: 线程池与协程 2. 线程 3. 进程 4. 协程:gevent模块,又叫微线程 5. 扩展 6. 自定义线程池 7. 实现多进程TCP服务器 8. 实现多线程TCP ...

  9. Mina、Netty、Twisted一起学(一):实现简单的TCP服务器

    MINA.Netty.Twisted为什么放在一起学习?首先,不妨先分别看一下它们官方网站对其的介绍: MINA: Apache MINA is a network application frame ...

随机推荐

  1. HDOJ(1242)BFS+优先队列

    Rescue http://acm.hdu.edu.cn/showproblem.php?pid=1242 题意:"#"是墙,"."是路,"a&quo ...

  2. 手把手教你编写一个具有基本功能的shell(已开源)

    刚接触Linux时,对shell总有种神秘感:在对shell的工作原理有所了解之后,便尝试着动手写一个shell.下面是一个从最简单的情况开始,一步步完成一个模拟的shell(我命名之为wshell) ...

  3. 何修改WAMP中mysql默认空密码--转

    何修改WAMP中mysql默认空密码  http://www.cnblogs.com/hooray/archive/2011/07/23/2114792.html WAMP安装好后,mysql密码是为 ...

  4. detection reading

    1512.07729v1 G-CNN an Iterative Grid Based Object Detector,先基于空间金字塔生成很多矩形框,然后把这些矩形框作为regions,进行fast ...

  5. BIEE 11g学习

    biee 11g学习1. 创建资料档案库文件(RPD)  文件数据库 1.1 创建数据源连接          运行Net Manager 用于BIEE的数据库服务 1.2 模型的建立   1.运行o ...

  6. [转载] 散列表(Hash Table) 从理论到实用(下)

    转载自: 白话算法(6) 散列表(Hash Table) 从理论到实用(下) [澈丹,我想要个钻戒.][小北,等等吧,等我再修行两年,你把我烧了,舍利子比钻戒值钱.] ——自扯自蛋 无论开发一个程序还 ...

  7. IOS线程的一些总结

    主线程的作用 (在主线程中才能设置) 显示/刷新UI界面 处理UI事件(比如点击事件.滚动事件.拖拽事件): 主线程的使用注意 别将比较耗时的操作放到主线程中. 耗时操作会卡住主线程.影响体验. [N ...

  8. C# HttpWebRequest与HttpWebResponse详解

    C# HttpWebRequest与HttpWebResponse详解  http://www.codeproject.com/Articles/6554/How-to-use-HttpWebRequ ...

  9. CoreLocation框架的使用---定位,求两地距离

    前言: 在iOS开发中,有关导航,周边的开发,必须基于2个框架: Map Kit :用于地图展示 Core Location :用于地理定位   用户隐私的保护 从iOS 6开始,苹果在保护用户隐私方 ...

  10. [Chapter 3 Process]Practice 3.5 When a process creates a new process using the fork() operation

    3.5 When a process creates a new process using the fork() operation, which of the following state is ...