Android之Handle全面理解

一.什么是Handler

    Handler是Android给我们提供用来更新UI的一套机制,是一套消息处理机制,可以通过它来发送消息和处理消息。那作为开发者的我们,不禁会疑问?Google为什么要设计这套机制呢?这是为了解决在非UI线程中更新UI组件比较麻烦的问题。那么Android为什么不能在非UI线程中更新呢?首先Android的UI控件不是线程安全的,这是因为避免多线程并发所带来不安全问题。例如作一个假设,现在在子线程中刷新界面,同时也在UI线程中刷新界面,就会出现刷新不同步,简单来讲通过Handler就可以更新UI操作切换到主线程中执行。

二.Handler机制

 Handler机制主要角色

Message:消息,就是一个载体,包含消息ID,消息处理对象和处理的数据等,统一放到MessageQueue,最终由Handler处理。

Handler:用于同一个进程的线程间通信,消息处理者,专门负责Message的发送和处理。我们使用Handler时,一般通过handleMessage(Message msg)来处理Message,也就是统一处理消息的回调,确保自己发出的消息也是自己来处理。

MessageQueue:由名字可推出这是队列,就是存放Handler发送过来的消息,按照先进先出的顺序规则来执行。将链表的数据结构以Message来串联起来,等待Looper的抽取,为什么需要队列呢?因为同一线程在一个时间只能处理一个消息。因此需要队列来保存这些消息,然后挨个挨个拿出来处理,创建一个线程时并不会自动创建MessageQueue,但是主线程创建时会默认创建Looper对象,而Looper创建时就会创建MessageQueue,其他非主线程需要looper的时候就会通过调用prepare函数来实现。

Looper:首先要理解一个线程是一段可执行的代码,作为App的主线程,不能让代码执行完,因为代码执行完的话app就会自动退出,因此不能让主线程不能让代码段执行,只能在代码中插入一个死循环,这时候Looper的作用就体现出来了,将主线程变成Looper线程。并且这时主线程就会在等其他线程发消息(更新UI和Activity状态等等)那,另外,Looper不断从消息队列拿出消息给主线程,也就是无限循环去查找是否有消息,有就去处理,没有的话就一直等待,一个MessageQueue需要一个Looper。

Thread:负责调度整个消息的循环。

通过上面可以得知:Handler的作用是发送和处理消息,Handler发送的消息必须被送到指定MessageQueue中,也就是说,要让Handler正常工作就必须有一个MessageQueue,但是MessageQueue是由Looper来抽取自身的Message,也因此当前线程中必须有一个Looper对象。

图解:


Handler深度理解

根据上面可得:每一个线程都必须有Looper,但是我们不禁会有疑问?主线程我们并没有创建Looper?其实主线程是ActivityThread,ActivityThread被创建的时候就会初始化Looper。


上面红色箭头就是主线程创建Looper对象代码语句。那么我们继续进入Looper去看看究竟是什么操作。


果然上面所分析的几个主要角色都在:Looper,MessageQueue,Thread,但是还多了一个ThreadLocal,这个是什么呢?其实这个是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定的线程获取到存储数据,对于其他线程来说则无法获取到数据,后面例子再详细叙述。


这时候又有疑问,为什么主线程中有一个死循环,但是没有造成阻塞呢?那我们从ActivityThread类开始分析:这个并不是一个真正的线程,就只是一个类,从main方法看到有Looper,那找找其对应的Handler,ActivityThread有一个内部类,继承Handler,并找到handleMessage


仔细观察:Activity的生命周期都有对应的case条件,并且service的生命周期等等都有条件。也就是通过loop来分发消息内部类的Handler,就会进入handleMessage。就会根据对应的条件来执行对应的代码,不断执行传递过来的消息。那么可以总结来说:ActivityThread的main方法主要是来做消息循环的,如果消息循环退出了,那么程序就退出了。但是从队列里取出消息可能会造成阻塞,如果某个消息处理的时间过于长,那么会影响UI线程的刷新,就造成卡顿现象。不会造成卡在的真正原因主要有二:

1.当没有消息来的时候就会wait,有句柄写的时候就会唤醒,进行等待。

2.所有的UI更新操作都通过handler来发消息。如各种的点击事件都会造成句柄写的操作来唤醒等待。

主线程是UI线程,优先级很高,主线程的死循环会不会特别消耗CPU资源呢?

其实如果主线程的队列里没有消息时,就会阻塞在loop的queue.next()里,这时候主线程就会释放CPU资源进入休眠状态,直到下个消息进来时就会唤醒主线程,现在是通过pipe管道来写入数据,类似I/0,不占用资源。

三.例子实践

下面简单写一个小例子加深理解:

倒计时例子:

public class MainActivity extends Activity {





    private TextView text_one;



    private mHandler mhandler = new mHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text_one = findViewById(R.id.text_one);
        start();
    }




    //Handler静态内部类 防止内存泄漏
    private static class mHandler extends Handler{
       WeakReference<MainActivity> weakReference;
       public mHandler(MainActivity mainActivity){
           weakReference = new WeakReference<MainActivity>(mainActivity);
       }
       @Override
        public void handleMessage(Message msg){
           MainActivity mainActivity = weakReference.get();
           if(mainActivity != null){
                 mainActivity.text_one.setText(String.valueOf(msg.arg1));
           }

       }

    }


  private void start(){
         new Thread(new Runnable() {
             @Override
             public void run() {

                 for(int i = 10;i>0;i--){
                     Message msg = new Message();
                     msg.what = 01;
                     msg.arg1 = i;
                     mhandler.sendMessage(msg);
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
                 //计时结束后先不管
            }
         }).start();
  }




  @Override
    protected void onDestroy(){
      super.onDestroy();
      if(mhandler != null){
          mhandler.removeCallbacksAndMessages(null);
      }

  }

猜你喜欢

转载自blog.csdn.net/qq_33453910/article/details/80069650
今日推荐