[安卓] 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 ...
随机推荐
- 正确停止kafka的方法
kill -15 pid 即: kill SIGNTERM pid 不要使用kill -9. kill -15会触发调用shutdownHook的run方法,从而可以执行关闭服务器的时候一些必要代码. ...
- 循序渐进之Spring AOP(6) - 使用@Aspect注解
前面几节的示例看起来让人沮丧,要记忆如此多的接口.类和继承关系,做各种复杂的配置.好在这些只是一种相对过时的实现方式,现在只需要使用@Aspect注解及表达式就可以轻松的使用POJO来定义切面,设计精 ...
- ORACLE 数据的逻辑组成
数据块(block) Oracle数据块(Data Block)是一组连续的操作系统块.分配数据库块大小是在Oracle数据库创建时设置的,数据块是Oracle读写的基本单位.数据块的大小一般是操作系 ...
- C# 关于时间
1.2016/7/8 00:10:10 转换成 2016-07-08T 00:10:10 在用VB动态调用WevService的时候,传入的时间格式为2016/7/8 00:10:10,导致调用出错, ...
- poj 2060 Taxi Cab Scheme (二分匹配)
Taxi Cab Scheme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 5710 Accepted: 2393 D ...
- DIV半透明,内层不受影响的代码
<div style=" background:rgba(0, 0, 0, 0.1)"><br /> <img src="http://im ...
- Hex编码字节
1.将字节数组转换为字符串 /** * 将字节数组转换为字符串 * 一个字节会形成两个字符,最终长度是原始数据的2倍 * @param data * @return */ public static ...
- 問題排查:行動裝置網頁前端 UI 設計 (2)
之前上網找了個星級評分的範例來玩, 當然這個範例已經用在另一個專案了, 目前看起來沒什麼狀況, 不過在移植到目前的專案之後, 就出現了怪現象: 1. 在大部份時間裡,點擊星星不會有任何反應 2. 即便 ...
- 2015/09/09夜晚js继续学习
单词:标量(scalar)数组(array)元素(element)填充(populating)下标(index) 向数组中添加元素的操作称之填充.在填充数组时,不仅需要给出新元素的值,还需要给新元素在 ...
- 烧写AT91Bootstrap不能连接SAM-BA的解决方法
AT91与SAM-BA的连接是由于芯片内有一段固化的代码运行起来后才会检测到目标板并建立连接. 假设现在你烧写了Bootstrap进去,芯片上电后发现有可运行的代码,从而就不执行片内固化的那个代 ...