• Handler消息传递机制——Handler、Loop、MessageQueue的工作原理


        为了更好地理解Handler的工作原理,先介绍一下与Handler一起工作的几个组件。

    • Message:Handler接收和处理的消息对象。
    • Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读取到消息之后就把消息交给发送该消息的Handler进行处理。
    • MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象。Looper提供的构造器源代码如下:   

         

        private Looper(boolean quitAllowed) {
                     mQueue = new MessageQueue(quitAllowed);
                     mRun = true;
                     mThread = Thread.currentThread();
                 }

            该构造器使用了private修饰,表明程序员无法通过构造器创建Looper对象。从上面的代码不难看出,程序在初始化Looper时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。

    • Handler:它的作用有两个——发送消息和处理消息,程序使用Handler发送消息,被Handler发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler正常工作,必须在当前线程中有一个MessageQueue,否则消息就没有MessageQueue进行保存了。不过MessageQueue是由Looper负责管理的,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象。为了保证当前线程中有Looper对象,可以分如下两种情况处理。
    1.  主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息、处理消息。
    2. 程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它 。创建Looper对象调用它的prepare()方法即可。

           prepare()方法保证每个线程最多只有一个Looper对象。prepare()方法的源代码如下:

         

      private static void prepare(boolean quitAllowed) {
             if (sThreadLocal.get() != null) {
    
                 throw new RuntimeException("Only one Looper may be created per thread");
    
             }
             sThreadLocal.set(new Looper(quitAllowed));
         }

          然后调用Looper的静态loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。下面是Looper类的loop()方法的源代码:

      

    public static void loop() {
         final Looper me = myLooper();
         if (me == null) {
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
         }
         final MessageQueue queue = me.mQueue;
    
         // Make sure the identity of this thread is that of the local process,
         // and keep track of what that identity token actually is.
         Binder.clearCallingIdentity();
    
         final long ident = Binder.clearCallingIdentity();
         for (;;) {
             Message msg = queue.next(); // might block
             if (msg == null) {
    
                 // No message indicates that the message queue is quitting.
                 return;
             }
    
             // This must be in a local variable, in case a UI event sets the logger
             Printer logging = me.mLogging;
             if (logging != null) {
                 logging.println(">>>>> Dispatching to " + msg.target + " " +
                         msg.callback + ": " + msg.what);
             }
             msg.target.dispatchMessage(msg);
             if (logging != null) {
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
             }
             // Make sure that during the course of dispatching the
             // identity of the thread wasn't corrupted.
             final long newIdent = Binder.clearCallingIdentity();
             if (ident != newIdent) {
                 Log.wtf(TAG, "Thread identity changed from 0x"
                         + Long.toHexString(ident) + " to 0x"
                         + Long.toHexString(newIdent) + " while dispatching to "
                         + msg.target.getClass().getName() + " "
                         + msg.callback + " what=" + msg.what);
             }
             msg.recycle();
         }
        }

    归纳起来,Looper、MessageQueue、Handler各自的作用如下。

    •  Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
    • MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
    • Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。

        在线程中使用Handler的步骤如下。

    1. 调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建于之配套的MessageQueue。
    2. 有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。
    3. 调用Looper的loop()方法启动Looper。

        下面通过实例来介绍Looper与Handler的用法。

         实例:使用新线程计算质数        

        该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将上限数值发送到新启动的线程,该线程来计算该范围内的所有质数。

         之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要相应用户动作,如果在UI线程中执行一个”耗时“操作,将会导致UI线程被阻塞,从而让应用程序失去响应。比如在该实例中,如果用户输入的数值太大,系统可能需要较长时间才能计算出所有质数,这就可能导致UI线程失去响应。

         为了将用户在UI界面输入的数值上限动态的传给新启动的线程,本实例将会在线程中创建一个Handler对象,然后UI线程的事件处理方法就可以通过该Handler向新线程发送消息了。

         该实例的界面布局文件如下:

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <EditText
            android:id="@+id/etNum"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入上限"/>
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="cal"
            android:text="计算"/>    
    </LinearLayout>

    该实例的Activity代码如下。

    package com.example.studyevent;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.app.Activity;
    import android.view.Menu;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    public class CalPrime extends Activity {
         private final String UPPER_NUM="upper";
         EditText etNum;
         CalThread calThread;
         //定义一个线程类
         class CalThread extends Thread{
             public Handler mHandler;
             public void run()
             {
                 Looper.prepare();
                 mHandler=new Handler()
                 {
                     //定义处理消息的方法
                    @Override
                    public void handleMessage(Message msg) {
                        // TODO Auto-generated method stub
                        if(msg.what==0x123)
                        {
                            int upper=msg.getData().getInt(UPPER_NUM);
                            List<Integer> nums=new ArrayList<Integer>();
                            //计算从2开始、到upper的所有质数
                            outer:
                                for(int i=2;i<=upper;i++)
                                {
                                    //用i除以从2开始、到i的平方根的所有数
                                    for(int j=2;j<=Math.sqrt(i);j++)
                                    {
    //如果可以整除,则表明这个数不是质数
                                        if(i!=2&&i%j==0)
                                        {
                                            continue outer;
                                        }
                                    }
                                    nums.add(i);
                                }
                            //使用Toast显示统计出来的所有质数
                            Toast.makeText(CalPrime.this,nums.toString(),Toast.LENGTH_LONG).show();
                        }
                    }
                    
                 };
                 Looper.loop();
             }
             
         }
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_cal_prime);
            etNum=(EditText)findViewById(R.id.etNum);
            calThread=new CalThread();
            //启动新线程
            calThread.start();
        }
        //为按钮的点击事件提供事件处理函数
        public void cal(View source)
        {
            //创建消息
            Message msg=new Message();
            msg.what=0x123;
            Bundle bundle=new Bundle();
            bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
            msg.setData(bundle);
            //向新线程中的Handler发送消息
            calThread.mHandler.sendMessage(msg);
            
        }
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.cal_prime, menu);
            return true;
        }
    
    }

       上面的粗体字代码是实例的关键代码,这些粗体字代码在新线程内创建了一个Handler,由于在新线程中创建Handler时必须创建Looper,因此程序先调用Looper()的prpare()方法为当前线程创建了一个Looper实例,并创建配套的MessageQueue,新线程有了Looper对象之后,接下来程序创建了一个Handler对象,该Handler可以处理其他线程发送过来的消息。程序还调用了Looper的loop()方法。

        运行该程序,无论用户输入多大的数值,计算该范围的质数将会交给新线程完成,而前台UI线程不会受影响。该线程运行效果如图所示。

     

                        

  • 相关阅读:
    用户输入
    hashlib 加密
    vue-cli项目配置mock数据(新版本)
    vue-cli 本地开发mock数据使用方法
    使用nodeJs安装Vue-cli
    购物车实现三
    购物车实现二
    购物车实现一
    极限开发与敏捷开发
    敏捷软件开发-极限编程
  • 原文地址:https://www.cnblogs.com/wolipengbo/p/3409340.html
Copyright © 2020-2023  润新知