一、Handler类的基本介绍以及使用:
Android中UI操作是线程不安全的操作,如果有多个线程并发操作UI组件,就会出现线程安全问题,所以Android中制定了一个规则:在Android中只允许主线程(UI线程)修改Activity中的UI组件,但是现在问题又来了,在开发中我们会需要在子线程中更新UI组件的情况,那怎么进行处理呢?其实Handler就是为了解决这种问题而生的。
Handler类的主要作用有两个:
1.在新启动的线程中发送消息
2.在主线程中获取,处理消息
Handler类中用于发送、处理消息的方法:
1. void handleMessage(Message msg):处理消息的方法。该方法通常用于被重写
2. boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值得消息
3. boolean hasMeesages(int what,Object obj):检查消息队列中时候包含what属性为指定值且object属性为指定对象的消息
4. 多个重载的Message obtainMessage():获取消息
5. sendEmptyMessage(int what):发送空消息
6. boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后发送空消息
7. boolean sendMessage(Message msg):立即发送消息
8. boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后发送消息
下面是一个使用Handler类更新UI组件的一个使用小例子:
- package com.my.Mytimetest;
-
- import java.util.Timer;
- import java.util.TimerTask;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.widget.ImageView;
-
- public class MainActivity extends Activity {
- private ImageView imageview;
-
- private int[] images = {R.drawable.image1,R.drawable.image2,R.drawable.image3,R.drawable.image4};
-
- private int index;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- imageview = (ImageView) findViewById(R.id.id_images);
-
- final Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- if(msg.arg1==123){
- index++;
- imageview.setImageResource(images[index%4]);
- }
- }
- };
-
- //定义一个计时器,设置为延迟0ms后执行,每隔1s执行一次(这里如果设置为 timer.schedule(task,1000)表示执行一次)
- new Timer().schedule(new TimerTask(){
- @Override
- public void run() {
- Message message = new Message();
- message.arg1 = 123;
- mHandler.sendMessage(message);
- }
- }, 0, 1000);
-
-
- }
- }
效果如图:
上边的案例,就是简单的用到了Handler类来实现一个图片轮播的效果。
二、Handler的实现原理:
与Handler一起工作的几个组件:
1.Message:Handler接收和处理的消息对象
2.Looper:每个线程只能拥有一个Looper.它的loop方法负责读取MessageQueue中的消息,
读到信息之后就把消息交给发送该消息的Handler进行处理。
3.MessageQueue:消息队列,它采用先进先出的方式来管理Message.
程序创建Looper对象时会在他的构造器中创建Looper对象。
Looper提供的构造器源代码如下:
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- mThread = Thread.currentThread();
- }
从源代码中可以看出:
(1). 构造方法是私有的,所以不允许我们自己创建Looper对象
(2). 程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。
4.Handler:
简而言之,Handler的作用:Handler的构造方法,其实就是在Handler中持有一个指向该Looper.mQueue对象,
当handler调用sendMessage方法时,其实就是往该mQueue中去插入一个message,然后Looper.loop()就会取出执行。
作用有两个-----发送消息和处理消息,程序中使用Handler发送消息,
被Handler发送的消息必须被送到指定的MessageQueue。
也就是说,如果希望Handler正常工作,必须在当前的线程中有一个MessageQueue,
否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,
也就是说,如果希望Handler正常工作,必须在当前的线程中有一个Looper对象,
为了当前线程中有Looper对象,可以分为两种情况处理:
(1).主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,
然后就可以通过Handler来发送消息,处理消息了。
(2).在自己启动的子线程中,必须自己创建一个Looper对象,并启动它。
------创建Looper对象调用它的prepare()方法即可。
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对象。
然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断的取出MessageQueue中的消息,
并将取出的消息分给该消息对应的Handler进行处理。
Looper类中loop()方法的源码:
- public static final void loop() {
- Looper me = myLooper();
- MessageQueue queue = me.mQueue;
- while (true) {
- Message msg = queue.next(); // might block
- //if (!me.mRun) {
- // break;
- //}
- if (msg != null) {
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
- msg.recycle();
- }
- }
- }
Looper,MessageQueue,Handler各自的作用如下:
Looper:每个线程只用一个Looper,负责管理MessageQueue,会不断的从MessageQueue中取出消息,
并将消息分给对应的Handler处理。
MessageQueue:由Looper负责管理,它采用先进先出的方式管理Message。
Handler: 它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
简而言之:
Handler负责发送消息,
Looper负责接收Handler发送的消息并直接把消息回传给Handler自己,
MessageQueue就是一个存储消息的容器。
三、在线程中使用Handler的步骤如下:
1. 调用Looper的prepare()方法为当前的线程创建Looper对象,
创建Looper对象时,Looper的构造方法中会创建与之配套的MessageQueue.
2. 有了Looper对象之后,创建Handler子类的实例,重写handleMessage()方法,
该方法负责处理来自于其他线程的消息。
3. 调用Looper的loop()方法启动Looper。
下面对比一下在主线程中和在子线程中使用Handler的区别:
在主线程中使用Handler计算一定范围之内的质数的例子:
在子线程中使用Handler计算一定范围之内的质数的例子:
- package com.my.Mytimetest;
-
-
- import java.util.ArrayList;
- import java.util.List;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Looper;
- import android.os.Message;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.TextView;
- import android.widget.Toast;
-
- public class MainActivity extends Activity implements OnClickListener {
- private EditText edt;
- private Button stop_btn;
- private MyThread myThread;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- initView();
- setListener();
- myThread = new MyThread();
- //开启子线程
- myThread.start();
-
-
-
- }
- private void setListener() {
- stop_btn.setOnClickListener(this);
-
- }
- private void initView() {
- edt = (EditText) findViewById(R.id.id_edt);
- stop_btn = (Button) findViewById(R.id.id_start);
- }
- @Override
- public void onClick(View v) {
- int num = Integer.parseInt(edt.getText().toString());
- Message message = new Message();
- message.what = 123;
- Bundle bundle = new Bundle();
- bundle.putInt("num", num);
- message.setData(bundle);
- //将消息发送给子线程中的Handler来处理
- myThread.mHandler.sendMessage(message);
-
- }
-
- class MyThread extends Thread{
-
- public Handler mHandler;
- private List<Integer> nums = new ArrayList<Integer>();
- @Override
- public void run() {
- Looper.prepare();
- mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- if(msg.what==123){
- int num = msg.getData().getInt("num");
- outer:
- for (int i = 2; i < num; i++) {
- for (int j = 2; j < Math.sqrt(i); j++) {
- if(i!=2&&i%j==0){
- continue outer;
- }
- }
- nums.add(i);
-
- }
- Toast.makeText(MainActivity.this, nums.toString(), Toast.LENGTH_LONG).show();
- nums.removeAll(nums);
- }
- }
-
-
- };
- Looper.loop();
- }
-
-
- }
-
- }
效果图:
四、HandlerThread类的使用:
HandlerThread继承自Thread,当线程开启时,也就是它run方法运行起来后,
线程同时创建了一个含有消息队列的Looper,并对外提供自己这个Looper对象的get方法,这就是它和普通Thread唯一不同的地方。
为什么要使用HandlerThread。
1.开发中如果多次使用类似new Thread(){...}.start()
这种方式开启一个子线程,会创建多个匿名线程,使得程序运行起来越来越慢,
而HandlerThread自带Looper使他可以通过消息来多次重复使用当前线程,节省开支;
2.android系统提供的Handler类内部的Looper默认绑定的是UI线程的消息队列,
对于非UI线程又想使用消息机制,那么HandlerThread内部的Looper是最合适的,它不会干扰或阻塞UI线程。
HandlerThread既然本质是Thread,为何前面加了一个Handler?
android中Handler类本质上就是从它内部的Looper中不断取消息,
然后触发它内部的Callback接口的handleMessage方法,让用户去实现对消息的具体处理。
而HandlerThread本身自带Looper,只要它实现了Callback接口,
那么HandlerThread也可以在自己线程内处理自己线程发出的消息,
充分实现非UI线程中较低开支下的消息处理。
HandlerThread类的使用:
效果图:
可以看出,打印的是子线程的信息。HandlerThread中加了同步锁,保证了线程的安全。
HandlerThread类的run()方法的源码:
- public void run() {
- mTid = Process.myTid();
- Looper.prepare();
- synchronized (this) {
- mLooper = Looper.myLooper();
- notifyAll();
- }
- Process.setThreadPriority(mPriority);
- onLooperPrepared();
- Looper.loop();
- mTid = -1;
- }
HandlerThread类中的getLooper()方法的源码:
- public Looper getLooper() {
- if (!isAlive()) {
- return null;
- }
-
- // If the thread has been started, wait until the looper has been created.
- synchronized (this) {
- while (isAlive() && mLooper == null) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
- }
- return mLooper;
- }
五、下面介绍一下主线程与子线程之间通过Handler传递消息的方法
六、Android中更新UI的几种方式
1.runOnUiThread
2.handler post
3.handler sendMessage
4.view post
代码:
效果图:
实际上,上边的这四种更新UI的方式都是使用到了handler机制来更新UI的,只是内部一些代码的处理不一样,看一下这四种方式的源码,我们就知道了。
源码对比:
handler post方式:
runOnUiThread方式:
view post方式:
其实都是通过handler机制来实现的。
七、最后,一些常见的异常的产生原因
1.Can't create handler inside thread that has not called Looper.prepare();
产生的原因:
是因为在子线程中创建Handler对象的时候没为创建的Handler指定Looper,
源码解析:
2.android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
产生的原因:是因为在非UI线程中更新UI
由于本人初写博客,写的不好的地方还请大家能批评指正,希望能和大家相互学习、相互交流、共同成长。