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操作会等待它完成才能执行

  

最新文章

  1. 2.MongoDB 基于node.js访问和操作集合
  2. 5-Zend Studio配置
  3. Java连接池详解
  4. josephus Problem 中级(使用数组模拟链表,提升效率)
  5. boost 源码编译 的 Makefile.am写法备份
  6. EF,ADO.NET Entity Data Model简要的笔记
  7. [Java] JAVA程序员您需要学习的25个标准
  8. ubuntu14.04 安装matlab r2013a
  9. 分享一个Web弹框类
  10. HW4.20
  11. thinkphp如何写find_in_set这样的orm查询封装
  12. 翻译Android USB HOST API
  13. PL/SQL developer export/import (转)
  14. DFU工作过程中USB机制
  15. BZOJ 1610: [Usaco2008 Feb]Line连线游戏
  16. LeetCode题目总结(三)
  17. $A,B$ 实对称 $\ra\tr((AB)^2)\leq \tr(A^2B^2)$
  18. appium元素获取
  19. IdentityServer4 错误解决方案
  20. jq版本的checkbox有radio的单选效果(可得到value值)

热门文章

  1. 二&#183;、spring成长之路——委派设计模式和单例设计模式
  2. PHP实现验证码功能
  3. PHP保存数组到数据库
  4. thinkphp3.2+cropper上传多张图片剪切图片
  5. 树莓派3B+学习笔记:5、安装vim
  6. opencv移植(一)cmake安装
  7. Python学习 :常用模块(三)----- 日志记录
  8. RandomAccessFile java
  9. MFC中两个对话框之间数据传递
  10. 2017-2018-1 20155321《信息安全技术》实验二——Windows口令破解