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

第一种:直接创建子线程并启动
      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();
}
});

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

  1. BeginInvoke 方法真的是新开一个线程进行异步调用吗?

    转自原文BeginInvoke 方法真的是新开一个线程进行异步调用吗? BeginInvoke 方法真的是新开一个线程进行异步调用吗? 参考以下代码: public delegate void tre ...

  2. java的服务是每收到一个请求就新开一个线程来处理吗?tomcat呢?

    首先,服务器的实现不止有这两种方式. 先谈谈题主说的这两种服务器模型: 1.收到一个请求就处理,这个时候就不能处理新的请求,这种为阻塞 这个是单线程模型,无法并发,一个请求没处理完服务器就会阻塞,不会 ...

  3. java方法中开启一个线程

    很多业务场景下需要你在一个方法中去开启一个线程,去跑一些处理时间较长的代码,这样调用方就不必经过长时间的等待了.好了 话不多说  先上代码: package test; public class Th ...

  4. C#新开一个线程取到数据,如何更新到主线程UI上面

       一:问题 之前有被面试官问过,在WinForm中,要去网络上获取数据,由于网络环境等原因,不能很快的完成,因此会发生进程阻塞,造成主进程假死的现象,需要怎么解决?    二:思路 因此,往往是新 ...

  5. java中最简单的方式新起一个线程

    启动一个线程在一个方法中启动一个线程,有两种方法第一种是让类实现Runable接口,这样的话编译器就会提示你实现里面的未实现的方法(就是run方法)第二种是,现在方法中new一个线程,然后直接调用他的 ...

  6. WinForm中新开一个线程操作 窗体上的控件(跨线程操作控件)

    最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录窗体的显示,开了一个线程去加载数据远程的数据,会报一个错误"线程间操作无效: 从不是 ...

  7. java多线程之:创建开启一个线程的开销

    ---->关于时间,创建线程使用是直接向系统申请资源的,这里调用系统函数进行分配资源的话耗时不好说.---->关于资源,Java线程的线程栈所占用的内存是在Java堆外的,所以是不受jav ...

  8. Asp.NET开启一个线程,不停的执行

    using System;using System.Threading;using System.Threading.Tasks; class StartNewDemo{    static void ...

  9. 【Android Developers Training】 21. 创建一个可变动的UI

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

随机推荐

  1. python3.5如何安装statsmodels包?

    如题: 系统win10,64 python3.5 32 使用pip install statsmodels 会报错,说cpython没有安装.一直找不到解决办法,就只好去第三方库下载了一个 http: ...

  2. Linux中怎么升级PHP

    推荐yum源安装: #查看 删除老php版本的源 yum list installed | grep php yum remove php.x86_64 php-cli.x86_64 php-comm ...

  3. PAT 乙级 1019.数字黑洞 C++/Java

    题目来源 给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字.一直重复这样做,我们很快会停在有 ...

  4. 2019年杭电多校第一场 1004题Vacation(HDU6581+数学)

    题目链接 传送门 题意 有\(n+1\)辆车要过红绿灯,告诉你车的长度.与红绿灯的起点(题目假设红绿灯始终为绿).车的最大速度,问你第\(0\)辆车(距离最远)车头到达红绿灯起点的时间是多少(每辆车最 ...

  5. 2019牛客暑期多校训练营(第八场)E:Explorer(LCT裸题 也可用线段树模拟并查集维护连通性)

    题意:给定N,M,然后给出M组信息(u,v,l,r),表示u到v有[l,r]范围的通行证有效.问有多少种通行证可以使得1和N连通. 思路:和bzoj魔法森林有点像,LCT维护最小生成树.  开始和队友 ...

  6. What is react-native link?

    What is react-native link? or Should you just use react-native link when linking any dependency or s ...

  7. A Funny Game——打表&&找规律

    题目 n枚硬币排成一个圈.Alice和Bob轮流从中取一枚或两枚硬币.不过,取两枚时,所取的两枚硬币必须是连续的.硬币取走之后留下空格,相隔空格的硬币视为不连续.Alice开始先取,取走最后一枚硬币的 ...

  8. python-随机生成验证码实例

    需求:随机生成验证码, 思路: 1.生成一个随机数,65-90 2.数字转化为字母:chr(数字) #!/usr/bin/env python # -*- coding:utf-8 -*- impor ...

  9. [Unit test] jasmine createSpyObj

    beforeEach(() => { contextStub = { debug: false, engine: jasmine.createSpyObj('engine', [ 'create ...

  10. luogu_2831: 愤怒的小鸟

    洛谷2831:愤怒的小鸟(状压\(dp\)) 题意: 在二维平面上给定\(n\)个点\((1\leq n\leq18)\). 其中每个点用\((x_i,y_i)\)表示\((0<x_i,y_i& ...