Android 你对Handler了解多少?(使用详解篇)

Android 你对Handler了解多少?(使用详解篇)

    • 前言
    • 什么是Handler
    • 为什么要用Handler
    • Handler 、Looper、MessageQueue、Message
    • 使用举例
    • Handler引发的内存泄露
    • 总结

前言

Handler一直是面试官必问的面试题,当然,你每次面试之前都温习了相关的知识点,但当你回答Handler相关问题的时候,你又回答的不是很好,这样的局面是不是经常碰到,那咱扪心自问一句,你真的懂Handler吗,对于刚入门的同学,往往都不知道Handler到底是个啥东西,但对于有经验的同学,你也不一定能真正的懂Handler,所以此文对有经验的同学也可以过来看看哟。我们接下来会对Handler做一个详细的讲解,讲解分为:

  1. Android 你对Handler了解多少?(使用详解篇)
  2. Android 你对Handler了解多少?(源码解析篇)
  3. Android 你对Handler了解多少?(深入解析篇)
  4. Android 手把手教你写Handler (原理运用篇)
  5. Android 你对Handler了解多少?(面试篇)

什么是Handler

Handler是一个消息分发对象。Handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息,其最大的作用就是线程切换。

为什么要用Handler

在Android开发中多线程的应用中,将工作线程中需更新UI的操作信息传递到UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理,最根本的目的就是为了解决多线程并发的问题,如果在一个Activity中有多个线程,并且没有加锁,就会出现界面错乱的问题。但是如果对这些更新UI的操作都加锁处理,又会导致性能下降。处于对性能的问题考虑,Android给我们提供这一套更新UI的机制我们只需要遵循这种机制就行了。不用再去关系多线程的问题,所有的更新UI的操作,都是在主线程的消息队列中去轮训的

Handler 、Looper、MessageQueue、Message

讲到Handler,都离不开Looper、MessageQueue、Message这三者和Handler之间的关系,面试过程中也经常问到这三者之间的关系,那么这三这于Handler之间到底有什么关系?

Looper
也称之为:消息泵,是MessageQueue的管理者,会不断从MessageQueue中取出消息,并将消息分给对应的Handler处理。每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。它的特点是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。

那么有人会问,我创建Handler的时候从来都没创建Looper呀,这是因为在主线程中,ActivityThread会自动帮我们创建,这个知识点到后面会详细讲解到。

  1. Looper.prepare(): 为当前线程创建Looper对象,说白了就是初始化
  2. Looper.myLooper():可以获得当前线程的Looper对象
  3. Looper.prepareMainLooper():为UI线程创建Looper对象
  4. Looper.getMainLooper():// 获得UI线程的Lopper对象

MessageQueue
也称之为:消息队列,其内部接口就是一个链表结构,存储的是Message,每个线程最多只有一个MessageQueue,MessageQueue通常都是由Looper来管理,而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue

Message
Handler接收和处理的消息对象,就是MessageQueue里面存放的对象,一个MessageQueu可以包括多个Message。当我们需要发送一个Message时,我们一般不建议使用new Message()的形式来创建,更推荐使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。其主要字段用途如下

  1. what:用户自定义的消息码,让接收者识别消息
  2. arg1、arg2:轻量级存储int类型的数据
  3. obj:存储任意对象
  4. replyTo:线程通信时使用

Handler
主要有一下方法

  1. void handleMessage(Message msg):处理消息的方法。该方法通常用于被重写
  2. final boolean hasMessages(int what, Object object):检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息。
  3. final boolean hasMessages(int what):检查消息队列中是否包含what属性为指定值的消息
  4. sendEmptyMessage(int what):发送一个空消息
  5. final boolean sendEmptyMessageDelayed(int what, long delayMillis):发送多少毫秒后发送空消息
  6. final boolean sendMessage(Message msg):立即发送消息
  7. final boolean sendMessageDelayed(Message msg, long delayMillis):发送多少毫秒后发送消息
  8. final void removeCallbacks(Runnable r):将MessageQueue中的Runnable对象移除

使用举例

从子线程发送消息到主线程

  public class MainActivity extends AppCompatActivity {
    TextView btn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn=findViewById(R.id.btn);
        new Thread(new Runnable() {
           @Override
           public void run() {
               //在子线程发送一个消息,不建议new Message() 建议用Message.obtain()
               Message msg=new Message();
               handler.sendMessage(msg);
           }
       }).start();
    }
    //在主线程创建Handler对象,重写handleMessage方法,这个方法就是接收并处理消息的方法
    @SuppressLint("HandlerLeak")
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            //msg就是子线程发送过来的消息
        }
    };
}

发送一个空消息

 handler.sendEmptyMessage(what);

同一消息不能发送两次

 Message msg=new Message();
 handler.sendMessage(msg);
 handler.sendMessage(msg);

发送一个带有obj属性的消息

