花样使用Handler与源码分析

作者:Zy_JiBai 时间:2023-07-30 08:36:31 

前几天在跟公司大佬讨论一个问题时,看到他使用Handler的一种方式,旁边的同事在说:以前不是这么用的啊。这个问题引发了我的好奇,虽然当时翻清楚道理了,但是还是想给大家分享一下。

Handler在之前也说到过他的使用以及源码分析,而且相信大家都知道如何使用它,最常见的使用方法恐怕就是下面这种了:


Handler handler = new Handler(){
 @Override
 public void handleMessage(Message msg) {
   super.handleMessage(msg);
 }
};

这种情况会有一个问题:我们都知道Handler是可以用在子线程给主线程更新的,当子线程给主线程回调时,主线程中的Handler通过接收发送过来的对应消息,去执行对应的任务。而对于上面这个Handler对象,如果他是主线程中的,那么我们子线程中需要拿到主线程的这个Handler对象。


 final Handler handler = new Handler(){
   @Override
   public void handleMessage(Message msg) {
     super.handleMessage(msg);
   }
 };
 new Thread(new Runnable() {
   @Override
   public void run() {
     handler.sendMessage(new Message());
   }
 }).start();

但是上面这种写法实在是太不好看了,而且handler还是一个局部变量,在其他方法中也无法使用。


 Handler handler;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   Log.e(TAG, "onCreate: " );
   handler = new Handler(){
     @Override
     public void handleMessage(Message msg) {
       super.handleMessage(msg);
     }
   };
   new Thread(new Runnable() {
     @Override
     public void run() {
       handler.sendMessage(new Message());
     }
   }).start();
 }

这个看上去应该就好多了,可能也是大多数人的一种写法。

其实说白了,如果说我们要在子线程中给主线程/相应线程回调,那么一定要拿到主线程中的Handler的索引。这么说就很直接了,可能有些情况下无法拿到主线程/相应线程的Handler,或者拿到的方法很麻烦:


public class MyService extends Service {
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
   return null;
 }
 @Override
 public void onCreate() {
   super.onCreate();
   new Thread(new Runnable() {
     @Override
     public void run() {
       //执行相关的耗时操作,然后结束后通过Handler回调给主线程
     }
   }).start();
 }
}

现在我有这样的需求:创建一个服务,在服务中开启一个子线程执行耗时操作,当执行完毕后回调在主线程中相应。这种情况下想要拿到主线程的Handler对象也不是不可以,方法还是有很多,把主线程的handler写成static、创建类继承Handler并且序列化,然后通过intent传入.....可能还有其他的一些方法,但是就目前的这些情况来看,貌似都不是很友好。下面给大家带来一种比较优雅且方便的方法:


public class MyService extends Service {
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
   return null;
 }
 @Override
 public void onCreate() {
   super.onCreate();
   new Thread(new Runnable() {
     @Override
     public void run() {
       /*执行相关的耗时操作,然后结束后通过Handler回调给主线程*/
       new Handler(Looper.getMainLooper()).sendMessage(new Message());
     }
   }).start();
 }
}

只有一句话:又方便看着又舒服。

通过Looper.getMainLooper方法,可以获取到主线程的Looper对象.

虽然之前说我们需要主线程中创建的Handler,其实严格的说是不对的。究其根本是因为主线程已经为他自己加载了mainLooper,而我们在主线程中new Handler,会默认获取主线程的Looper引用。


 public static void main(String[] args) {
   //pass
   Looper.prepareMainLooper();
   //pass
   Looper.loop();
   throw new RuntimeException("Main thread loop unexpectedly exited");
 }
 public Handler(Callback callback, boolean async) {
   //pass
   mLooper = Looper.myLooper();//在主线程中new的Handler获取到的looper就是主线程的mainLooper
   if (mLooper == null) {
     throw new RuntimeException(
       "Can't create handler inside thread that has not called Looper.prepare()");
   }
   //pass
 }

