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

时间:2021-05-20 09:40:44

一、前言:

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

二、业务逻辑:

[安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示 [安卓] 9、线程、VIEW、消息实现从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根据相应消息来更新页面。

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

 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