public class MainActivity extends AppCompatActivity {
    TextView btn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn=findViewById(R.id.btn);
        new Thread(new Runnable() {
           @Override
           public void run() {
              
               Message msg=new Message();
               msg.obj="我是消息";
               handler.sendMessage(msg);
           }
       }).start();
    }
  
    @SuppressLint("HandlerLeak")
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String str= (String) msg.obj;
            Toast.makeText(MainActivity.this,str,1000).show();
        }
    };
}

发送一个带有what标识的消息

 public class MainActivity extends AppCompatActivity {
    TextView btn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn=findViewById(R.id.btn);
        new Thread(new Runnable() {
           @Override
           public void run() {
      
               Message msg1=new Message();
               msg1.obj="我是消息1";
               msg1.what=1;
               handler.sendMessage(msg1);

               Message msg2=new Message();
               msg2.obj="我是消息2";
               msg2.what=2;
               handler.sendMessage(msg2);
           }
       }).start();
    }
  
    @SuppressLint("HandlerLeak")
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    String str1= (String) msg.obj;
                    Toast.makeText(MainActivity.this,"第一个消息是"+str1,1000).show();
                    break;
                case 2:
                    String str2= (String) msg.obj;
                    Toast.makeText(MainActivity.this,"第二个消息是"+str2,1000).show();
                    break;
            }

        }
    };
}

Handler还可以发送post一个Runnable

public class MainActivity extends AppCompatActivity {
    TextView btn;
    private Handler mHandler = new Handler();
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn=findViewById(R.id.btn);
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                //在子线程post一个Runnable对象
                Log.e("TAG",Thread.currentThread().getName());
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //这里运行在主线程,可能有人很好奇为什么运行在主线程,我这不是在子线程中吗?
                        //当你使用Handler的时候,已经把线程切换过来了,Handler的线程是默认创建的
                        Log.e("TAG",Thread.currentThread().getName());
                        btn.setText("已点击");
                        
                    }
                });
            }
        }).start();

    }
}

以上可以看出,Handler发送消息有两种方式,一个是直接Message,一个是通过post一个Runnable,那么这两个有什么区别呢,我们来稍微看一下源码

public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

可以看出两个方法都是通过调用sendMessageDelayed(),post方法的底层调用sendMessageDelayed的时候,却是通过getPostMessage®来将Runnable对象来转为Message,我们再来看看getPostMessage®里面是怎么实现的

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

然后,looper进行消息循环,进行消息的分发,

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果回调不为空,handleCallback(msg) 找个方法就会执行,

 private static void handleCallback(Message message) {
        message.callback.run();
    }

最终Runnable也是转化为一个Message,而这个Message只被一个变量赋值,就是Runnable的回调函数,Run方法里面的操作。

那么问题又来了,Goole工程师为什么要设计这两种方式?
我的总结是这样的,如果需要进行很多数据来传输,我们可以使用sendMessage,因为Meaage可以赋值很多丰富的对象,如果我们只需要进行某一个动作,可以用post Runnable,在run方法里面实现即可

Handler引发的内存泄露

在Android中Handler引发的内存泄露是非常常见的,也是面试的时候会问到的,那么为什么会泄露呢?在Android中非静态内部内持有外部类的引用会导致泄露,这句话什么意思呢?就是说在Handler处理消息的时候,Handler必须处理完所有消息才会与外部类解除引用关系,如果此时外部Activity需要提前被销毁了,而Handler因还未完成消息处理而继续持有外部Activity的引用。由于上述引用关系,垃圾回收器(GC)便无法回收Activity,从而造成内存泄漏。
下面就是解决方案,静态内部类+弱引用

public class MainActivity extends AppCompatActivity {


   private TextView mTv;
   private MyHandler myHandler;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       myHandler = new MyHandler(this);
   }



   //这样的写法会造成内存泄漏
   //mHandler是MainActivity的非静态内部类的实例,它持有外部类的引用,我们知道handler的消息是在一个loop
   //中不断的轮询处理消息,那么当MainActivity退出时,消息队列中还有没处理的消息或正在处理的消息,所以会造成内存泄漏
  @SuppressLint("HandlerLeak")
  private Handler mHandler = new Handler(){
      @Override
      public void handleMessage(Message msg) {
          super.handleMessage(msg);
      }
  };

   //这样写是正确的写法
   static class MyHandler extends Handler{

       //创建一个软引用
       private WeakReference reference;

       public MyHandler(Context context){
           reference = new WeakReference(context);
       }

       @Override
       public void handleMessage(Message msg) {
           MainActivity mainActivity = (MainActivity) reference.get();
           if(mainActivity != null){
               //TODO------
               mainActivity.mTv.setText("11");
           }
       }
   }
  @Override
  protected void onDestroy() {
     super.onDestroy();
     myHandler.removeCallbacksAndMessages(null);
  }
   
}

总结

Handler是主要用于主线程更新UI,在Android中也是一套非常重要的UI更新机制,当然如果处理不当就会造成内存泄露,里面其实还设及到了很多知识点,就比如为什么要在主线程更新UI,子线程不能更新吗?后面我们会更深入研究它的源码。

你可能感兴趣的:(Android,Handler)