现在看来就很明确了,在主线程中创建Handler只是个幌子,真正在背后操纵一切的其实是looper对象。所以只需要让Handler的mLooper引用获取到主线程的引用就好了。

而且Looper.getMainLooper方法是外部可见的,大胆猜测这个方法就是为了这种方便的写法而存在的。我们可以通过这个方法获取到主线程的looper,让他实现主线程中接收回调。


 public Handler(Looper looper, Callback callback, boolean async) {
   mLooper = looper;
   mQueue = looper.mQueue;
   mCallback = callback;
   mAsynchronous = async;
 }

但是注意我们上述的这种写法:new Handler(....).sendMessage

这种写法不管你怎么去实现,他无法在主线程得到回应(是给大家挖了个坑哈哈),原因很简单:没有重写Handler.handlerMessage方法。

在使用Handler接受消息时有三种方式:

  • 重写Handler.handlerMessage方法,在该方法中接收

  • 在Handler构造器中实现Callback接口,在回调接口中接收

  • 不做任何处理,但是使用post方式发送消息。

在之前我们Handler接收消息见到的几乎都是handleMessage方法,其实这只是其中一种方法,在执行该方法之前会有一个分发的方法dispatchMessage:


 /**
  * Handle system messages here.
  **/
 public void dispatchMessage(Message msg) {
   if (msg.callback != null) {//msg中的callback,这个是通过post方法自己封装的msg(自行查源码),优先级是最高的
     handleCallback(msg);
   } else {//或者在构造器中实现Handler的Callback接口,这个优先级第二
     if (mCallback != null) {
       if (mCallback.handleMessage(msg)) {
         return;
       }
     }
     handleMessage(msg);//这才是我们之前最常用的方法,最低的优先级
   }
 }

可以看到在handlerMessage方法之前还有两种回调的方法。在上述案例中我们并没有重写第三种方法,所以对于在子线程中匿名使用Handler的情况,我们可以采取上述两种方案。代码就不写了,大家都是聪明人。

好了关于Handler 的更多使用就到这里了,喜欢的朋友希望多多支持。

来源:https://blog.csdn.net/zy_jibai/article/details/81583397

标签:android,handler,looper
0
投稿

猜你喜欢

  • string与stringbuilder两者的区别

    2021-11-26 00:01:06
  • Android开发使用HttpURLConnection进行网络编程详解【附源码下载】

    2022-01-11 23:27:37
  • Java设计模式之java组合模式详解

    2022-05-02 07:52:38
  • 深入Android HandlerThread 使用及其源码完全解析

    2023-11-30 19:16:56
  • Java SpringBoot拦截器详解

    2021-11-01 15:29:37
  • Android实现密码明密文切换(小眼睛)

    2023-07-01 15:42:18
  • Kotlin try catch异常处理i详解

    2022-10-14 19:55:50
  • springboot redis分布式锁代码实例

    2023-12-05 21:40:10
  • Java倒计时三种实现方式代码实例

    2021-09-22 00:20:59
  • MyEclipse设置Console输出到文件的实现方法

    2022-01-14 10:37:03
  • Android开发自学笔记(一):Hello,world!

    2023-09-12 14:48:50
  • ijkPlayer播放器的全自动编译脚本及最终编译包

    2023-01-10 13:52:18
  • java中的GC收集器详情

    2021-11-22 16:59:34
  • 基于Android实现百度地图定位过程详解

    2021-06-12 20:55:34
  • java异常处理机制示例(java抛出异常、捕获、断言)

    2022-11-16 21:26:13
  • Java学习之线程同步与线程间通信详解

    2022-05-27 08:03:59
  • Springboot 使用maven release插件执行版本管理及打包操作

    2023-07-12 01:20:35
  • 基于C#实现Windows服务状态启动和停止服务的方法

    2022-07-27 12:17:57
  • java中不定长参数的实例用法

    2021-06-17 02:49:35
  • 详解Xamarin.Android 利用Fragment实现底部菜单

    2021-07-06 23:41:18
  • asp之家 软件编程 m.aspxhome.com