android中的一些耗时操作,例如网络请求,如果不能及时响应,就会导致主线程被阻塞,出现ANR,非常影响用户体验,所以一些耗时的操作,我们会想办法放在子线程中去完成。

  android的UI操作并不是线程安全的,所以多个线程并发操作UI组件的时候,则可能导致线程安全问题。为了解决这个问题,android只允许UI线程修改UI组件。

  

public class MainActivity extends AppCompatActivity
{
TextView textView;
Button changeText;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
changeText = (Button)findViewById(R.id.btn);
textView = (TextView)findViewById(R.id.textView);
changeText.setOnClickListener(this);
} public void change(View view)
{
switch (view.getId())
{
case R.id.btn:
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("被改变了");
}
}).start();
break;
default:
break;
}
}
}

  在xml中定义了一个按钮和一个TextView,当点击按钮的时候,开启子线程去更改TextView中的文字,但是在编译的时候是无法通过的,因为不允许在子线程中直接对UI线程中组件进行操作。

  需要借用Handler来实现子线程更新UI组件的功能。

public class MainActivity extends AppCompatActivity
{ private TextView textView;
private Handler handler = new Handler()
{
//接收的是消息队列中的msg
public void handleMessage(Message msg)
{
switch (msg.what)
{
case 0x0001:
int index = msg.arg1;
textView.setText(index + "");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.textView1);
} public void changeNumber(View view)
{
switch (view.getId())
{
case R.id.btn:
new Thread()
{
public void run()
{
for(int i = 0; i < 10; i++)
{
Message msg = new Message();
//msg.what是必不可少的,需要用来做判定
msg.what = 0x0001;
msg.arg1 = i;
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
break;
}
}
}

  

Handler中的组件

Message:Handler接收和处理的消息对象。

Looper:每个线程只能拥有一个Looper,它的loop方法负责读取MessageQueue中的消息,读到消息之后将消息交给发送该消息的Handler进行处理。

private Looper()
{
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}

  从Looper的构造器的源码中可以看到,初始化Looper的时候会创建一个与之关联的MessageQueue。

MessageQueue:消息队列,采用先进先出的方式来管理Message。程序创建Looper对象的时候,会在它的构造器中创建MessageQueue对象。

  android采用的是MessageQueue机制保证线程间通信。

  MessageQueue是一个消息队列,用来存放通过Handler发布的消息,Android在第一次启动程序的时候会默认为UI线程创建一个关联的消息队列,用来管理程序的组件,如Activity、BroadcastReceiver、Service等。

Handler:它的作用是发送消息和处理消息,程序使用Handler发送消息的时候,发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler能够正常工作,当前线程必须有一个MessageQueue,否则消息就没有保存。不过由于MessageQueue是由Looper管理的,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象,为了保证当前线程中有Looper对象,分为两种情况。

  1.在UI线程中,系统初始化了Looper对象,只需要手动创建Handler即可,然后可以进行消息的发送和接收。

  2.在子线程中,必须自己创建一个Looper对象,并启动它。创建的时候,调用prepare()方法即可。

public static final void prepare()
{
if(sThreadLocal.get() != null)
{
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

  上面是prepare()方法的源码。

  有了Looper()之后,需要调用loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对于的Handler进行处理。

Handler消息传递机制

  工作线程通过handler对象和主线程进行通信

  Handler对象所有工作都在主线程中执行

  Handler类需要实现handleMessage()方法,来处理消息队列中取出的Message对象

  handleMessage()方法由主线程调用,可以在需要的时候更新UI线程,但是必须确保此方法快速完成,因为其他的UI操作会等待它完成才能执行

  

android入门 — 多线程(一)的更多相关文章

  1. linux.linuxidc.com - /2011年资料/Android入门教程/

    本文转自 http://itindex.net/detail/15843-linux.linuxidc.com-%E8%B5%84%E6%96%99-android Shared by Yuan 用户 ...

  2. Android入门(十二)SQLite事务、升级数据库

    原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...

  3. 【转】Xamarin.Android 入门之:Xamarin+vs2015 环境搭建

    Xamarin.Android 入门之:Xamarin+vs2015 环境搭建   一.前言 此篇博客主要写了如何使用搭建xamarin开发的环境,防止我自己万一哪天电脑重装系统了,可以直接看这篇博客 ...

  4. android 入门 006(sqlite增删改查)

    android 入门 006(sqlite增删改查) package cn.rfvip.feb_14_2_sqlite; import android.content.Context; import ...

  5. android 入门 005(登录记住)

    android 入门 005(登录记住) <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android ...

  6. Android入门:绑定本地服务

    一.绑定服务介绍   前面文章中讲过一般的通过startService开启的服务,当访问者关闭时,服务仍然存在: 但是如果存在这样一种情况:访问者需要与服务进行通信,则我们需要将访问者与服务进行绑定: ...

  7. Android入门视频推荐

      marschen老师的Android入门视频推荐网址: 1.Android应用程序开发视频教程(重制版)第一季 2.Android应用开发视频教程(重制版)第二季 2.marschen老师的个人微 ...

  8. Android入门教程之我见

    真正的从安卓入门学习到实际工作也差不多一年时间了,也做了几个项目.在这期间经历了一开始学习Android的基本知识后仍旧无从下手,不知道如何开始开发一个app,到现在也开始学会注意Android架构的 ...

  9. 小猪的Android入门之路 Day 3 - part 3

    小猪的Android入门之路 Day 3 - part 3 各种UI组件的学习 Part 3 本节引言: 在前面两个部分中我们对Android中一些比較经常使用的基本组件进行了一个了解, part 1 ...

随机推荐

  1. 解决在 win10 下 vs2017 中创建 MFC 程序拖放文件接收不到 WM_DROPFILES 消息问题

    解决方案 这个问题是由于 win10 的安全机制搞的鬼,即使以管理员权限运行也不行,因为它会把 WM_DROPFILES 消息过滤掉,那怎么办呢?只需在窗口初始化 OnInitDialog() 里添加 ...

  2. 嵌入式nand flash详解

    一.s3c2440启动后会将nand flash的前4K程序复制到内部的sram中,这个过程是硬件自动完成的,但是如果我们的程序远远大于4K,这个时候就需要将程序从flash拷贝到内存中来运行了. 二 ...

  3. 深入了解Linux(一)

    Linux的各个文件夹 每次当我使用linux的时候我都被一个个文件夹整懵逼,那么多文件夹到底是怎么分类的呢.今天终于有时间好好整理一下 /boot: 引导文件存放目录,内核文件(vmlinuz),引 ...

  4. python财经数据接口包Tushare pro的入门及简单使用方式(大数据,股票数据接口)

    最近在做一个项目,需要用到股票的数据,我在网上查了很久,最终发现在股票数据上面还是tushare比较专业,而且对于将来做金融行业的大数据这一块的,tushare绝对是你的一个好帮手,所以下面我就简单介 ...

  5. 用k8s构建生产环境下应用服务

    1.生成镜像 见https://www.cnblogs.com/mushou/p/9713741.html,把测试成熟的应用添加到tomcat镜像生成新的镜像,用ansible部署到集群的几点服务器中 ...

  6. python写爬虫时的编码问题解决方案

    在使用Python写爬虫的时候,常常会遇到各种令人抓狂的编码错误问题.下面给出一些简单的解决编码错误问题的思路,希望对大家有所帮助. 首先,打开你要爬取的网站,右击查看源码,查看它指定的编码是什么,如 ...

  7. Go黑帽子

    使用go语言来实现python黑帽子和绝技的代码 1.unix密码破解器 package main import( "bufio" "flag" "i ...

  8. Ubuntu16.04安装TFTP服务,完成开发板下载文件

    1.安装TFTP服务 $ sudo apt-get install tftp-hpa tftpd-hpa 2.建立传递目录 $ mkdir tftp $ sudo chmod 777 tftp/ -R ...

  9. MFC中两个对话框之间数据传递

    以下是在网上参考的一篇文章,网址:https://blog.csdn.net/foreverhuylee/article/details/21707197 这里有两种情况, 第一种情况是: (在一个基 ...

  10. 20155227 2016-2017-2 《Java程序设计》第六周学习总结

    20155227 2016-2017-2 <Java程序设计>第六周学习总结 教材学习内容总结 InputStream与OutputStream 串流设计 流(Stream)是对「输入输出 ...