[安卓] 9、线程、VIEW、消息实现从TCP服务器获取数据动态加载显示
一、前言:
一般情况下从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服务器获取数据动态加载显示的更多相关文章
- 线性布局通过适配器可以动态加载view
因为适配器的getView的返回对象是view, 所以线性布局的对象可以通过addView动态加载适配器的getView 举例: mTestAdapter = new ListAdapter(this ...
- 动态加载Layout 与 论Activity、 Window、View的关系
1)动态加载Layout的代码是 getWindow().setContentView(LayoutInflater.from(this).inflate(R.layout.main, null)); ...
- 动态加载dll的实现+远线程注入
1.在目标进程中申请内存 2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦) 3.创建远线程执行shellcode 之前可以看到shellcode很难编写还要去依赖库,去字符串区等等 ...
- 【Android编程实战】源码级免杀_Dex动态加载技术_Metasploit安卓载荷傀儡机代码复现
/文章作者:MG193.7 CNBLOG博客ID:ALDYS4 QQ:3496925334/ 在读者阅读本文章前,建议先阅读笔者之前写的一篇对安卓载荷的分析文章 [逆向&编程实战]Metasp ...
- Android学习笔记_31_通过后台代码生成View对象以及动态加载XML布局文件到LinearLayout
一.布局文件part.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...
- 并发编程中死锁、递归锁、进程/线程池、协程TCP服务器并发等知识点
1.死锁 定义; 类似两个人分别被囚禁在两间房子里,A手上拿着的是B囚禁房间的钥匙,而B拿着A的钥匙,两个人都没法出去,没法给对方开锁,进而造成死锁现象.具体例子代码如下: # -*-coding:u ...
- 安卓studio导入jra包和so包,百度地图so包加载
导入so包 这个我只接受测试可用的一种方法 第一步:把so包放在libs目录下,可以是文件夹也可以是单独的一个个so文件 然后在src同级的目录下找到build.gradle文件下如下信息 sourc ...
- python day 20: 线程池与协程,多进程TCP服务器
目录 python day 20: 线程池与协程 2. 线程 3. 进程 4. 协程:gevent模块,又叫微线程 5. 扩展 6. 自定义线程池 7. 实现多进程TCP服务器 8. 实现多线程TCP ...
- Mina、Netty、Twisted一起学(一):实现简单的TCP服务器
MINA.Netty.Twisted为什么放在一起学习?首先,不妨先分别看一下它们官方网站对其的介绍: MINA: Apache MINA is a network application frame ...
随机推荐
- 5.Mybatis的输出映射(就是对查询的结果集的映射)
Mybatis的输出映射,也就是对查询结果集的一个映射,主要有两种: 1.resultType(不需要配置,可以直接用) 一般是实体类 基本类型也可以 2.resultMap(需要配置resultMa ...
- Spring中Aware相关接口原理
Spring中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.ServletContextA ...
- ORACLE 10进制与16进制的互相转换
1. 10---->16 使用to_char(10,'xxx')函数,如果位数长,多加几个 x 2. 16---->10 使用to_number(’a','xxx')函数,如果位数长,多加 ...
- 在eclipse之中使用Junit
使用Junit单位测试,能够一定程度上减小项目bug的产生.很多时候,我们都是在main函数里面单独去测试一个方法,这样的测试非常不方便.使用Junit可以是测试单位和项目代码分离,一次性测试多个方法 ...
- 从 Eclipse 导入项目到 Android Studio
ADT已经有些过时了,现在比较流行的安卓开发工具是Android Studio,毕竟是谷歌一直在推广的. 最近装了as,从之前的adt迁移项目到as上,遇到了不少坎. 主要注意以下两点: (1)文件路 ...
- laravel 事件的使用案例
以下是我对事件使用的一些记录 创建事件 执行以下命令,执行完成后,会在 app\Events 下面出现一个 DeleteEvent.php 文件,事件就在次定义 php artisan make:ev ...
- Sql Server 孤立用户解决办法
Sql Server 孤立用户 是我们经常遇到的事情,今天详细的梳理了下,希望能帮到你 当把用户数据库从一台 Sql Server 使用备份和恢复的方式迁移到另一台服务器.数据库恢复以后,原先用户定义 ...
- 返回绝对值--Math.Abs 方法
Math.abs() 返回指定数字的绝对值.
- coreseek实战(四):php接口的使用,完善php脚本代码
coreseek实战(四):php接口的使用,完善php脚本代码 在上一篇文章 coreseeek实战(三)中,已经能够正常搜索到结果,这篇文章主要是把 index.php 文件代码写得相对完整一点点 ...
- Eclipse 反编译插件安装jad
Eclipse的反编译插件一直在用jad,感觉很不错. 刚下了个新版的eclipse,配置jad的时候发现要多设置个东西. 从头开始 环境介绍: eclipse version:Kepler Rele ...