android为什么不允许新开启一个线程来更新UI,而是用handler来更新界面

摘要:
那为什么不直接new一个新线程而要使用一个所谓的handler?就是因为new了一个子线程才要用handler的,不然在主线程里更新UI要handler干什么?handle是主线程,Thread是从线程。比如说等待1min后再进行刷新操作,这个时间段要保证主UI线程是可操作的,所以要用到Thread来更新。但是由于android是单线程模型,存在线程安全问题,所以当第二次刷新的时候,出现错误。但是在单线程模型的android中,是不能用的。

下面是快速创建一个新线程的方法:

第一种:直接创建子线程并启动
new Thread() {
@Override
public void run() {
//这里写入子线程需要做的工作
}
}.start();
第二种:先创建子线程,然后启动
private Thread newThread; //声明一个子线程
newThread = new Thread(new Runnable() {
@Override
public void run() {
//这里写入子线程需要做的工作
}
});

newThread.start(); //启动线程

操作是很有可能并发的,而界面只有一个
这个和买票排队是一回事
买票的人太多了,卖票的只有一个,只能一个一个来
如果你开多线程,让100个人同时去买票,而且不排队,那么后果会怎么样- -
同理,你开多线程,让100个线程去设置同一个TextView的显示内容,每个显示内容都不一样,它该听谁的?

那为什么不直接new一个新线程而要使用一个所谓的handler?

就是因为new了一个子线程才要用handler的,
不然在主线程里更新UI要handler干什么?多此一举
就好比只有1个人来买票,卖票的难道会跟他说:同志,请你排队!?
handle是主线程 ,Thread是从线程。控件数据更改只能在主线程 里,所以要用handle

更新UI只能在主线程里进行,否则会报错。但有时我们在子线程里进行操作需要更新UI,handler就登场了,它可以把子线程的数据传给主线程,让主线程同步操作来更新UI。

先来看这样一个例子

package com.hua;

import android.app.Activity;
import android.os.Bundle;

public class UpdateUInum1Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new Runnable() {
@Override
public void run() {
setTitle("fengyi.hua");
}
});
}
}

上面就是为了实现用一个Thread来更新Title,可以实现这个功能,刷新UI界面。但是这样是不对的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

有些人觉得这个方法确实也多余,为什么呢,因为既然是刷新一次,我完全可以在主线程中执行刷新Title的操作的,为什么还要开启线程。这是因为可能涉及到延时或者其它。比如说等待1min后再进行刷新操作,这个时间段要保证主UI线程是可操作的,所以要用到Thread来更新。但是这确实对于android的单线程模型有冲突,不建议使用。使用错误的例子如下:

package com.hua;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class UpdateTitleActivity extends Activity {

private void updateTitle() {
Date date = new Date();
int hour, minute, second;
String shour, sminute, ssecond;
hour = (date.getHours() + 8) % 24;
minute = date.getMinutes();
second = date.getSeconds();

if (hour < 10) {
shour = "0" + hour;
} else {
shour = "" + hour;
}

if (minute < 10) {
sminute = "0" + minute;
} else {
sminute = "" + minute;
}

if (second < 10) {
ssecond = "0" + second;
} else {
ssecond = "" + second;
}

setTitle("当前时间:" + shour + ":" + sminute + ":" + ssecond);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new myTask(), 1, 1000);//这里是利用Timer,跟使用Thread是一个效果。表现

//的效果就是多次更新Title,看会不会出问题。
}

private class myTask extends TimerTask {
@Override
public void run() {
updateTitle();
}

}

}

上面的代码用来每1s刷新一次Title,用来显示当前时间。但是由于android是单线程模型,存在线程安全问题,所以当第二次刷新的时候,出现错误。

正确的做法

上面所述两种方法,分别是Thread方法,和TimerTask方法。在Java中是常用的,因为线程安全。但是在单线程模型的android中,是不能用的。正确的方法有2个。

1.Thread+handler

2.TimerTask+handler

3.Runnable+Handler.postDelayed(runnable,time)

例子:Timertask+handler

package com.hua;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class UpdateTitleActivity extends Activity {

private Handler mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
updateTitle();
break;
default:
break;
}
}
};

