Android Handler简介及其用法

Handler简介及其用法

       前言

          今天来给大家分享下我学习Handler的一些笔记吧,为什么会用到Handler这个东西呢?出于对性能优化的考虑,android的UI操作不是线程安全的,也就是说只有主线程才能直接对UI进行操作,但是我们在开发的过程中必然要用到多线程处理来改变UI上的某些操作,这个时候就需要用到Handler的消息处理机制了。

        Handler类主要用处

        1.在新启动的线程中发送消息。

        2.在主线程中获取并处理消息。


        Handler的运行机制

        在启动了一个应用后,会有一个主线程,也称作UI线程,只有这个线程才能改变UI界面,伴随着这个线程产生的还有一个Looper,一个Looper可以和多个Handler绑定,一个UI线程就对应着它自己的Looper,这个Looper对象会一直从主线程的MessageQueue(消息队列)中取出消息并传递给Handler进行处理。因此我们在主线程中可以直接创建Handler然后使用即可。一个线程可以拥有多个Handler.

        若想在子线程中控制UI界面,我们需要做的就是将主线程的Handler传入,然后向主线程的Handler发送消息,然后在主线程中的handleMessage()中去处理消息。


             由于Handler是需要和线程绑定在一起的,因此在初始化Handler的时候需要注意了一般有两种方法创建:


         1.给Handler指定Looper对象,那么这个Handler便绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

         2.不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行.

         private class MyThread extends Thread {	
    		public void run() {	
    			Looper.prepare();
    			private Handler myHandler = new Handler() {	
    				public void handleMessage(Message msg) {	
    					...
    				}
                        Looper.loop();
    			}
    		}
    	}

    这样定义了之后,如果在主线程中new MyThread(), MyThread中的Handler就会和主线程中的looper绑定在一起了,理所当然的handlerMessage()方法也会在主线程中执行,再来控制UI界面就没问题了。

        下面有个小例子,用定时任务创建的线程定时任务(其中有子线程)向主线程的Handler发送消息,主线程来进行处理。

public class MainActivity extends Activity {


    int[] imageIds = new int[]{
        R.drawable.ic_launcher,R.drawable.e1,R.drawable.e2,
            R.drawable.e3,R.drawable.e4,R.drawable.e5,
            R.drawable.e6
    };
    int currentImageId = 0;

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

        final ImageView imageView = (ImageView)findViewById(R.id.imageview);
        final Handler myHandler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                //接受消息并更换图片
                if (msg.what == 0x1233) {
                    imageView.setBackgroundResource(imageIds[currentImageId++ % imageIds.length]);
                }
            }
        };

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                //向主线程中Handler实例发送消息
                myHandler.sendEmptyMessage(0x1233);
            }
        },0,1200);

    }
}

        Handler及Loop、MessageQueue之间的合作关系

       上面简单的介绍了Handler及其用法之后,下面来介绍下和Handler一起工作的几个组件:

        1.Message: Handler接受和处理的消息对象。

        2.Looper: 每个线程中只能拥有一个Looper,但是一个Looper可以和多个线程的Handler绑定起来,也就是说很多个线程可以往一个Looper所持有的MessageQueue中发送消息。这就给我们提供了线程之间通信的可能。

        3.MessageQueue : 消息队列,采用着先进先出的管理方式,管理Message。

        

        为什么说Looper持有MessageQueue呢?让我们来看看内部的实现:

<span style="font-size:14px;">private Looper () {
	mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
}</span>

        在初始化Looper的时候,里面会关联着一个MessageQueue实例,这个Messagequeue也就是在线程发送的消息的容器。仔细观察上面的代码,发现Looper是private的,这就说明了我们不能用构造方法来创建Looper对象,那该怎么弄呢?由于和线程扯上了关系,我们不得部分下面两种情况来考虑了:

        1.在主线程(UI线程)中,系统在一个应用开始的时候就已经帮我们初始化好了属于主线程的Looper,我们直接可以在主线程中实例化一个Handler对象,使用即可:

<span style="font-size:14px;">Handler myHandler = new Handler(){

            @Override
            public void handleMessage(Message msg) {

                if (msg.what == 0x1233) {
                    System.out.println("hello world~~~");
                }
            }
        };</span>

        2.第二种情况就是在我们自己启动的子线程中了,自己创建的东西由于不是亲生的(系统自动生成并启动的),所以你懂的,只能靠自己来弄Looper对象了,好在Looper类留了一个静态的方法prepare()供我们使用,可以调用这个方法来创建Looper对象:

<span style="font-size:14px;">public static final void prepare() {
	if (sThreadLocal.get() != null) {
		throw new RuntimeException("Only one Looper may be created per thread");
	}	
	sThreadLocal.set(new Looper());
}</span>

            那么还没完呢,有了这个Looper对象之后,怎么让它不断地从自己的MessageQueue中取出消息并传递给绑定了的Handler上面呢?Looper中还有一个死循环的函数,loop(),一旦开始,就会不停的从MessageQueue中取出消息并交给绑定了的Handler处理。网上找了找,下面贴上实现的代码:

 /**
     *  Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static final void loop() {
        Looper me = myLooper();  // 线程中存储的Looper对象
        MessageQueue queue = me.mQueue;  // Looper类中的消息队列
        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);  // 利用 Target 注册的方法处理消息
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle(); 
            }
        }
    }


             到这里差不多基础的东西就介绍完了,其是我知道的也就这么多了,,全部都呈上来了尴尬,下面总结下关于Handler的一些说法:

           1.一个线程只能有一个looper,或者没有looper,这个looper可以是自己通过Looper.prepare()创建出来的,也可以是通过Looper.getMainLooper()得到的.

           2.一个Handler只能和一个Looper绑定起来,但是一个Looper可以被多个Handler来绑定,共同使用里面的消息队列.并不会出现冲突的情况,谁发的消息,到时候就该谁拿走.并且处理掉.

           3.一个Thread能够有多个Handler,这些Handler如果在创建的时候没有指定Looper,那么就是用的该Thread的Looper,如果指定了Looper,发送的消息将送到指定的looper里面.

           4.Handler它用的Looper之间没有任何的瓜葛,只是借用了一下Looper而已,在用mHandler.sendEmptyMessage()的时候,发送到哪个Looper的MessageQueue,到时候就从那个MessageQueue中取出.

           5.子线程中想控制UI的话,如果子线程和主线程在同一个文件下(一般都是这样子),直接对主线程的Handler发送消息,如果不在同一个文件夹下,那就将主线程的Handler作为参数传入.好像没有其它的办法,因为即使用同一个Looper,也不能处理其他Handler发送过来的消息,所以在子线程中用Handler发送消息,主线程上的Handler是无法处理的.关于这方面的知识,我还会整理之后和大家分享的,其实在handler向Looper发送消息的时候,传过去了一个target,用来指名这条消息归哪个handler处理的.


           今天的博客就到这里咯,如果有什么不对的地方还望各位指出,今天捋Handler的知识把自己都弄晕了......23333


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值