Handler原理解析

首先要理解 ThreadLocal 这个类,这个类的具体用法可以参考Java API文档或其它人的博客,在这里只对ThreadLocal做一个简单的说明.

ThreadLocal 的作用就是一个 Map<Thread,Ojbect>, 也就是说 ThreadLocal 中存放的是和一个线程相关的某一个对象,其实 ThreadLocal是为线程同步准备的,线程同步还有另

外一种方式就是使用synchronized关键字,但这种同步方法效率效低,而使用ThreadLocal则效率高点. 总之,了解到 ThreadLocal相当于一个Map<Thread,Object>,用于线程同步即可.

在 android.os.Looper中有下面一行:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
因为它是static的,这里我们把 sThreadLocal 看成是一个 全局变量.

一个Looper对象有哪些成员呢?

public class Looper {
    private static final String TAG = "Looper";
    //一个消息队列
    final MessageQueue mQueue;
}

Android文档里说, “一个Thread可以关联一个 MessageQueue”,关联示意图如下图所示:

一个线程通过 ThreadLocal 和一个 Looper 对应,即 Map<ThreadLocal,Looper>, 而一个Looper拥有一个 MessageQueue,所以一个 Thread就和一个 MessageQueue关联起来了.

一个 Thread 本来是没有和 MessageQueue关联的,关联的过程通过下面一个全局函数(因为是静态的,暂且当作是全局的)来实现:

//Looper.java 中的一个方法
public static void prepare() {
        //调用此方法的线程是否在 全局变量 sThreadLocal(即Map<Thread,Looper>)中存有一组以此线程为键的键值对
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //如果在Map<Thread,Looper>中没有存放当前线程对应的键值对当存入
        // 一个 set 操作,其实就是在 Map 中插入了一组 <Thread,Looper>值,即 <调用此方法的线程,new Looper()>
        // new Looper() 时,Looper的构造函数就会实例化一个 MessageQueue
        sThreadLocal.set(new Looper());
}

通过以上阐述,我们了解 存在一个全局的 ThreadLocal,它是一个Map<Thread,Looper>型的变量,一个线程在调用 prepare( )全局函数后都会在ThreadLocal中存在一组以当前线程为键的键值对,这个键值对所关键的值就是一个Looper对象,一个Looper对象有一个MessageQueue,这样一个线程就和一个MessageQueue关联起来.


一个 Handler 是什么东西?它的作用是什么?

一个Handler共有两个作用: 1 向某一线程的 MessageQueue 插入 Message   2 处理某一线程的 MessageQueue 取出的 Message

Handler的作用主要围绕 MessageQueue, 它即向 MessageQueue中插入信息,同时也处理 MessageQueue 弹出的 Message.

插入信息的方法如下:

处理信息的方法如下:


那么现在考虑下面一段非常常见的代码(为了完整性把无用的代码也贴出来了):

import android.app.Activity;
import android.graphics.drawable.ClipDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;

public class MainActivity extends Activity {

	public static final int WHAT = 1;
	private ClipDrawable mDrawable;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ImageView view = (ImageView)findViewById(R.id.view);
        mDrawable = (ClipDrawable)view.getDrawable();
        new ProgressThread(mHandler).start();
        
    }

    private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			if(msg.what == MainActivity.WHAT) {
				mDrawable.setLevel(mDrawable.getLevel() + 500);
			}
		}
    };
    
   private class ProgressThread extends Thread {
	   //这里暂不考虑线程同步
	   private Handler mHandler;
	   public ProgressThread(Handler handler) {
		   mHandler = handler;
	   }
	@Override
	public void run() {
		Message msg;
		for(int i = 0; i < 20; i++) {
			msg = mHandler.obtainMessage(MainActivity.WHAT);
			mHandler.sendMessage(msg);
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	   
   }
}
这段代码的常见之处就是:

private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			if(msg.what == MainActivity.WHAT) {
				mDrawable.setLevel(mDrawable.getLevel() + 500);
			}
		}
    };
我们把 Handler 当做一个 Activity 的成员变量,并对它进行实例化,那么实例化时发生了什么呢? 看一下 android.os.Handler的默认构造函数源码:

public Handler() {
        //获得当前线程在 ThreadLocal 中所对应的 Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

Handler的默认构造函数会得到当前线程的Looper,并通过这个Looper获得 MessageQueue, 获得MessageQueue以后就可以往里面 send 信息了.

也就是说,当你实例化一个 Handler时,这个 Handler就和当前这个线程的 MessageQueue 相关联,它能作用于这个 MessageQueue的两头(插入信息/处理弹出的信息).

当你把这个Handler的引用传给其它Thread时,那个Thread就可以通过 Handler 与Handler所关联的线程进行交互. 如下图:

Thread2 通过  mHandler.sendMessage( ) 向线程1 发出异步的 Message, Thread1 通过 handleMessage( ) 来处理来自于 Thread2 的 Message.

可以更直接的认为你建立的 Handler实例就代表一个线程,它是两个线程间通信的桥梁.


还有一些问题需要思考?

问题一:

一个Thread 必须要经过 Looper.prepare( ) 才能拥有自己的 MessageQueue, 而我们在Activity的类是定义一个Handler内部类来使用时,系统不会报错,也就是说Activity这个线程已经在某些地方调用过 Looper.prepare( ) 来生成自己的消息队列,这个太复杂了,没有搞懂,但能在Activity源码中发现下面几行代码:

public void recreate() {
        if (mParent != null) {
            throw new IllegalStateException("Can only be called on top-level activity");
        }
        if (Looper.myLooper() != mMainThread.getLooper()) {
            throw new IllegalStateException("Must be called from main thread");
        }
        mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
    }
具体的内部机制暂时搞不清了!


问题二:

一个线程的 MessageQueue 要经过 Looper.loop( )  才能把消息弹出,并调用 Message.target.dispatch(Message ) 方法, 在这里 loop( ) 方法内部有一个 while(true)的无限循环,如果这个方法是在当前线程执行的话,当前线程就会被卡死,如果不是在当前线程执行的话,那么由哪一个线程在执行呢? framework里是如何实现的呢?



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值