private void updateTitle() {
Date date = new Date();
int hour, minute, second;
String shour, sminute, ssecond;
hour = (date.getHours() + 8) % 24;
minute = date.getMinutes();
second = date.getSeconds();

if (hour < 10) {
shour = "0" + hour;
} else {
shour = "" + hour;
}

if (minute < 10) {
sminute = "0" + minute;
} else {
sminute = "" + minute;
}

if (second < 10) {
ssecond = "0" + second;
} else {
ssecond = "" + second;
}

setTitle("当前时间:" + shour + ":" + sminute + ":" + ssecond);
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new myTask(), 1, 10000);
}

private class myTask extends TimerTask {
@Override
public void run() {
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
}

}

}

记住,处理都是在handleMessage里面,当然也可以不在,可以在handler的内类Callback的方法handleMessage里面。Handler跟其Callback也是学问,可以以后讲。

为了解决在Android非UI线程更新UI这个问题,Android提供了一些方法,从其他线程访问UI线程。

  1. Activity.runOnUiThread(Runnable)
  2. View.post(Runnable)
  3. View.postDelayed(Runnable, long)
  4. Looper的方式。
  5. 使用Handler的方式。
// 1. 使用runOnUiThread的方式
runOnUiThread(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
    }
});
// 2. 使用post的方式
btn.post(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
    }
});
// 3. 使用postDelayed的方式
btn.postDelayed(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
    }
}, 1000);
// 4. 使用Looper的方式
Looper.prepare();
Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
Looper.loop();
// 5. 使用Handler的方式
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
                break;
        }
    }
};
// 发送消息
handler.sendEmptyMessage(1);
或者
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show()

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java中各种集合(字符串类)的线程安全性!!!Thread.setDaemon设置说明下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)

C/S系统实现两数求和 任务要求: 实现配置文件 实现日志滚动 设置非阻塞套接字,EPOLL实现 检测客户端的连接,设置心跳检测 主线程 + 心跳检测线程 + EPOLL的ET模式处理事务线程 注意事项:设置volatile类型跳出死循环 作品简介: 本次实验分为bin,config,include,log,src五个文件夹以及一个makefile...

Object 中的wait和Thread中sleep的区别

一、区别 1、wait()来自于Object类而sleep来自于Thread类 2、sleep没有释放锁,但是wait释放了锁(使得其他线程可以使用同步控制块或者方法锁) 3、wait,notify和notifyAll只能在同步控制方法或者同步控制块使用,而sleep能在各个地方使用 4、sleep必须捕获异常,但是其它wait不用 5、sleep让一个线...

【C/C++多线程编程之五】pthread线程深入理解

多线程编程之pthread线程深入理解       Pthread是 POSIX threads 的简称,是POSIX的线程标准。           前几篇博客已经能给你初步的多线程概念。在进一步学习线程同步等多线程核心知识之前,须要对多线程深入的理解。非常多人忽略或者回避这部分内容,直接的问题是学习者无法把握多线程编程的内在原理,理解的层次太浅。...

JVM 详解

1 jdk  和jre 的区别     jre 石 Java 运行环境,只能运行 class 不能编辑 Java文件,不能dubug。  2  jdk下面的  bin/jconsole.exe 监控 一些内存,线程,jvm 。 3 Java 的 层级 ,以前我们关注的是三面 三次。jvm 是最下面一层    4 Java的  作者是  詹姆斯·高斯林   ...

Java并发(思维导图)

1,线程状态转换 无限期等待: 限期等待: 线程生命流程:    2,实现方式    代码实现样例【三种方式】: package com.cnblogs.mufasa.demo2; import java.util.concurrent.Callable; public class test1_Runnable implements Run...

HashMap之原理及死锁

一、HashMap原理 1.HashMap的本质就是数组和链表。table是一个entry数组,每一个数组元素保存一个Entry节点,而Entry节点内部又连接着同样key的下一个Entry节点,就构成了链表。. 详情见 HashMap源码分析 2.HashMap死锁原因: HashMap会造成死锁,因为HashMap是线程非安全的,多并发的情况...