您的位置:首页 > 移动开发 > Android开发

深入了解android中的消息机制Handler

2016-04-03 19:05 483 查看
什么是Handler?

handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制.

我们可以使用它发送消息,也可以通过它处理消息.

我们为什么要使用Handler?

Android在设计的时候,就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制,就没有办法更新UI,而且还会抛出异常信息.

例如:大家都知道,更新UI的操作一般都是放在main线程中,当我们需要在子线程中更新UI时,就需要使用到了Handler,虽然在子线程更新Ui的方法有好几种,但内部实现原理基本都是通过Handler发送消息处理的,不要着急,下面会提到.

Handler的使用:

sendMessage()方法的使用:

package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
private TextView tv;
//创建main线程的Handler
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText("我是通过handler发送消息更新的");
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.textview);
//开启子线程
new Thread(){
@Override
public void run() {
super.run();
//例如此处我们正在执行一个耗时操作,执行完毕后发送消息更新ui

//发送一个空消息
handler.sendEmptyMessage(0);

//如果此处我们需要使用执行完耗时操作的数据,可以这样写
//Message msg = handler.obtainMessage();
//msg.obj = "数据";
//handler.sendMessage(msg);

}
}.start();

}
}


sendEmptyMessage(); 此方法和sendMessage使用一致,区别就是发送一个空消息.

sendMessageDelayed(); 发送一个延时执行的消息

post(Runnable);该方法可以在子线程中更新UI,该方法运行在main线程中

removeCallbacksAndMessages();移除回调和消息;例如:我们在使Handler轮播一些图片时,想让它停止轮播,就可以使用这个方法.

android为什么要设计只能通过handler来更新UI呢?

最根本的的目的就是解决多线程并发问题.假设如果在一个activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题呢? 更新界面错误.

你可能会说,我可以使用加锁的多线程啊,如果对更新UI的操作都进行加锁处理的话,应用程序的性能会大大下降.

处于对以上问题的考虑,Android给我们提供了一套更新UI的机制,我们只需要遵循这样的机制就可以了.

根本不用关心多线程的问题,所以更新UI的操作,都是在主线程的消息队列当中去轮询处理的.

Handler的原理是什么呢?

一,Handler封装了消息的发送,(主要包括消息发送给谁)

Looper(Handler内部自己的Looper)

1,内部包含一个消息队列,也就是MessageQueue,所有的Handler发送消息

都走向这个消息队列.

2,Looper.loop()方法,就是一个死循环,不断的从MessageQueue中取消息,

如果有消息就处理消息,没有消息就阻塞.

二,MessageQueue,就是一个消息队列,可以添加消息,并处理消息.

三,Handler,内部会跟Lopper进行关联,也就是说在Handler的

内部可以找到Looper,找到了Lopper也就找到 了MessageQueue,

在Handler中发送消息,其实就是向MessageQueue队列中发送消息.

Handler原理总结:

Handler负责发送消息,Loooper负责接收Handler发送的消息,

并直接把消息回传给Handler自己.

MessageQueue就是一个存储消息的容器.

Handler使用中遇到的问题:

在非UI线程中更新UI,抛出的异常:



在子线程创建Handler,抛出的异常:



注意:当需要在子线程中创建Handler时,需要先创建一个Looper,因为子线程中没有Looper对象

HandlerThread又是什么?

当我们向创建一个与线程相关的Handler时,我们可以使用HandlerThread,来解决多线程的并发问题.



子线程与主线程如何互发消息:

主线程Handler向子线程发送消息(伪代码)



子线程Handler向主线程发送消息(伪代码)



Android在子线程中更新UI的几种方式:

使用图片吧,以前做的笔记,看着感觉更加清晰..



非UI线程真的不能更新UI吗?

答案是能,对.你没有看错,非UI线程也能更新UI.可能你会觉得我在扯淡,下面看一段代码:

package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
private TextView tv;
//创建main线程的Handler
private Handler handler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.textview);
//开启子线程
new Thread(){
@Override
public void run() {
super.run();
//SystemClock.sleep(100);
tv.setText("我是在子线程中更新的UI");
}
}.start();

}
}


我把布局代码也贴出来,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="呵呵"
android:textSize="40sp"
/>
</LinearLayout>


执行效果图:



可能看到这,你已经目瞪口呆了,这怎么可能,fuck,这完全颠覆了啊…..

主要原因:

当我们在更新UI时,Android中的ViewRootImpl类中的checkThread()方法会检查当前更新UI所在的线程,如图

3937    void More ...checkThread() {
//检查执行更新UI所在的线程
3938        if (mThread != Thread.currentThread()) {
//如果不在UI线程,就会抛出下面的异常,大家应该很眼熟吧
3939            throw new CalledFromWrongThreadException(
3940                    "Only the original thread that                 created a view hierarchy can touch its views.");
3941        }
3942    }


查看系统源码后,你会发现,ViewRootImpl类会在 Activity的onResume()方法执行完成后才初始化,这也就解释了上面代码能运行的原因了,但是,你发现没有,我们在子线程中没有做任何的耗时操作,如果我在子线程中添加这句代码:

SystemClock.sleep(100);


那么系统将会抛出异常:”Only the original thread that created a view hierarchy can touch its views.”

不能在非UI线程中更新UI.

那么问题来了,如果ViewRootImpl类没有初始化完成,那么view视图是如何显示出来的呢???我也正在解决中…….

另外,当我们在子线程中获取到ViewRoot,我们可以调用addView()方法在子线程中更新UI,这其中的详情就靠大家去探索了…..
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: